JavaScript使用DeviceOne开发实战(三)仿微信应用


Posted in Javascript onDecember 02, 2015

这是一个系列的文档,长期目标是利用DeviceOne开发一些目前使用广泛的优质手机应用,我们会最大化的实现这些应用的每一个功能和细节,不只停留在简单的UI模仿和Demo阶段,而是一个基本可以使用的实际App。 

在实现的过程中,会有很多困难,还会发现有一些功能目前缺乏组件支持而无法实现,也会碰见各种移动开发中都会碰到的常见技术问题。一步一步的操作和问题的解决可以让开发者直观的了解通过DeviceOne如何开发一个实际App,也可以了解移动开发本身的很多技术细节,可以让App开发者少走很多弯路。

这个文档主要介绍微信的模仿。

第一部分是框架的搭建

UE和UI设计,通常App的开发需要产品人员的UE设计和美工人员的UI设计,这二个步骤完成后才开始功能实现。我们现在是模仿已有的微信,这些步骤都可以省去,看下图是美工提供的主界面UI设计图,里面的元素的尺寸都标记了。
 

JavaScript使用DeviceOne开发实战(三)仿微信应用

1.新建项目:我们选择了Simple template,选择空模板是因为我们可以更细致的讲解开发的过程。实际上这个项目是可以选择Multi View with ViewShower模板更合适。

JavaScript使用DeviceOne开发实战(三)仿微信应用

 2.简单分析一下主界面,整个主界面的尺寸是iphone6的尺寸750*1334. 分上下二个部分,底部是一个Bottom Bar导航栏,上面是4个独立的界面,这4个界面永远只有一个界面显示,其他三个界面在内存中,靠底部的导航来切换,这个适合用do_ViewShower为框架的主体如下图:

JavaScript使用DeviceOne开发实战(三)仿微信应用

对应这个设计,我们在新建的项目上删除自动生成的button,添加一个do_ALayout组件和do_ViewShower组件,设置它们的高宽和x,y坐标:

JavaScript使用DeviceOne开发实战(三)仿微信应用

3.接下来我们为ViewShower增加4个分页,在这里我还强调一下,实际App会多人参与,我们的代码结构要清晰易读,命名尽量使用英文,实在不行使用中文全拼,包括要建立多级子目录,不要都混在一起。在这里我们增加4个子目录:chats, contacts, discover, me. 右键每个子目录,选择New--Other--DeviceOne--UI File,创建4个index.ui,对应的index.ui.js会自动生成。

JavaScript使用DeviceOne开发实战(三)仿微信应用

这里注意把这4个ui文件对应的根节点ALayout的高度设置为1220,因为这4个ui文件都是主界面的ViewShower的子view,不应该超过ViewShower的大小。

另外为每个ui里都添加一个label,标记中文名,只是一会儿debug看真实效果更清楚。

JavaScript使用DeviceOne开发实战(三)仿微信应用

4.Bottom Bar内简单添加4个按钮,然后添加相应的js代码实现ViewShower的页面切换功能。这里提一个技巧,选中2个或以上组件,可以使用各种对齐方式的功能,如下图:

JavaScript使用DeviceOne开发实战(三)仿微信应用

5.在index.ui.js里添加ViewShower初始化的代码和按钮的点击事件。

var viewshower = ui("viewshower");
var page = sm("do_Page");
// 为viewshower增加4个子页面
viewshower.addViews([ {
    id : "chats",// 页面的标示
    path : "source://view/chats/index.ui"// 页面的路径
}, {
    id : "contacts",
    path : "source://view/contacts/index.ui"
}, {
    id : "discover",
    path : "source://view/discover/index.ui"
}, {
    id : "me",
    path : "source://view/me/index.ui"
} ]);
// 初始化先显示第一个页面
viewshower.showView("chats");
var button1 = ui("do_Button_1");
button1.on("touch", function() {
    viewshower.showView("chats");
});
var button2 = ui("do_Button_2");
button2.on("touch", function() {
    viewshower.showView("contacts");
});
var button3 = ui("do_Button_3");
button3.on("touch", function() {
    viewshower.showView("discover");
});
var button4 = ui("do_Button_4");
button4.on("touch", function() {
    viewshower.showView("me");
});

6.我们来真机看看运行效果,启动设计器的调试服务,启动手机端的调试程序,最后我们看到的效果图如下,iOS和Android界面完全一样。点击底部的4个按钮,切换效果都是好的:

