界面实现
无论是WEX还是BEX,开发各种客观题模块都是最常见的。但是,最近在利用业余时间想开发一个学生使用的小型客观题系统时遇到了麻烦。首先,在论坛上搜索到了大量相似问题。问题或者过于简单,或者过于特殊。且看一下本人提供的客观题界面,我相信这种设计思路还是与大多数应用需求相一致的。
这是单选题界面和多选题界面。
注意:我每一个小题旁边放置的按钮是便于学生自我测试时看参考答案时使用的。这个小功能设计比较简单(我使用的是PopOver组件,其内的span对应的bind-text令其等于 $model.singleData.val("fAnswer")类似的内容即可实现即时地查看对应小题的答案)。
问题
在开发类似于上面的系统时注意到不少像我这样的初学者存在与我十分类似的困惑。
(补注:这样人可能以前有JAVA,C#,VB等UI开发经验,但遗憾的是,这些软件开发思路与X5中的H5开发思想发生了重大变化--不知大伙与我意见一致否?)
归纳起来,问题有:
1,list组件的onClick 事件中 如何得到 list某一行的id? 地址:http://doc.wex5.com/comps-list/。
2,如何在list组件中放置radio组件或者radioGroup组件实现类似于我实现的上述选择题界面?
3,在上面界面前提下,在用户做过好几个小题后,如果对比答案(比较库中正确与错误的答案,检查其做题效果如何)?
补充
由于我上述小系统比较简单,所以暂时只考虑使用本地json方式存储试题内容,单选,多选,判断等都各自对应一个JSON文件。例如,单选题文件singleData.json类似于如下结构:
[
{"fCode":"0401",
"fTrunk":"在数据库表格中唯一标识一条记录的是( )。",
"fChoiceA":"A.主键",
"fChoiceB":"B.候选键",
"fChoiceC":"C.索引",
"fChoiceD":"D.关键字",
"fAnswer":"A"
},
{"fCode":"0402",
"fTrunk":"下列哪一种不属于ACCESS 2010的数据类型( )。",
"fChoiceA":"A.数字",
"fChoiceB":"B.文本",
"fChoiceC":"C.附件",
"fChoiceD":"D.插件",
"fAnswer":"D"
},
{"fCode":"0403",
"fTrunk":"下列哪一种不属于ACCESS 2010的关系表达式中的逻辑运算符( )。",
"fChoiceA":"A.And",
"fChoiceB":"B.Not",
"fChoiceC":"C.Or",
"fChoiceD":"D.Else",
"fAnswer":"D"
},
{"fCode":"0404",
"fTrunk":"ACCESS 2010六大对象不可以( )。",
"fChoiceA":"A.删除",
"fChoiceB":"B.隐藏",
"fChoiceC":"C.创建",
"fChoiceD":"D.创建快捷方式",
"fAnswer":"D"
},
............
]
多选题文件结构类似于:
[
{"fCode":"0401",
"fTrunk":"数据库管理系统主要包含以下功能( )。",
"fChoiceA":"A.数据定义",
"fChoiceB":"B.数据操纵",
"fChoiceC":"C.数据库运行管理",
"fChoiceD":"D.数据库建立和维护",
"fChoiceE":"E.数据通信功能",
"fAnswer":"ABCDE"
},
{"fCode":"0402",
"fTrunk":"数据库管理系统的简称是( )。",
"fChoiceA":"A.DB",
"fChoiceB":"B.DBMS",
"fChoiceC":"C.ORDBS",
"fChoiceD":"D.RDBS",
"fChoiceE":"E.DDL",
"fAnswer":"B"
},
{"fCode":"0403",
"fTrunk":"关系是具有如下特征的二维表( )。",
"fChoiceA":"A.行存储实体数据",
"fChoiceB":"B.列存储实体属性",
"fChoiceC":"C.每列具有唯一名称且数据类型一致",
"fChoiceD":"D.列的顺序任意,行的顺序也任意",
"fChoiceE":"E.任意两行内容不能完全重复",
"fAnswer":"ABCDE"
},
{"fCode":"0404",
"fTrunk":"ACCESS 2010六大对象包括( )。",
"fChoiceA":"A.表",
"fChoiceB":"B.查询",
"fChoiceC":"C.窗体",
"fChoiceD":"D.宏",
"fChoiceE":"E.模块",
"fAnswer":"ABCDE"
},
{"fCode":"0405",
"fTrunk":"ACCESS 2010中查询分为两大类型,它们是( )。",
"fChoiceA":"A.选择查询",
"fChoiceB":"B.删除查询",
"fChoiceC":"C.操作查询",
"fChoiceD":"D.生成表查询",
"fChoiceE":"E.SQL查询",
"fAnswer":"AC"
}
]
加载上述试题内容的方式官方DEMO中大量提供类似代码,直接使用即可(下面是我加载单选内容的代码):
Model.prototype.singleDataCustomRefresh = function(event){
var singleData = event.source;
$.ajax({
type: "GET",
url: require.toUrl(‘./json/singleData.json‘),
dataType: ‘json‘,
async: false,
cache: false,
success: function(data){
singleData.loadData(data);//将返回的数据加载到data组件
},
error: function(){
throw justep.Error.create("加载数据失败");
}
});
};
至于设置单选题对应数据组件singleData的limit等属性小问题及radio和radioGroup组件的常见属性设置在此小不赘述了(我使用的是radioGroup组件设计单选题界面)。
真正的问题
可能受到以前UI设计思路的影响,很可能大家的问题都集中在前面列举的问题1上,即“list组件的onClick 事件中 如何得到 list某一行的id”。但是,看X5的UI组件设计思想,这种想法本身可以理解,但是在X5 UI编程中是不能考虑的(即它无法支持)。
例如,无论在官方‘文档中心’的list组件介绍或者‘如何主动定位到行’(http://bbs.wex5.com/forum.php?mod=viewthread&tid=117208&highlight=list%E7%BB%84%E4%BB%B6)帖子中,都提供类似于这样的描述:
“
数据再多也是遍历!
wex5开发项目,准守的一个原则就是面向数据变成,面向data组件编程!
页面一切操作都是直接操作数据组件的!”
如果上面这个问题1解决了,那么其他相应问题便迎刃而解!不知官方权威开发朋友对此是如何解释的(因为那个‘文档中心’的问题贵方并没有给予解释)。
我遇到的一个具体问题
在上面以单选题为代表的设计下,我很自然地想到了radioGroup的onChanged事件,于是添加了如下代码:
Model.prototype.radioGroupSingleChange = function(event){
var crow = this.comp(‘singleData‘).getCurrentRow();//start from 0
//var id1 = this.comp(‘singleData‘).getRowID(crow);
console.log(‘Current row index: ‘+" "+crow.index());
//var context=event.bindingContext;
//var span1=this.comp(‘outputSingle‘);
//console.log("No: "+span1.innerText);
//debugger;
var data=this.comp(‘singleData‘);
var v = data.getValue(‘fCode‘);
console.log("fCode: "+v);
//get question seqCode
var i=data.val("fCode").substring(3);
//怎么总是输出相同的第1题,选择A
//store the user select
console.log(‘第‘+i+‘题‘);
//获取radioGroup的值
var val = this.comp("radioGroupSingle").val();
console.log(‘select: ‘+val);
//data.next();
//var confirmRefresh = data.confirmRefresh;
//强制刷新数据
//data.refreshData();
// try{
// data.confirmRefresh = false;
// data.refreshData();
// }finally{
// data.confirmRefresh = confirmRefresh;
// }
//data.refresh();
};
上面的注释内容我添加了又删除,删除了又添加,反复试验多次,结果只有一个:并不输出当前题号,即只是显示list中第一项,即数据组件第一行中对应内容!
一种勉强的解决办法
一下找不到那个帖子了,他是在每一个界面中只显示一个题(无论是单选还是多选),然后下面放置类似于‘第一个’‘下一个’‘最后一个’‘最开始’等常见导航按钮,这样设置界面的话问题简单得多了,即使用类似于上面的radioGroup的onChanged事件中,调用数据组件的next,prev等方法即可实现每一小题的实时定位。这种方案容易多了!
但是,想一下,如今的智能手机屏幕越来越大,不考虑使用scrollView与list组件(其中放置每道试题相关内容)结合,每一页中显示N道题,显然是不友好的设计。即上述设计方案存在片面性,而不是更为一般的方案。
我目前想到的针对我的上面设计的另一种尚未试验的方案是,在DATA组件设计中,再添加一个答案列。上面的fAnswer这一列对应于正确的标准答案,可以再添加一个用户本人选择答案的存储列,例如称为fUserAnswer。我们不去考虑类似于上面onChanged事件中准确定位每一题的题号的问题,而只是关注用户做题结束后通过对应上面的两个列,让用户知道他做对了哪些?做错了哪些即可。另外,有了上面的即时提示按钮,他当时做第X小题的正确与否情况问题也解决了。不知各位看法如何?
我抓紧试验一下,结果将会一并提交于下面。
奇怪的是,‘利用BeX5做随机抽取试题的考试系统’(http://bbs.wex5.com/forum.php?mo ... 5%E9%80%89%E9%A2%98)与我忘记的一个帖子中提到的解决类似于上述试题系统的界面设计都是每一个界面显示一个小题。我认为这并不代表X5软件设计的主流(灵活多变)。因此,可以把这种‘勉强’的方案放在一边,而探讨更为通用的办法。
小结
没有问题,根据我的上述分析与猜测,终于解决了问题!
所犯的错误,现在根本来看,还是对于X5的UI设计思想没有根本把握所致。也就是说,由于受以前语言UI设计思想的影响,还是想定位到LIST组件中某一项的ID!这种想法本身在X5设计来说就是“错误路线”!
其实,在X5数据组件为核心的UI设计理念下,我们无需关注当前做某一小题对应的哪一个小题的做题(这着实令人感觉有些奇怪!?)
基于上面想法,我把数据组件修改了一下(JSON文件结构不必作任何改变!),如下图所示:
设计中,需要把radioGroup组件的bind-ref关联到上面SingleData组件等,请参考下图:
另外,radioGroup组件的bind-itemset等属性与另一个简单数据组件radioGroupDataForSingle也关联到一起,此组件的结构很简单,只是想显示radioGroup组件的A,B,C,D四个选项而已,代码如下:
<div component="$UI/system/components/justep/data/data" autoLoad="true"
xid="radioGroupDataForSingle" idColumn="choice">
<column label="选项" name="choice" type="String" xid="xid21"/>
<data xid="default3">[{"choice":"A"},{"choice":"B"},{"choice":"C"},{"choice":"D"}]</data>
</div>
做题过程中,SingleData组件的fUserAnswer字段存储当前用户做每一个小题的实时选项。
最后,对照正确与错误答案统一进行处理即可。请参考如下代码:
Model.prototype.button1Click = function(event){
//显示用户的做题结果
//如果data指定的是data对象
var rows = this.comp(‘singleList‘).getBindingObjects();
if(rows && rows.length>0)
$.each(rows,function(i,row){
console.log(‘i: ‘+i);
console.log(‘fCode: ‘+row.val(‘fCode‘));
console.log(‘right: ‘+row.val(‘fAnswer‘)+‘---‘+‘user: ‘+row.val(‘fUserAnswer‘));
//......
});
};
控制台输出结果是没有问题的,如下:
i: 0
console.js:7 fCode: 0401
console.js:7 right: A---user: C
console.js:7 i: 1
console.js:7 fCode: 0402
console.js:7 right: D---user: B
console.js:7 i: 2
console.js:7 fCode: 0403
console.js:7 right: D---user: A
console.js:7 i: 3
console.js:7 fCode: 0404
console.js:7 right: D---user: C
console.js:7 i: 4
console.js:7 fCode: 0405
console.js:7 right: A---user: C
console.js:7 i: 5
console.js:7 fCode: 0406
console.js:7 right: A---user: C
console.js:7 i: 6
console.js:7 fCode: 0407
console.js:7 right: B---user: C
console.js:7 i: 7
console.js:7 fCode: 0408
console.js:7 right: D---user: C
console.js:7 i: 8
console.js:7 fCode: 0409
console.js:7 right: C---user: B
console.js:7 i: 9
console.js:7 fCode: 0410
console.js:7 right: D---user: B
console.js:7 i: 10
console.js:7 fCode: 0411
console.js:7 right: D---user: A
console.js:7 i: 11
最后,祝各位一举解决所有移动或者微信平台客观题有关UI设计问题.
又一扇大门敞开
补充此节,我不由得“冒了一身冷汗”(鲁迅先生在哪一篇文章中使用了类似的术语?),亏得我前面的语言描述还算谦逊一些。
刚刚,在学习bind-select属性时,我通过帖子http://bbs.wex5.com/forum.php?mod=viewthread&tid=82417&extra=&page=1,找到了又一种方法的启示。以及上面的单选题UI设计为例,即想寻找LIST组件的当前行序号是可以实现的,代码如下:
Model.prototype.li1Touchend = function(event){
//var rows = this.comp(“list1”).getBindingObjects();
var row = event.bindingContext.$object;
//debugger;
console.log(row.val(‘fCode‘));
};
注意,我使用的也是list中li的touchstart和touchend事件。另外,前面帖子中还提供另一个重要技巧,即:
根据索引获得行的方法找到了
data.datas.get()[index]
本文出自 “青峰” 博客,请务必保留此出处http://zhuxianzhong.blog.51cto.com/157061/1910985
原文地址:http://zhuxianzhong.blog.51cto.com/157061/1910985