이 문서의 장기적인 목표는 DeviceOne을 사용하여 현재 널리 사용되는 고품질 모바일 애플리케이션을 개발하는 것입니다. 우리는 단순히 단순한 것에 머물지 않고 이러한 애플리케이션의 모든 기능과 세부 사항을 극대화할 것입니다. UI 모방 및 데모 단계이지만 기본적으로 사용할 수 있는 실용적인 App입니다.
구현 과정에서 많은 어려움이 있을 것입니다. 또한 현재 일부 기능은 구성 요소 지원이 부족하여 구현할 수 없다는 점을 알게 될 것입니다. 또한 다양한 모바일 개발에서 직면하게 되는 일반적인 기술적 문제에 직면하게 될 것입니다. 단계별 운영과 문제 해결을 통해 개발자는 DeviceOne을 통해 실제 앱을 개발하는 방법을 직관적으로 이해할 수 있으며, 모바일 개발 자체의 많은 기술적 세부 사항을 이해할 수 있어 앱 개발자가 많은 우회를 피할 수 있습니다.
이 문서는 주로 WeChat 모방을 소개합니다.
첫 번째 부분은 프레임워크 구축
UE 및 UI 디자인. 일반적으로 앱 개발에는 제품 직원의 UE 디자인과 아트 직원의 UI 디자인이 필요하며 이 두 단계가 완료되어야만 시작할 수 있습니다. 이제 기존 위챗을 모방하고 있으며, 이 단계는 생략 가능합니다. 아래 그림은 작가가 제공한 메인 인터페이스 UI 디자인이며, 내부 요소의 크기가 표시되어 있습니다.
1. 새 프로젝트: 개발 과정을 더 자세히 설명할 수 있기 때문에 빈 템플릿을 선택했습니다. 실제로 이 프로젝트의 경우 Multi View with ViewShower 템플릿을 선택하는 것이 더 적합합니다.
2. 메인 인터페이스에 대한 간략한 분석입니다. 전체 메인 인터페이스의 크기는 750*1334 크기로 상단과 하단으로 나누어져 있습니다. 바 네비게이션 바, 상단은 4개의 독립적인 인터페이스입니다. 이 4개의 인터페이스는 항상 하나의 인터페이스만 표시되며 나머지 3개의 인터페이스는 메모리에 있으며 하단의 네비게이션으로 전환할 수 있습니다. 이는 do_ViewShower를 본체로 사용하는 데 적합합니다. 아래와 같이 프레임을 구성합니다.
이 디자인에 맞춰 새로 생성된 프로젝트에서 자동으로 생성된 버튼을 삭제하고 do_ALayout 구성 요소와 do_ViewShower 구성 요소를 추가한 후 높이, 너비, x, y 좌표를 설정합니다.
3. 다음으로 ViewShower에 4페이지를 추가합니다. 여기서도 실제 앱에는 여러 사람이 참여해야 한다는 점을 강조하고 싶습니다. 이름을 지정할 때 영어를 사용하세요. 불가능합니다. 전체 중국어 철자를 사용하세요. 여기에는 다중 레벨 하위 디렉터리를 만들고 모두 함께 혼합하지 않는 것도 포함됩니다. 여기에서는 chats, Contacts, discover, me라는 4개의 하위 디렉터리를 추가합니다. 각 하위 디렉터리를 마우스 오른쪽 버튼으로 클릭하고 New--Other--DeviceOne--UI File을 선택하고 4개의 index.ui를 생성하면 해당 index.ui.js가 자동으로 생성됩니다. .
여기서 이 4개의 UI 파일에 해당하는 루트 노드 ALayout의 높이를 1220으로 설정하는 데 주의하세요. 왜냐하면 이 4개의 UI 파일은 모두 기본 인터페이스 ViewShower의 하위 뷰이고 ViewShower의 크기를 초과해서는 안 되기 때문입니다.
또한 각 UI에 라벨을 추가하여 중국어 이름을 표시하면 나중에 디버깅하여 실제 효과를 더 명확하게 확인할 수 있습니다.
4. 하단 바에 버튼 4개를 추가한 후 해당 js 코드를 추가하면 ViewShower의 페이지 전환 기능이 구현됩니다. 2개 이상의 컴포넌트를 선택하면 아래와 같이 다양한 정렬 기능을 사용할 수 있다는 팁이 있습니다.
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. 실제 머신에서 실행 효과를 살펴보고, 디자이너의 디버깅 서비스를 시작하고, 휴대폰에서 디버깅 프로그램을 시작하면, 우리가 보는 최종 렌더링은 다음과 같습니다. 정확히 동일합니다. 하단의 버튼 4개를 클릭하면 전환 효과가 모두 좋습니다.
7. 프레임워크 작업이 완료되었나요? 프레임워크의 첫 번째 단계가 완료되었다고 할 수 있습니다. 이 앱을 함께 개발하는 동료가 많으면 작업을 병렬로 분리한 다음 5개 부분으로 나눌 수 있습니다.
* 하단바 완성
* /chats/index.ui 완성
* /contacts/index.ui 완성
* /discover/index.ui 완성
* /me/index.ui 완성
여러 사람이 동시에 작업하기 위한 전제조건은 SVN, GIT 등의 코드 버전 관리입니다. 여기서는 주소가 https://github.com/do-project/Fake-Weixin입니다. 각 섹션마다 GIT 서비스가 제출됩니다. 이 노드의 코드 참조를 다운로드할 수 있습니다. 또한 첨부 파일에 이 섹션의 프로젝트 코드를 첨부하겠습니다.
다음 섹션에서는 첫 번째 하위 작업인 BottomBar 구현을 완료합니다.
---------------------------------- --- ---------------
이 섹션에서는 주로 하단 탐색 메뉴 구현을 완료하는 방법에 대해 설명합니다.
0. 먼저 인터페이스 렌더링과 디자인 도면을 분석해 보겠습니다
하단 내비게이션 전체는 4개의 반복 부분으로 나누어져 있습니다. 각 부분은 ImageView, 하단 제목 라벨, 오른쪽 상단의 라벨로 구성됩니다. 이 라벨은 둥근 모서리 라벨로 구현될 수 있습니다. 기본적으로 숨겨져 있습니다.
1. 첫 번째 단계는 해당 사진 리소스를 찾는 것입니다. 일반적으로 이러한 리소스는 WeChat을 모방하고 있습니다. 가장 좋은 방법은 스크린샷에 직접 의존할 수 없습니다. , 그러나 WeChat ios, 안드로이드 설치 패키지, ios 설치 패키지는 ipa이고 안드로이드 설치 패키지 apk는 모두 압축 파일이므로 압축을 풀어 일부 이미지 리소스를 얻을 수 있습니다. 현재는 클릭하지 않은 강조 표시된 아이콘과 클릭한 아이콘을 포함하여 하단에 있는 8개의 아이콘만 필요합니다.
2. 먼저 추가한 임시 버튼 4개를 삭제한 후 아티스트가 제공한 크기 데이터에 따라 do_ImageView 구성 요소 4개, Label 구성 요소 4개, 오른쪽 상단에 Label 4개 등 새 구성 요소를 배치합니다
간단한 계산으로 ImageView의 크기는 60*60임을 알 수 있습니다. 여기에 약간의 요령이 있습니다. ImageView 및 Label 세트를 설정한 후 2개의 구성 요소를 선택한 다음 "복사"를 마우스 오른쪽 버튼으로 클릭하고 "붙여넣기"를 세 번 클릭하면 다양한 정렬을 위해 여러 구성 요소를 선택할 수도 있습니다.
다시 조정하고 이미지와 텍스트를 설정합니다. 이미지 설정은 ImageView의 source 속성을 설정하고, textAlign 속성을 가운데로 설정하고, 글꼴을 설정하고, 배경색을 설정해야 합니다. , 전경색 등은 오른쪽 상단을 설정합니다. 모서리에 있는 세 개의 라벨의 가시성은 거짓입니다. 중앙에 ALayout을 추가하고 ViewShower와 하단 표시줄 사이를 구분하는 선으로 배경을 회색으로 설정합니다. 여기서 오른쪽 상단 모서리의 완벽한 원형 라벨은 테두리 속성을 FF0000FF로 설정하여 구현됩니다. ,1,15 색상은 빨간색, 너비는 1, 필렛 반경은 15(Label의 너비와 높이가 모두 30)이므로 완벽한 원이 됩니다.
실제 기기에서 효과를 테스트해 보세요. 실제 iPhone과 Android 휴대폰의 렌더링은 다음과 같습니다.
3. 이때 두 가지 문제가 발생합니다. ImageView에 클릭 이벤트를 추가하면 사용자가 클릭을 유발하기 위해 이미지를 클릭해야 하는데 이는 좋은 경험이 아닙니다. 두 번째 문제는 예를 들어 iPhone 4의 경우 사진이 약간 변형된다는 것입니다. 이 문제는 휴대폰마다 화면 비율의 차이로 인해 발생합니다.
해결책은 다음과 같습니다.
* 하단 바가 위치한 ALayout에 동일한 크기의 하위 ALayout 4개를 추가한 후, 해당 하위 ALayout에 이미지뷰와 라벨을 배치하고, 하위 ALayout에 클릭 이벤트를 추가하여 사용자의 손가락은 같은 위치만 터치하면 됩니다. 이벤트가 트리거될 수 있습니다
* 위 4개 하위 ALayout의 isStretch 속성을 false로 변경하세요. 이 원칙에 대해서는 ALayout 예제 데모 문서를 참조하세요
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,由三个大部分组成,系统状态栏,工具栏和微信聊天记录列表。
1. 系统状态栏高度40,背景黑色,我们注意到微信的首页4个子页面都有这个系统状态栏,这样我们需要做一个整体框架的调整,没有必要为4个子页面都添加一个状态栏,只需要在ViewShower上添加一个就可以,对应的ViewShower和子页面的高度都变成1180.
对应设计器里index.ui, chats/index.ui, contacts/index.ui, discover/index.ui, me/index.ui 都要作相应的height,y属性值的变化。效果如下:
2. 我们回到chats/index.ui ,先增加工具导航栏(高度80)和里面的标题和工具按钮,这个需要增加加号的资源文件,因为这个文件是chats页面专有的,所以存在image/chats/bar_add.png下。
我们看看真机效果,我们注意到顶部多出一块黑色区域,这是设计器里的增加的状态栏,因为这个页面是从系统状态栏开始往下绘制的,所以会把设计器里这一部分多出来,
要解决的方法是修改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" }); });
再来看看真机效果图:
3. 主体部分是一个do_ListView,接下来设置ListView的cell和数据。
ListView的cell指列表框的每一行,比如ListView有100行数据,实际上可见的屏幕永远只能看到8,9行左右,所以我们手势上下滑动的时候并没有创建100行,而是重复使用这8,9行,只不过替换里面的数据而已。我们称之为行模板,在DeviceOne里这种模板也是一个ui文件,比如这里我们在chats子目录下新建一个chat_cell.ui,这个ui基本界面如下:
按照美工的设计尺寸我们来拖拽UI
这里同样需要考虑纯圆变形问题,需要设置好文字大小,前景色等属性,大家可以看到里面有多个do_Label,do_ImageView组件,由于模板ui是靠后期绑定数据的,所以在设计阶段都是空白的。
接下来我们需要设计chat_cell.ui对应的数据,通常为了用户体验,需要尽可能的减少网络交互,页面打开的时候通常先读取本地的数据文件,把界面显示出来,然后再考虑是否要进行网络连接来获取最新数据,所以App开发需要仔细考虑数据的本地化读写和数据的时效性的平衡。
DeviceOne的传递数据基本上都是标准的JSON格式。如下图,chat_cell.ui里的组件属性和JSON数据结构对应的关系
对应的映射关系的代码在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
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 });
我们在真机上看看效果
在运行中有几个细节:
* 上下滑动的时候,图片不断的刷新,原因是我们的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加载一个三角形图标来实现。
我们再给chat_add_menu的根节点添加点击事件,点击的时候把自己隐藏,在chat_add_menu.js
var root = ui("$"); root.on("touch",function(){ root.visible = false; });
最后我们先看看真机效果,点击加号弹出菜单,点击任何地方都把菜单隐藏。
这一节暂时先到这里,我们先开始拖拽后几个主页面,那几个页面基本完成后再重新回到这一个页面来细琢。