APP开发平台 > Blog > 自定义样式的日期选择器 |APICloud开发者进阶之路

自定义样式的日期选择器 | APICloud开发者进阶之路

微信图片_20180820161555.jpg

  duangduang~每周最期待的周五又来啦!提前祝小伙伴们周末愉快呀!按照惯例,继续分享我们《30天,App开发从0到1》内容。本周为大家精选的章节是第二部分第八章第一小节——自定义样式的日期选择器。敲黑板!!!感兴趣的小伙伴带上小板凳上课啦!

  学习目标:

  实现自定义样式的日期选择器

  以下示例讲解仅选择部分核心代码进行详细说明,读者可在 GitHub 本书的资源范例中获取示例的完整代码。

  8.1

  自定义样式的日期选择器

  APICloud 模块因其易用性、高效性,在 APICloud 应用开发中会被频繁地使用。UI 类的APICloud 模块,可以修改颜色、字体、背景色等样式,形成不同的风格与外观样式,但模块的整体布局结构是无法改变的。本示例提供一种思路,将 HTML 页面与模块混合搭配,利用HTML 快速布局形成不同的页面格局,形成另类的视觉体验。

  下面采用 HTML 页面与 APICloud 模块混合嵌套的方式,实现一个不一样的日期选择器,如图 8-1 所示。

微信图片_20180820161907.jpg

  图 8-1

  8.1.1

  使用模块UICustomPicker

  UICustomPicker 模块是一个自定义内容选择器,可自定义模块位置、内容取值范围、内容标签、设置选中内容,还可用于实现固定取值范围的内容选择器;多项内容之间没有级联关系。

  8.1.2

  开发流程及要点概述

  本示例的实现思路是先用 HTML 代码创建一个背景页面,然后将模块打开在这个背景层上面,从而从视觉上实现既定的目标样式。

  (1)实现 HTML 静态页面开发

  为相关页面添加如下 HTML 代码,使用了弹性盒子布局。篇幅所限,CSS 样式部分就不在这里列出,具体可从示例源码中获取查看。注意页面中的 onclick 点击事件使用了 tapmode 属性去消除 300 ms 的点击延迟。

<body class="fl ex-box fl ex-column">

<div class="fl ex-1"></div>

<div class="sheet">

<div class="fl ex-box sheet-header">

<div class="Btn"></div>

<div id="title" class="fl ex-1">请选择日期</div>

<div class="Btn" tapmode="touched" onclick="fnCompleteBtnTouched();">完成</div>

</div>

<div class="sheet-body">

<div class="title fl ex-box">

<span class="fl ex-1">年</span>

<span class="fl ex-1">月</span>

<span class="fl ex-1">日</span>

</div>

<div id="picker-container" class="fl ex-box fl ex-column">

<div class="fl ex-1"></div>

<div class="cell"></div>

<div class="fl ex-1"></div>

</div>

</div>

<div class="cancel" tapmode="touched" onclick="fnCancelBtnTouched();">取消</div>

</div>

