ホームページ > 記事 > ウェブフロントエンド > DeviceOneを使ったJavaScriptの実践開発(3)WeChatアプリケーションを真似てみる_JavaScriptスキル
これは一連のドキュメントです。長期的な目標は、DeviceOne を使用して、現在広く使用されている高品質のモバイル アプリケーションを開発することであり、単に単純なままではなく、これらのアプリケーションのあらゆる機能と詳細を最大限に活用することです。 UI模倣とデモ段階ですが、基本的には使える実用的なアプリです。
実装プロセス中には、現在コンポーネントのサポートが不足しており、実装できない機能も多く発生するでしょう。また、さまざまなモバイル開発で遭遇する一般的な技術的な問題も発生します。段階的な操作と問題解決により、開発者は DeviceOne を通じて実際のアプリを開発する方法を直感的に理解できるようになり、モバイル開発自体の多くの技術的な詳細も理解できるため、アプリ開発者は多くの回り道を避けることができます。
この文書は主に WeChat の模倣を紹介します。
最初の部分はフレームワークの構築です
UE と UI のデザイン。通常、アプリ開発にはプロダクト スタッフによる UE デザインと、アート スタッフによる UI デザインが必要です。この 2 つのステップが完了して初めて開始できます。現在は既存の WeChat を模倣しており、これらの手順は省略できます。下の図はアーティストによって提供されたメイン インターフェイス UI デザインであり、内部の要素のサイズがマークされています。
1. 新しいプロジェクト: シンプルなテンプレートを選択しました。開発プロセスをより詳細に説明できるため、空のテンプレートを選択しました。実際、このプロジェクトでは、ViewShower テンプレートを使用したマルチ ビューを選択する方が適切です。
2. メインインターフェイスの簡単な分析 メインインターフェイス全体のサイズは、750*1334 で、上部と下部の 2 つの部分に分かれています。バーナビゲーションバーで、上部は4つの独立したインターフェースです。 残りの3つのインターフェースは常にメモリ内にあり、下部のナビゲーションで切り替えることができます。 これは、do_ViewShowerを本体として使用するのに適しています。以下に示すようにフレームを編集します:
このデザインに対応して、新しく作成したプロジェクトで自動生成されたボタンを削除し、do_ALayout コンポーネントと do_ViewShower コンポーネントを追加し、それらの高さ、幅、x、y 座標を設定します。
3. 次に、ViewShower に 4 ページを追加します。ここで強調したいのは、実際のアプリでは、名前を付けるときに明確で読みやすいものにする必要があります。複数レベルのサブディレクトリを作成し、それらをすべて混合しないでください。完全な中国語のスペルを使用することはできません。ここでは、chats、contacts、discover、me の 4 つのサブディレクトリを追加します。各サブディレクトリを右クリックして、[新規]--[その他]--[DeviceOne]--[UI ファイル] を選択し、4 つのindex.ui を作成すると、対応する Index.ui.js が自動的に生成されます。 。
ここで、これら 4 つの ui ファイルに対応するルート ノード ALayout の高さを 1220 に設定するように注意してください。これらの 4 つの ui ファイルはすべてメイン インターフェイスの ViewShower のサブビューであり、ViewShower のサイズを超えてはいけないためです。
さらに、各 UI にラベルを追加して中国語の名前をマークします。後でデバッグして、実際の効果をより明確に確認できます。
4. ボトムバーに 4 つのボタンを追加し、対応する js コードを追加して ViewShower のページ切り替え機能を実装します。ここでヒントを紹介します。以下に示すように、2 つ以上のコンポーネントを選択すると、さまざまな位置合わせ機能を使用できます。
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 つのボタンをクリックすると、切り替え効果がすべて良好になります:
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、android インストール パッケージ、ios インストール パッケージは ipa、android インストール パッケージ apk はどちらも圧縮ファイルであり、解凍していくつかの画像リソースを取得できます。現時点では、クリックされていない強調表示されたアイコンとクリックされたアイコンを含む下部の 8 つのアイコンのみが必要で、これらのアイコンを画像ディレクトリ
に配置します。2. まず、前に追加した 4 つの一時ボタンを削除し、アーティストが提供したサイズ データに従って新しいコンポーネント (4 つの do_ImageView コンポーネント、4 つの Label コンポーネント、右上隅の 4 つのラベルを含む) をレイアウトします。
簡単な計算では、ImageView のサイズは 60*60 であることがわかります。 ImageView と Label のセットを設定した後、2 つのコンポーネントを選択し、右クリックして「コピー」、「貼り付け」を 3 回クリックします。また、複数のコンポーネントを選択してさまざまな位置に配置することもできます。
再度調整して画像とテキストを設定します。画像の設定では、ImageView のソース属性を設定し、textAlign 属性を中央に設定し、フォントを設定し、背景色を設定する必要があります。 、前景色など、右上を設定します。隅にある 3 つのラベルの可視性は false です。中央に ALayout を追加し、ViewShower と Bottom Bar の間の境界線として背景を灰色に設定します。ここでは、境界線属性を FF0000FF に設定することで、右上隅の真円のラベルが実装されていることに注意してください。 ,1,15 は境界線を表し、色は赤、幅は 1、フィレット半径は 15 (ラベルの幅と高さは両方とも 30) で、完全な円になります。
実際のデバイスで効果をテストします。実際の iPhone および Android スマートフォンのレンダリングは次のとおりです。
e09233e72a88913c4fbff7920f86497b
3. 現時点では、クリック イベントを ImageView に追加する場合、ユーザーはクリックをトリガーするために画像をクリックする必要があり、これは良いエクスペリエンスではありません。 2 番目の問題は、Android では、たとえば、円が楕円になるなど、画像がわずかに変形することです。これは、携帯電話のアスペクト比の違いが原因です。
解決策は次のとおりです:
* ボトムバーが配置されている ALayout に同じサイズの 4 つのサブ ALayout を追加し、対応するサブ 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; });
最后我们先看看真机效果,点击加号弹出菜单,点击任何地方都把菜单隐藏。
这一节暂时先到这里,我们先开始拖拽后几个主页面,那几个页面基本完成后再重新回到这一个页面来细琢。