JavaScript使用DeviceOne开发实战(三)仿微信应用

7. 这一节的内容就到这里,框架工作完成了吗?只能说框架的第一步工作完成,如果我们有很多同事一起开发这个App的话,我们可以把工作开始分开并行,接下来分5个部分:

* Bottom Bar的完成
* /chats/index.ui的完成
* /contacts/index.ui的完成
* /discover/index.ui的完成
* /me/index.ui的完成
多人并行工作的前提是代码版本管理比如SVN,GIT,我们这里使用GIT,地址是https://github.com/do-project/Fake-Weixin,每一节结束后我们都会提交GIT服务,你可以下载这个节点的代码参考。我们也会在附件附加这一节的项目代码。

下一节我们完成第一个子任务,BottomBar的实现。

-------------------------------------------------------------

这一节主要是完成底部导航栏的实现。

0. 我们首先来分析一下界面效果图和设计图

JavaScript使用DeviceOne开发实战(三)仿微信应用

 JavaScript使用DeviceOne开发实战(三)仿微信应用

整个底部导航分4个重复的部分,每个部分由一个图片ImageView和底部标题Label,以及右上角标示,这个标示可以用圆角Label来实现,这个标示缺省的时候应该是隐藏的。

1. 第一步我们得找到对应的图片资源,通常开发这些资源由美工提供,我们现在模仿微信,最好的方式是从微信原生安装包里获取,不能直接靠截图,而是打开微信ios,android安装包,ios的安装包是ipa和android的安装包apk都是一个压缩文件,解开可以获取到一些图片资源。目前我只需要底部8个图标,包括未点中以及点中的高亮图标,外面把这些图标放在image目录下

JavaScript使用DeviceOne开发实战(三)仿微信应用

2. 先删除先前增加的4个临时按钮,然后按照美工提供的尺寸数据布局好新的组件,包括4个do_ImageView组件和4个Label组件以及4个右上角的Label
简单计算可以得出ImageView的大小是60*60。这里有一个小技巧,设置好一组ImageView和Label后选中2个组件,然后右键“Copy”,然后再"Paste"三次,还可以选中多个组件各种对齐。

JavaScript使用DeviceOne开发实战(三)仿微信应用

再细调一下,把图片和文字设置好,图片设置就是设置ImageView的source属性,Label需要设置文字居中,设置textAlign属性为center,设置字体,设置背景色,前景色等等,设置右上角三个Label的visible为false。中间添加一个ALayout设置背景为灰色,作为ViewShower和Bottom Bar上下的分割线. 这里要注意,右上角的正圆形Label的实现方式就是设置border属性,border设置为FF0000FF,1,15 表示边界线的颜色是红色,宽度1,圆角半径15(Label的宽高都是30),从而实现正圆。

 JavaScript使用DeviceOne开发实战(三)仿微信应用

在真机上测试一下效果,iPhone和Android手机真机的效果图如下:

JavaScript使用DeviceOne开发实战(三)仿微信应用<ignore_js_op>JavaScript使用DeviceOne开发实战(三)仿微信应用

3. 这个时候会有2个问题,如果给ImageView上添加点击事件的话,用户必须点中这个图片才能触发点击,这样体验不好。第二个问题是图片在Android上稍微有点变形,如果在比如iPhone4上,可能圆形会变成椭圆,这个问题是由于不同手机宽高比的差异。

要解决的办法是:

* 把Bottom Bar所在的ALayout增加4个一样大小的子ALayout,然后把imageview,label都放在对应的子ALayout上,然后给子ALayout增加点击事件,这样用户的手指只要接触到差不多位置都能触发事件
* 把上面的4个子ALayout的isStretch属性改成false,这个原理可以参考文档 ALayout的示例demo

JavaScript使用DeviceOne开发实战(三)仿微信应用

4. 修改index.ui.js,添加代码主要是在底部bottom bar切换按钮的时候修改所有图标的颜色和字的前景色。

