在 Android 上使用本机 date/time 选择器时的布局挑战 5.1.x
Layout challenge when using native date/time picker on Android 5.1.x
我这里有一种"catch 22"情况。
我正在使用 Appcelerator Titatium SDK 5.1.2。在 Android 5.1.x 上,屏幕尺寸非常小,我找不到使用本机选择器正确 select 日期和时间的解决方案。我需要添加一个 <ScrollView>
以允许用户移动内容以使选择器完全可见。但是,在 Android 5.1.x 上执行此操作时,用户无法在日期选择器中滚动回前几个月....将其更改为 <View>
控件会使日期选择器的行为如下预期的。但是,如果选择器的一部分在可见区域之外,则用户没有机会从那里 select 值...
我创建了一个简单的例子来说明这一点。在 <ScrollView>
而不是 <View>
中评论相同的 id 以查看差异:
查看:
<Alloy>
<Window class="container">
<View id="form" onClick="clickHandler">
<!--
<ScrollView id="form" onClick="clickHandler">
-->
<View id="formRow">
<Label id="title">Picker demo</Label>
</View>
<View id="formRow">
<Label id="label">Date</Label>
<TextField id="startDate" bubbleParent="true" editable="false"></TextField>
</View>
<View id="formRow">
<Label id="label">Time</Label>
<TextField id="startTime" bubbleParent="true" editable="false"></TextField>
</View>
<!--
</ScrollView>
-->
</View>
</Window>
</Alloy>
风格:
".container": {
top: 20,
backgroundColor:"#fa0",
orientationModes: [Ti.UI.PORTRAIT]
}
"Label": {
width: Ti.UI.SIZE,
height: Ti.UI.SIZE,
backgroundColor: 'transparent',
left:10,
color: "#000"
}
"#title": { top:15,
font: {
fontSize: '25dp',
fontStyle: 'bold'
}
}
"#label": { top:0,
font: {
fontSize: '18dp',
fontStyle: 'bold'
}
}
"TextField": { font: {
fontSize: '18dp',
fontStyle: 'normal'
},
backgroundColor:'orange'
}
"#formRow":{
top:7,
height:Ti.UI.SIZE,
width:Ti.UI.FILL
}
"#startDate":{
top:0,
width:150,
right:10,
}
"#startTime":{
top:0,
width:70,
right:10
}
"#form":{
showVerticalScrollIndicator:"true",
layout:"vertical"
}
控制器:
var Moment = require('alloy/moment');
function clickHandler(e){
if(e && e.source){
console.log("clickHandler. id="+e.source.id);
if(e.source.id === 'startDate'){
openDatePicker(e);
} else if(e.source.id === 'startTime'){
openTimePicker(e);
}
}
}
// Date/time utils:
function getAsDate(date) {
// Return as Date object
var dt = null;
if(typeof date === 'number') {
dt = new Date(date);
} else if(date instanceof Date) {
dt = date;
}
return dt;
};
function getDMY(date) {
var dt = getAsDate(date);
if(dt) {
return Moment(dt).format('DD-MM-YYYY');
}
return null;
}
function getHMM(date) {
// Returns format: H:mm
var dt = getAsDate(date);
if(dt) {
return Moment(dt).format('H:mm');
}
return null;
}
function fromDMY(dt){
var date = Moment(dt, "DD-MM-YYYY");
if(date){
date = new Date(date);
}
return date;
}
function fromHMM(time){
var datetime = Moment(time, "H:mm");
if(datetime) {
return new Date(datetime);
}
return null;
}
function openDatePicker(){
// Inner helper class to clean up and remove picker items
function cleanup(){
console.log("openDatePicker.cleanup...");
pickerOpen = null;
$.startDate.value = getDMY(picker.value);
$.formRow.remove(picker);
$.startDate.bubbleParent = true;
$.form.removeEventListener('click',cleanup);
}
var v = $.startDate.value;
if(v && fromDMY(v)) {
v = fromDMY(v);
console.debug("startDate.value=" + $.startDate.value + " --> v=" + v + " - type: " + typeof v);
}else{
v = new Date();
}
var picker = Ti.UI.createPicker({
type:Ti.UI.PICKER_TYPE_DATE,
maxDate:new Date(),
top:35,
value:v
});
$.formRow.add(picker);
$.startDate.bubbleParent = false;
$.form.addEventListener('click',cleanup);
}
function openTimePicker(){
// Inner helper class to clean up and remove picker items
function cleanup(){
console.log("openTimePicker.cleanup...");
pickerOpen = null;
$.startTime.value = getHMM(picker.value);
$.formRow.remove(picker);
$.startTime.bubbleParent = true;
$.form.removeEventListener('click',cleanup);
}
var v = $.startTime.value;
if(v && fromHMM(v)) {
v = fromHMM(v);
console.debug("startTime.value=" + $.startTime.value + " --> v=" + v + " - type: " + typeof v);
}else{
v = new Date();
}
var picker = Ti.UI.createPicker({
type:Ti.UI.PICKER_TYPE_TIME,
format24:true,
minuteInterval:5,
top:35,
value:v
});
$.formRow.add(picker);
$.startTime.bubbleParent = false;
$.form.addEventListener('click',cleanup);
}
$.index.open();
对于冗长的代码感到抱歉(无法真正缩短它)- 但它是一个 有效 示例。
在 Android 的其他版本上,<ScrollView>
并不优先于本机日期选择器 - 因此允许用户轻松地重新定位整个内容以查看选择器,然后 select 日期。
我已经在 Genymotion VM 运行 API 22 中使用 480 x 800 (240 dpi) 的屏幕尺寸验证了上述应用程序。它很好地说明了问题:-)
知道如何为 Android 5.1.x 做这件事吗?
提前致谢!
/约翰
您尝试过在 ScrollView 上设置 canCancelEvents: false
吗?默认情况下,ScrollView 会对所有事件做出反应,甚至是其子项的事件。
嵌套的可滚动 UI 组件很容易出问题。在这种情况下,您可能希望改用 dialog。
我这里有一种"catch 22"情况。
我正在使用 Appcelerator Titatium SDK 5.1.2。在 Android 5.1.x 上,屏幕尺寸非常小,我找不到使用本机选择器正确 select 日期和时间的解决方案。我需要添加一个 <ScrollView>
以允许用户移动内容以使选择器完全可见。但是,在 Android 5.1.x 上执行此操作时,用户无法在日期选择器中滚动回前几个月....将其更改为 <View>
控件会使日期选择器的行为如下预期的。但是,如果选择器的一部分在可见区域之外,则用户没有机会从那里 select 值...
我创建了一个简单的例子来说明这一点。在 <ScrollView>
而不是 <View>
中评论相同的 id 以查看差异:
查看:
<Alloy>
<Window class="container">
<View id="form" onClick="clickHandler">
<!--
<ScrollView id="form" onClick="clickHandler">
-->
<View id="formRow">
<Label id="title">Picker demo</Label>
</View>
<View id="formRow">
<Label id="label">Date</Label>
<TextField id="startDate" bubbleParent="true" editable="false"></TextField>
</View>
<View id="formRow">
<Label id="label">Time</Label>
<TextField id="startTime" bubbleParent="true" editable="false"></TextField>
</View>
<!--
</ScrollView>
-->
</View>
</Window>
</Alloy>
风格:
".container": {
top: 20,
backgroundColor:"#fa0",
orientationModes: [Ti.UI.PORTRAIT]
}
"Label": {
width: Ti.UI.SIZE,
height: Ti.UI.SIZE,
backgroundColor: 'transparent',
left:10,
color: "#000"
}
"#title": { top:15,
font: {
fontSize: '25dp',
fontStyle: 'bold'
}
}
"#label": { top:0,
font: {
fontSize: '18dp',
fontStyle: 'bold'
}
}
"TextField": { font: {
fontSize: '18dp',
fontStyle: 'normal'
},
backgroundColor:'orange'
}
"#formRow":{
top:7,
height:Ti.UI.SIZE,
width:Ti.UI.FILL
}
"#startDate":{
top:0,
width:150,
right:10,
}
"#startTime":{
top:0,
width:70,
right:10
}
"#form":{
showVerticalScrollIndicator:"true",
layout:"vertical"
}
控制器:
var Moment = require('alloy/moment');
function clickHandler(e){
if(e && e.source){
console.log("clickHandler. id="+e.source.id);
if(e.source.id === 'startDate'){
openDatePicker(e);
} else if(e.source.id === 'startTime'){
openTimePicker(e);
}
}
}
// Date/time utils:
function getAsDate(date) {
// Return as Date object
var dt = null;
if(typeof date === 'number') {
dt = new Date(date);
} else if(date instanceof Date) {
dt = date;
}
return dt;
};
function getDMY(date) {
var dt = getAsDate(date);
if(dt) {
return Moment(dt).format('DD-MM-YYYY');
}
return null;
}
function getHMM(date) {
// Returns format: H:mm
var dt = getAsDate(date);
if(dt) {
return Moment(dt).format('H:mm');
}
return null;
}
function fromDMY(dt){
var date = Moment(dt, "DD-MM-YYYY");
if(date){
date = new Date(date);
}
return date;
}
function fromHMM(time){
var datetime = Moment(time, "H:mm");
if(datetime) {
return new Date(datetime);
}
return null;
}
function openDatePicker(){
// Inner helper class to clean up and remove picker items
function cleanup(){
console.log("openDatePicker.cleanup...");
pickerOpen = null;
$.startDate.value = getDMY(picker.value);
$.formRow.remove(picker);
$.startDate.bubbleParent = true;
$.form.removeEventListener('click',cleanup);
}
var v = $.startDate.value;
if(v && fromDMY(v)) {
v = fromDMY(v);
console.debug("startDate.value=" + $.startDate.value + " --> v=" + v + " - type: " + typeof v);
}else{
v = new Date();
}
var picker = Ti.UI.createPicker({
type:Ti.UI.PICKER_TYPE_DATE,
maxDate:new Date(),
top:35,
value:v
});
$.formRow.add(picker);
$.startDate.bubbleParent = false;
$.form.addEventListener('click',cleanup);
}
function openTimePicker(){
// Inner helper class to clean up and remove picker items
function cleanup(){
console.log("openTimePicker.cleanup...");
pickerOpen = null;
$.startTime.value = getHMM(picker.value);
$.formRow.remove(picker);
$.startTime.bubbleParent = true;
$.form.removeEventListener('click',cleanup);
}
var v = $.startTime.value;
if(v && fromHMM(v)) {
v = fromHMM(v);
console.debug("startTime.value=" + $.startTime.value + " --> v=" + v + " - type: " + typeof v);
}else{
v = new Date();
}
var picker = Ti.UI.createPicker({
type:Ti.UI.PICKER_TYPE_TIME,
format24:true,
minuteInterval:5,
top:35,
value:v
});
$.formRow.add(picker);
$.startTime.bubbleParent = false;
$.form.addEventListener('click',cleanup);
}
$.index.open();
对于冗长的代码感到抱歉(无法真正缩短它)- 但它是一个 有效 示例。
在 Android 的其他版本上,<ScrollView>
并不优先于本机日期选择器 - 因此允许用户轻松地重新定位整个内容以查看选择器,然后 select 日期。
我已经在 Genymotion VM 运行 API 22 中使用 480 x 800 (240 dpi) 的屏幕尺寸验证了上述应用程序。它很好地说明了问题:-)
知道如何为 Android 5.1.x 做这件事吗?
提前致谢!
/约翰
您尝试过在 ScrollView 上设置 canCancelEvents: false
吗?默认情况下,ScrollView 会对所有事件做出反应,甚至是其子项的事件。
嵌套的可滚动 UI 组件很容易出问题。在这种情况下,您可能希望改用 dialog。