Il s'agit d'une série de documents. L'objectif à long terme est d'utiliser DeviceOne pour développer des applications mobiles de haute qualité qui sont actuellement largement utilisées. Nous maximiserons chaque fonction et chaque détail de ces applications, et ne nous contenterons pas de rester simples. Étape d'imitation et de démonstration de l'interface utilisateur, mais une application pratique qui peut être essentiellement utilisée.
Au cours du processus de mise en œuvre, vous rencontrerez de nombreuses difficultés. Vous constaterez également que certaines fonctions manquent actuellement de support de composants et ne peuvent pas être mises en œuvre. Vous rencontrerez également des problèmes techniques courants rencontrés dans divers développements mobiles. Le fonctionnement étape par étape et la résolution des problèmes peuvent permettre aux développeurs de comprendre intuitivement comment développer une application réelle via DeviceOne, et peuvent également comprendre de nombreux détails techniques du développement mobile lui-même, ce qui peut aider les développeurs d'applications à éviter de nombreux détours.
Ce document présente principalement l'imitation WeChat.
La première partie est la construction de la charpente
Conception de l'UE et de l'interface utilisateur. Généralement, le développement d'applications nécessite la conception de l'UE par le personnel du produit et la conception de l'interface utilisateur par le personnel artistique. La mise en œuvre des fonctions ne peut commencer qu'une fois ces deux étapes terminées. Nous imitons maintenant le WeChat existant, et ces étapes peuvent être omises. L'image ci-dessous est la conception principale de l'interface utilisateur fournie par l'artiste, et les tailles des éléments à l'intérieur sont marquées.
1. Nouveau projet : Nous avons choisi le modèle simple. Nous avons choisi le modèle vide car nous pouvons expliquer le processus de développement plus en détail. En fait, pour ce projet, il est plus approprié de choisir le modèle Multi View avec ViewShower.
2. Une brève analyse de l'interface principale. La taille de l'ensemble de l'interface principale est la taille d'un iPhone 6, 750*1334. Elle est divisée en deux parties, la partie supérieure et la partie inférieure. Barre de navigation, et le haut est composé de 4 interfaces indépendantes. Ces 4 interfaces Une seule interface est toujours affichée. Les trois autres interfaces sont en mémoire et peuvent être commutées par la navigation en bas. Cela convient à l'utilisation de do_ViewShower comme corps principal. du cadre comme indiqué ci-dessous :
Correspondant à cette conception, nous supprimons le bouton généré automatiquement sur le projet nouvellement créé, ajoutons un composant do_ALayout et un composant do_ViewShower, et définissons leurs coordonnées hauteur, largeur, x et y :
3. Ensuite, nous ajoutons 4 pages à ViewShower. Je voudrais également souligner ici que l'application réelle impliquera plusieurs personnes. Notre structure de code doit être claire et facile à lire. ce n'est pas possible, utilisez l'orthographe chinoise complète, notamment en créant des sous-répertoires à plusieurs niveaux et en ne les mélangeant pas tous. Ici, nous ajoutons 4 sous-répertoires : chats, contacts, découverte, moi. Cliquez avec le bouton droit sur chaque sous-répertoire, sélectionnez Nouveau--Autre--DeviceOne--UI File, créez 4 index.ui et l'index.ui.js correspondant générera automatiquement. .
Attention ici à définir la hauteur du nœud racine ALayout correspondant à ces 4 fichiers ui à 1220, car ces 4 fichiers ui sont tous des sous-vues du ViewShower de l'interface principale et ne doivent pas dépasser la taille du ViewShower.
De plus, ajoutez une étiquette à chaque interface utilisateur pour marquer le nom chinois. Vous pourrez le déboguer plus tard pour voir plus clairement l'effet réel.
4. Ajoutez simplement 4 boutons dans la barre inférieure, puis ajoutez le code js correspondant pour implémenter la fonction de changement de page de ViewShower. Voici un conseil. Sélectionnez 2 composants ou plus et vous pourrez utiliser diverses fonctions d'alignement, comme indiqué ci-dessous :
5. Ajoutez le code d'initialisation de ViewShower et l'événement de clic sur le bouton dans index.ui.js.
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. Jetons un coup d'œil à l'effet d'exécution sur une machine réelle, démarrons le service de débogage du concepteur et démarrons le programme de débogage sur le téléphone mobile. Le rendu final que nous voyons est le suivant. sont exactement les mêmes. Cliquez sur les 4 boutons en bas, et les effets de commutation sont tous bons :
7. C’est tout pour cette section. Le travail de charpente est-il terminé ? On peut seulement dire que la première étape du framework est terminée. Si nous avons de nombreux collègues développant cette application ensemble, nous pouvons commencer à séparer le travail en parallèle, puis le diviser en 5 parties :
* Achèvement de la barre inférieure
* Achèvement de /chats/index.ui
* Achèvement de /contacts/index.ui
* Achèvement de /discover/index.ui
* Achèvement de /me/index.ui
La condition préalable pour que plusieurs personnes travaillent en parallèle est la gestion des versions de code telles que SVN et GIT. Nous utilisons ici GIT. L'adresse est https://github.com/do-project/Fake-Weixin. chaque section, nous Le service GIT sera soumis. Vous pouvez télécharger la référence de code de ce nœud. Nous joindrons également le code du projet pour cette section dans la pièce jointe.
Dans la section suivante, nous terminons la première sous-tâche, la mise en œuvre de BottomBar.
-------------------------------------------------------------- --- ---------------
Cette section concerne principalement l'achèvement de la mise en œuvre de la barre de navigation inférieure.
0. Analysons d'abord les rendus d'interface et les dessins de conception
L'ensemble de la navigation inférieure est divisé en 4 parties répétées. Chaque partie se compose d'une ImageView, d'une étiquette de titre inférieure et d'une étiquette dans le coin supérieur droit. Cette étiquette peut être implémentée avec une étiquette à coin arrondi. masqué par défaut.
1. La première étape consiste à trouver les ressources d'images correspondantes. Habituellement, ces ressources sont fournies par des artistes. Maintenant, nous imitons WeChat. Le meilleur moyen est de les obtenir à partir du package d'installation natif de WeChat. , mais ouvrez-les WeChat ios, le package d'installation Android, le package d'installation iOS est ipa et le package d'installation Android apk sont tous deux des fichiers compressés, qui peuvent être décompressés pour obtenir des ressources d'image. À l'heure actuelle, je n'ai besoin que des 8 icônes en bas, y compris les icônes en surbrillance sur lesquelles on ne clique pas et celles sur lesquelles on clique. Je mets ces icônes dans le répertoire d'images
.2. Supprimez d'abord les 4 boutons temporaires ajoutés précédemment, puis disposez les nouveaux composants en fonction des données de taille fournies par l'artiste, dont 4 composants do_ImageView, 4 composants Label et 4 Labels dans le coin supérieur droit
Un simple calcul peut montrer que la taille d'ImageView est de 60*60. Voici une petite astuce. Après avoir configuré un ensemble d'ImageView et d'étiquette, sélectionnez 2 composants, puis cliquez avec le bouton droit sur "Copier", puis sur "Coller" trois fois. Vous pouvez également sélectionner plusieurs composants pour différents alignements.
Réglez-le à nouveau et définissez l'image et le texte. Le paramètre d'image consiste à définir l'attribut source d'ImageView. L'étiquette doit définir le texte pour qu'il soit centré, définir l'attribut textAlign au centre, définir la police, définir la couleur d'arrière-plan. , couleur de premier plan, etc., définissez le coin supérieur droit. La visibilité des trois étiquettes dans le coin est fausse. Ajoutez un ALayout au milieu et définissez l'arrière-plan sur gris comme ligne de séparation entre ViewShower et la barre inférieure. Notez ici que l'étiquette circulaire parfaite dans le coin supérieur droit est implémentée en définissant l'attribut border. La bordure est définie sur FF0000FF. ,1,15 pour représenter la ligne de bordure. La couleur est rouge, la largeur est 1 et le rayon du congé est 15 (la largeur et la hauteur de l'étiquette sont toutes deux 30), obtenant ainsi un cercle parfait.
Testez l'effet sur un appareil réel. Les rendus de vrais téléphones iPhone et Android sont les suivants :
3. Il y aura deux problèmes à ce moment-là. Si vous ajoutez un événement de clic à ImageView, l'utilisateur doit cliquer sur l'image pour déclencher le clic, ce qui n'est pas une bonne expérience. Le deuxième problème est que l'image est légèrement déformée sur Android. S'il s'agit d'un iPhone 4 par exemple, le cercle peut devenir une ellipse. Ce problème est dû à la différence de format d'image des différents téléphones mobiles.
La solution est :
* Ajoutez 4 sous-ALayouts de la même taille à l'ALayout où se trouve la barre inférieure, puis placez la vue d'image et l'étiquette sur le sous-ALayout correspondant, puis ajoutez un événement de clic au sous-ALayout afin que le le doigt de l'utilisateur n'a qu'à toucher la même position. Des événements peuvent être déclenchés
* Modifiez l'attribut isStretch des quatre sous-ALayouts ci-dessus en false Pour ce principe, veuillez vous référer au document ALayout exemple de démonstration
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; });
最后我们先看看真机效果,点击加号弹出菜单,点击任何地方都把菜单隐藏。
这一节暂时先到这里,我们先开始拖拽后几个主页面,那几个页面基本完成后再重新回到这一个页面来细琢。