var button = ui("do_Button_");
var imageview = ui("do_ImageView_");
var label = ui("do_Label_");
button.on("touch", function() {
    showView("chats");
});
var button = ui("do_Button_");
var imageview = ui("do_ImageView_");
var label = ui("do_Label_");
button.on("touch", function() {
    showView("contacts");
});
var button = ui("do_Button_");
var imageview = ui("do_ImageView_");
var label = ui("do_Label_");
button.on("touch", function() {
    showView("discover");
});
var button = ui("do_Button_");
var imageview = ui("do_ImageView_");
var label = ui("do_Label_");
button.on("touch", function() {
    showView("me");
});
function showView(name) {
    viewshower.showView(name);
    if (name == "chats") {
        imageview.source = "source://image/tabbar_mainframeHL.png";
        label.fontColor = "BBFF";
    } else {
        imageview.source = "source://image/tabbar_mainframe.png";
        label.fontColor = "FFFFF";
    }
    if (name == "contacts") {
        imageview.source = "source://image/tabbar_contactsHL.png";
        label.fontColor = "BBFF";
    } else {
        imageview.source = "source://image/tabbar_contacts.png";
        label.fontColor = "FFFFF";
    }
    if (name == "discover") {
        imageview.source = "source://image/tabbar_discoverHL.png";
        label.fontColor = "BBFF";
    } else {
        imageview.source = "source://image/tabbar_discover.png";
        label.fontColor = "FFFFF";
    }
    if (name == "me") {
        imageview.source = "source://image/tabbar_meHL.png";
        label.fontColor = "BBFF";
    } else {
        imageview.source = "source://image/tabbar_me.png";
        label.fontColor = "FFFFF";
    }
}

到此为止,底部导航栏基本实现完成,这一节比较简单,主要是一些细致的ui拖拽调整。我们用调试版本看一下Android,iOS的效果都非常不错。

我们开始实现ViewShower的第一页主体内容.

---------------------------------------------------------------------------------------

接上一节 底部导航  ,我们这一节主要是完成微信4个主页面的第一个页面“微信”页面,这一节内容比较多,我们分多个跟帖来完成

0 老规矩,先分析一下UI,由三个大部分组成,系统状态栏,工具栏和微信聊天记录列表。

 JavaScript使用DeviceOne开发实战(三)仿微信应用

1. 系统状态栏高度40,背景黑色,我们注意到微信的首页4个子页面都有这个系统状态栏,这样我们需要做一个整体框架的调整,没有必要为4个子页面都添加一个状态栏,只需要在ViewShower上添加一个就可以,对应的ViewShower和子页面的高度都变成1180. 

JavaScript使用DeviceOne开发实战(三)仿微信应用

对应设计器里index.ui, chats/index.ui, contacts/index.ui, discover/index.ui, me/index.ui 都要作相应的height,y属性值的变化。效果如下: 

JavaScript使用DeviceOne开发实战(三)仿微信应用

2. 我们回到chats/index.ui ,先增加工具导航栏(高度80)和里面的标题和工具按钮,这个需要增加加号的资源文件,因为这个文件是chats页面专有的,所以存在image/chats/bar_add.png下。
我们看看真机效果,我们注意到顶部多出一块黑色区域,这是设计器里的增加的状态栏,因为这个页面是从系统状态栏开始往下绘制的,所以会把设计器里这一部分多出来,

JavaScript使用DeviceOne开发实战(三)仿微信应用

要解决的方法是修改app.js,openPage增加一个statusBarState参数(API文档),设置为transparent表示页面从屏幕最顶端开始绘制。

var d1 = require("deviceone");
var app = d1.sm("do_App");
app.on("loaded", function() {
    this.openPage({
        source : "source://view/index.ui",
        statusBarState : "transparent"
    });
});

再来看看真机效果图:

JavaScript使用DeviceOne开发实战(三)仿微信应用

3. 主体部分是一个do_ListView,接下来设置ListView的cell和数据。
ListView的cell指列表框的每一行,比如ListView有100行数据,实际上可见的屏幕永远只能看到8,9行左右,所以我们手势上下滑动的时候并没有创建100行,而是重复使用这8,9行,只不过替换里面的数据而已。我们称之为行模板,在DeviceOne里这种模板也是一个ui文件,比如这里我们在chats子目录下新建一个chat_cell.ui,这个ui基本界面如下:

JavaScript使用DeviceOne开发实战(三)仿微信应用

按照美工的设计尺寸我们来拖拽UI

JavaScript使用DeviceOne开发实战(三)仿微信应用 

这里同样需要考虑纯圆变形问题,需要设置好文字大小,前景色等属性,大家可以看到里面有多个do_Label,do_ImageView组件,由于模板ui是靠后期绑定数据的,所以在设计阶段都是空白的。

