Egret社区
充电的时间到了,为了让你萌技能日有所张,按照惯例,继续分享《30天,App开发从0到1》的内容。本篇为大家精选的章节是第二部分第八章第一小节——自定义样式的日期选择器。敲黑板!!!感兴趣的小伙伴带上小板凳上课啦!

主要内容
示例1和示例2讲的是“融合”,示范了APICloud模块与HTML5代码混合开发的使用技巧;
示例 3 讲的是“基础特性”,帮助开发者理解与掌握 APICloud 的基础特性,这样需求实现
就会变得简单;
示例 4 ~示例 6 讲的是 CSS 的使用技巧,APICloud 采用 HTML、CSS 和 JavaScript 为主开
发语言,可以完美实现各种 CSS 样式效果。

学习目标
(1)实现自定义样式的日期选择器。
(2)实现自定义样式的三级联动城市选择器。
(3)实现固定不动的下拉筛选菜单。
(4)实现滑动页面时,动态改变导航条颜色。
(5)实现背景图片的高斯模糊效果。
(6)实现 0.5 px 的细线。

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

8.1
自定义样式的日期选择器


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

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

图 8-1


8.1.1
使用模块UICustomPicker


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


8.1.2
开发流程及要点概述


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

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

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

/* HTML页面部分代码 */

<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">完成</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">取消</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 对象是由上级页面传递的,目的是将页面选择的数据回传给调用的上级页面。
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|京网文[2014]0791-191号|京ICP证150115号|Egret社区 ( 京ICP备14025619号 )

Powered by Discuz! X3.4 © 2001-2019 Comsenz Inc.

返回顶部