</body>


  (2)创建日期选择器

  创建模块实例,在 open 方法中定义了模块的位置、大小尺寸和颜色样式、可选的时间范围等参数。在 open 方法的回调中记录了模块的 ID 值,用于后续操作模块的逻辑方法时使用。同时加入了设置模块初始化显示的默认值方法和防止选择错误日期(如 2 月 30 日)的方法。

  为相关页面添加如下代码:

  // JavaScript 部分代码

  var UICustomPicker; //模块对象

  var vPickerId; // 记录当前模块ID的变量

  function fnOpenPicker() { // 创建联动选择器

  UICustomPicker = api.require('UICustomPicker'); // 引入模块

  // 定义模块初始化需要的参数

  // 根据页面HTML布局,定义模块所在位置参数

  var tY = api.winHeight - 184 - 10; // 定义模块rect中的Y,起始高度数值

  var tW = api.frameWidth - 40; // 定义模块rect中的w,宽度数值

  // 定义模块可选择的时间范围参数

  // 获取当前年份

  var tNow = new Date();

  var tYear = tNow.getFullYear(); // 获取当前年份

  var tMonth = tNow.getMonth(); // 获取当前月份

  var tDate = tNow.getDate(); // 获取当前日期

  var tMinYear = tYear - 100; // 可选最小时间,100年前

  var tMaxYear = tYear + 100; // 可选最大时间,100年后

  UICustomPicker.open({

  rect: {

  x: 20,

  y: tY,

  w: tW,

  h: 135

  },

  styles: {

  bg: 'rgba(61,61,61,0.0)',

  normalColor: 'rgba(61,61,61,0.5)',

  selectedColor: '#3d3d3d',

  selectedSize: 28,

  tagColor: '#3685dd',

  tagSize: 16

  },

  data: [{

  scope: tMinYear + '-' + tMaxYear

  }, {

  scope: '1-12'

  }, {

  scope: '1-31'

  }],

  autoHide: false,

  loop: true,

  rows: 3,

  fi xedOn: api.frameName,

  fi xed: true

  }, function(ret, err) {

  if (ret) {

  if('number' == typeof ret.id) {

  vPickerId = ret.id; // 记录当前模块的ID

  }

  if('show' === ret.eventType) {

  // 设置当前时间为默认值

  var tDefault = [tYear,tMonth+1,tDate];

  fnSetSelectedValue(tDefault);

  }

  if('selected' === ret.eventType) {

  //判断选择值的合法性

  fnCheckSelectedValue(ret.data);

  }

  }

  });

  }

  (3)加入时间校验逻辑

  因为现实时间存在闰年,并且每个月的天数不同,所以需要完善日期选择器,加上补充逻辑,以避免出现选择了 ×××× 年 2 月 31 日的错误发生。

  /**

  * 闰年判断

  * @param {Number} pYear 4位数字组成的年份值

  * @constructor

  */

  Date.prototype.isLeapYear = function(pYear) {

  var self = this;

  var tYear = 'number' === typeof pYear ? pYear:self.getFullYear();

  return (tYear % 4 == 0) && (tYear % 100 != 0 || tYear % 400 == 0);

  }

  var oSelectedData; // 选择的时间数组

  /**

  * 判断选择值的合法性

  * @param {Array} pData 日期选择器选择后的回调数据

  * @return {void}

  */

  function fnCheckSelectedValue(pData) {

  if('[object Array]' !== Object.prototype.toString.call(pData)) {

  return;

  }

  //判断特殊日期

  //获取月份进行判断

  var tData = pData;

  switch (tData[1]) {

  case '2':

  //判断是否为闰年

  var tNum = '28';

  if(new Date().isLeapYear(tData[0])){

  tNum = '29';

  }

  if( parseInt(tData[2]) > parseInt(tNum) ){

  tData[2] = tNum;

  fnSetSelectedValue(tData);

  }

  else {

  oSelectedData = tData;

  }

  break;

  case '4':

  case '6':

  case '9':

  case '11':

  if( tData[2] == '31') {

  tData[2] = '30';

  fnSetSelectedValue(tData);

  }

  else {

  oSelectedData = tData;

  }

  break;

  default:

  oSelectedData = tData;

  }

  }

  /**

  * 主动设置选择器的选择值

  * @param {Array} pData 日期选择器选择后的回调数据

  * @return {void}

  */

  function fnSetSelectedValue(pData) {

  if('[object Array]' !== Object.prototype.toString.call(pData)) {

  return;

  }

  UICustomPicker.setValue({

  id: vPickerId,

  data: pData

  });

  oSelectedData = pData;

  }

  (4)加入 HTML 页面按钮点击事件

  点击事件是实现模块和其他页面的交互逻辑的。

  fnCancelBtnTouched() 函数方法中使用了 api.pageParam 这个 api 的属性,其中 cb_win( 表示回调的 win 窗口名称 ) 和 cb_frm(表示回调的 frame 窗口名称),具体对应的值是上一级打开本页面的窗口传送过来的,这样的好处是方便本页面封装成一个通用的公共页面,更加灵活。

  为相关页面添加如下 JS 代码:

  //取消按钮点击事件

  function fnCancelBtnTouched() {

  api.execScript({ // 调用上级页面方法来关闭选择器

  name: api.pageParam.cb_win,

  frameName: api.pageParam.cb_frm,

  script: 'fnCloseSheetFrame();'

  });

  }

  fnCompleteBtnTouched() 完成按钮的点击事件,将关闭页面的方法放在了上级页面,使用 api.execScript 方法去调用执行。这样处理是为了避免页面关闭的执行过快,后续的逻辑代码还没来得及执行或没有执行完,从而产生错误异常。

  //完成按钮点击

  function fnCompleteBtnTouched() {

  if(!oSelectedData || oSelectedData.length == 0) {

  api.toast({

  msg: '请选择日期!',

  duration: 2000,

  location: 'bottom'

  });

  return;

  }

  else {

  /* 执行完成后续业务逻辑 */

  // console.log('选择数据:'+JSON.stringify(oSelectedData));

  api.execScript({ //执行选择后的回调方法

  name: api.pageParam.cb_win,

  frameName: api.pageParam.cb_frm,

  script: api.pageParam.cb_fun+'('+JSON.stringify(oSelectedData)+');'

  });

  }

  }

  本示例的点击事件中使用的 api.pageParam 对象是由上级页面传递的,目的是将页面选择的数据回传给调用的上级页面。

微信图片_20180820162154.jpg

2018-08-20 来源:APICloud

An efficient app outsourcing platform that guarantees timely delivery!

Submit Requirements