JavaScript使用DeviceOne开发实战(三)仿微信应用

接下来我们需要设计chat_cell.ui对应的数据,通常为了用户体验,需要尽可能的减少网络交互,页面打开的时候通常先读取本地的数据文件,把界面显示出来,然后再考虑是否要进行网络连接来获取最新数据,所以App开发需要仔细考虑数据的本地化读写和数据的时效性的平衡。
DeviceOne的传递数据基本上都是标准的JSON格式。如下图,chat_cell.ui里的组件属性和JSON数据结构对应的关系

JavaScript使用DeviceOne开发实战(三)仿微信应用

对应的映射关系的代码在chat_cell.ui.js如下,我们可以看到映射关系的左边是组件id.组件属性名,右边是数据JSON的key名称:

//related to chat_cell.ui
var root = ui("$");;//$是这个ui文件根节点组件的通配符,如果指定组件的id,也可以用id来获取对象
root.setMapping({
    "photo_imageview.source" : "photo",
    "name_label.text" : "name",
    "lastmessage_label.text" : "lastmessage.message",
    "lasttime_label.text" : "lastmessage.time",
    "unread_label.visible" : "unread",
    "unread_label.text" : "unread-count",
    "name_label.fontColor" : "isgroup",
});

对应的数据本应该是第一次运行从网络上获取之后再缓存到本地的,我们是模拟,所以先手动生成一个文件到data/chats/chat.json

JavaScript使用DeviceOne开发实战(三)仿微信应用

4. 我们回到chats/index.ui,我们需要给这里ui文件里的listview设置模板,绑定数据。
设置index.ui 里的listview的templates属性为        source://view/chats/chat_cell.ui
注意:chat_cell.ui存储在source/default/view/chats/chat_cell.ui,但是source://的根节点指的是 source/default/目录
在index.ui.js里添加绑定数据的代码

//related to index.ui
var storage = sm("do_Storage");
var listdata = mm("do_ListData");
var listview = ui("listview");
var json_path = "data://chats/chat.json";//本地缓存的数据
if (storage.fileExist(json_path)) {
    storage.readFile(json_path, function(data, e) {
        //deviceone.print(JSON.stringify(data));
        listdata.addData(data);
        listview.bindItems(listdata);
        listview.refreshItems();
    })
}
var page = sm("do_Page");
page.on("loaded",function(){
    //这个页面加载完显示出来后触发这个事件
    //我们可以在这个事件里去获取最新的网络数据,来更新listview和data/chats/chat.json
});

我们在真机上看看效果

JavaScript使用DeviceOne开发实战(三)仿微信应用

在运行中有几个细节:

* 上下滑动的时候,图片不断的刷新,原因是我们的ImageView的source是网络图片,每次显示的时候都是从网络上获取的,所以这里需要把chat_cell.ui里的ImageView的cacheType属性换成"always" 意思是只从网络上读取一次就会缓存到本地,下一次不会再从网络上读取了。关于cacheType属性参考API文档
* ImageView也是圆角的,圆角通常可以使用border属性来设置,但是android只有ImageView不能通过border来设置圆角,ImageView还有一个专有属性radius来设置Android才有效,这个我们以后可以改进

5. 处理右上角的add按钮,点击弹出菜单

先给右上角ImageView的enable属性设置为true,才可以处理点击事件,在chats/index.ui.js里添加代码

var add_button = ui("add_imageview");
add_button.on("touch", function() {
    var menu = ui("menu_id");
    if (menu) {//如果已经add过,就只是让这个view显示,而不是add一个新的
        if (menu.visible == false)
            menu.visible = true;
    } else {
        main.add("menu_id", "source://view/chats/chat_add_menu.ui");
    }
});

其中chat_add_menu.ui 是弹出的菜单对应的ui文件,这个ui文件的根节点大小和chat/index.ui一样,这样确保我点击任何空白处都可以关闭这个菜单(实际上是隐藏这个菜单),我们在这个ui文件里把对应的布局都拖拽好,其中需要添加4个资源png文件。

这里有个小技巧,顶部的三角形标记只能通过一个ImageView加载一个三角形图标来实现。

JavaScript使用DeviceOne开发实战(三)仿微信应用

我们再给chat_add_menu的根节点添加点击事件,点击的时候把自己隐藏,在chat_add_menu.js

var root = ui("$");
root.on("touch",function(){
    root.visible = false;
});

最后我们先看看真机效果,点击加号弹出菜单,点击任何地方都把菜单隐藏。

JavaScript使用DeviceOne开发实战(三)仿微信应用

这一节暂时先到这里,我们先开始拖拽后几个主页面,那几个页面基本完成后再重新回到这一个页面来细琢。

Javascript 相关文章推荐
js技巧--转义符&quot;\&quot;的妙用
Jan 09 Javascript
Javascript实现CheckBox的全选与取消全选的代码
Jul 20 Javascript
JavaScript 原型链学习总结
Oct 29 Javascript
学习JavaScript的最佳方法分享
Oct 21 Javascript
用js小类库获取浏览器的高度和宽度信息
Jan 15 Javascript
父页面显示遮罩层弹出半透明状态的dialog
Mar 04 Javascript
关于Vue.js一些问题和思考学习笔记(2)
Dec 02 Javascript
使用grunt合并压缩js和css文件的方法
Mar 02 Javascript
微信小程序 中wx.chooseAddress(OBJECT)实例详解
Mar 31 Javascript
使用D3.js制作图表详解
Aug 13 Javascript
JavaScript callback回调函数用法实例分析
May 08 Javascript
Vue项目实现换肤功能的一种方案分析
Aug 28 Javascript
jquery密码强度校验
Dec 02 #Javascript
基于jQuery实现的双11天猫拆红包抽奖效果
Dec 01 #Javascript
jquery实现全选、反选、获得所有选中的checkbox
Sep 13 #Javascript
js实现继承的5种方式
Dec 01 #Javascript
6种javascript显示当前系统时间代码
Dec 01 #Javascript
基于jQuery实现网页打印功能
Dec 01 #Javascript
jQuery-1.9.1源码分析系列(十一)DOM操作续之克隆节点
Dec 01 #Javascript
You might like
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
php中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
支付宝服务窗API接口开发php版本
2016/07/20 PHP
PHPWind9.0手动屏蔽验证码解决后台关闭验证码但是依然显示的问题
2016/08/12 PHP
php使用curl实现简单模拟提交表单功能
2017/05/15 PHP
Safari5中alert的无限循环BUG
2011/04/07 Javascript
原生js实现fadein 和 fadeout淡入淡出效果
2014/06/05 Javascript
简单方法判断JavaScript对象为null或者属性为空
2014/09/26 Javascript
jQuery选择器源码解读(八):addCombinator函数
2015/03/31 Javascript
js为什么不能正确处理小数运算?
2015/12/29 Javascript
AngularJS向后端ASP.NET API控制器上传文件
2016/02/03 Javascript
javascript正则表达式总结
2016/02/29 Javascript
jQuery如何获取动态添加的元素
2016/06/24 Javascript
JavaScript中误用/g导致的正则test()无法正确重复执行的解决方案
2016/07/27 Javascript
AngularJS实践之使用NgModelController进行数据绑定
2016/10/08 Javascript
纯JS实现表单验证实例
2016/12/24 Javascript
bootstrap导航条实现代码
2016/12/28 Javascript
关于vue面试题汇总
2018/03/20 Javascript
使用JavaScript生成罗马字符的实例代码
2018/06/08 Javascript
vue 刷新之后 嵌套路由不变 重新渲染页面的方法
2018/09/13 Javascript
vue 监听某个div垂直滚动条下拉到底部的方法
2018/09/15 Javascript
jQuery单页面文字搜索插件jquery.fullsearch.js的使用方法
2020/02/04 jQuery
详解Python装饰器由浅入深
2016/12/09 Python
Python Gitlab Api 使用方法
2019/08/28 Python
在tensorflow以及keras安装目录查询操作(windows下)
2020/06/19 Python
资生堂美国官网:Shiseido美国
2016/09/02 全球购物
Smashbox官网:美国知名彩妆品牌
2017/01/05 全球购物
高中毕业自我鉴定
2013/12/19 职场文书
课前三分钟演讲稿
2014/04/24 职场文书
供用电专业求职信
2014/07/07 职场文书
干部作风建设年活动剖析材料
2014/10/23 职场文书
2014年材料员工作总结
2014/11/19 职场文书
优秀教师个人总结
2015/02/11 职场文书
导游词范文
2015/02/13 职场文书
2019新员工试用期转正工作总结范文
2019/08/21 职场文书
高通2023 年将发布高性能PC处理器
2022/04/29 数码科技