


<ignore_js_op>


<ignore_js_op>
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
var
viewshower = ui(
"viewshower"
);
var
page = sm(
"do_Page"
);
// 為viewshower增加4個子頁面
viewshower.addViews([ {
id :
"chats"
,
// 頁面的標示
}, {
id :
"contacts"
,
}, {
id :
"discover"
,
}, {
id :
"me"
,
} ]);
// 初始化先顯示第一個頁面
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"
);
});
|

-------------------------------------------------------------
這一節主要是完成底部導航欄的實現。
0. 我們首先來分析一下界面效果圖和設計圖
<ignore_js_op>
<ignore_js_op>
整個底部導航分4個重復的部分,每個部分由一個圖片ImageView和底部標題Label,以及右上角標示,這個標示可以用圓角Label來實現,這個標示缺省的時候應該是隱藏的。
1. 第一步我們得找到對應的圖片資源,通常開發這些資源由美工提供,我們現在模仿微信,最好的方式是從微信原生安裝包里獲取,不能直接靠截圖,而是打開微信ios,android安裝包,ios的安裝包是ipa和android的安裝包apk都是一個壓縮文件,解開可以獲取到一些圖片資源。目前我只需要底部8個圖標,包括未點中以及點中的高亮圖標,外面把這些圖標放在image目錄下
<ignore_js_op>
2. 先刪除先前增加的4個臨時按鈕,然后按照美工提供的尺寸數據布局好新的組件,包括4個do_ImageView組件和4個Label組件以及4個右上角的Label
簡單計算可以得出ImageView的大小是60*60。這里有一個小技巧,設置好一組ImageView和Label后選中2個組件,然后右鍵“Copy”,然后再"Paste"三次,還可以選中多個組件各種對齊。
<ignore_js_op>
再細調一下,把圖片和文字設置好,圖片設置就是設置ImageView的source屬性,Label需要設置文字居中,設置textAlign屬性為center,設置字體,設置背景色,前景色等等,設置右上角三個Label的visible為false。中間添加一個ALayout設置背景為灰色,作為ViewShower和Bottom Bar上下的分割線. 這里要注意,右上角的正圓形Label的實現方式就是設置border屬性,border設置為FF0000FF,1,15 表示邊界線的顏色是紅色,寬度1,圓角半徑15(Label的寬高都是30),從而實現正圓。
<ignore_js_op>
在真機上測試一下效果,iPhone和Android手機真機的效果圖如下:
<ignore_js_op><ignore_js_op>
3. 這個時候會有2個問題,如果給ImageView上添加點擊事件的話,用戶必須點中這個圖片才能觸發點擊,這樣體驗不好。第二個問題是圖片在Android上稍微有點變形,如果在比如iPhone4上,可能圓形會變成橢圓,這個問題是由於不同手機寬高比的差異。
要解決的辦法是:
* 把Bottom Bar所在的ALayout增加4個一樣大小的子ALayout,然后把imageview,label都放在對應的子ALayout上,然后給子ALayout增加點擊事件,這樣用戶的手指只要接觸到差不多位置都能觸發事件
* 把上面的4個子ALayout的isStretch屬性改成false,這個原理可以參考文檔 ALayout的示例demo
<ignore_js_op>
4. 修改index.ui.js,添加代碼主要是在底部bottom bar切換按鈕的時候修改所有圖標的顏色和字的前景色。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
var
button1 = ui(
"do_Button_1"
);
var
imageview1 = ui(
"do_ImageView_1"
);
var
label1 = ui(
"do_Label_1"
);
button1.on(
"touch"
,
function
() {
showView(
"chats"
);
});
var
button2 = ui(
"do_Button_2"
);
var
imageview2 = ui(
"do_ImageView_2"
);
var
label2 = ui(
"do_Label_2"
);
button2.on(
"touch"
,
function
() {
showView(
"contacts"
);
});
var
button3 = ui(
"do_Button_3"
);
var
imageview3 = ui(
"do_ImageView_3"
);
var
label3 = ui(
"do_Label_3"
);
button3.on(
"touch"
,
function
() {
showView(
"discover"
);
});
var
button4 = ui(
"do_Button_4"
);
var
imageview4 = ui(
"do_ImageView_4"
);
var
label4 = ui(
"do_Label_4"
);
button4.on(
"touch"
,
function
() {
showView(
"me"
);
});
function
showView(name) {
viewshower.showView(name);
if
(name ==
"chats"
) {
label1.fontColor =
"09BB07FF"
;
}
else
{
label1.fontColor =
"9F9F9FFF"
;
}
if
(name ==
"contacts"
) {
label2.fontColor =
"09BB07FF"
;
}
else
{
label2.fontColor =
"9F9F9FFF"
;
}
if
(name ==
"discover"
) {
label3.fontColor =
"09BB07FF"
;
}
else
{
label3.fontColor =
"9F9F9FFF"
;
}
if
(name ==
"me"
) {
label4.fontColor =
"09BB07FF"
;
}
else
{
label4.fontColor =
"9F9F9FFF"
;
}
}
|
到此為止,底部導航欄基本實現完成,這一節比較簡單,主要是一些細致的ui拖拽調整。我們用調試版本看一下Android,iOS的效果都非常不錯。源代碼下載
我們開始實現ViewShower的第一頁主體內容.
---------------------------------------------------------------------------------------
接上一節 底部導航 ,我們這一節主要是完成微信4個主頁面的第一個頁面“微信”頁面,這一節內容比較多,我們分多個跟帖來完成
0 老規矩,先分析一下UI,由三個大部分組成,系統狀態欄,工具欄和微信聊天記錄列表。
<ignore_js_op>
1. 系統狀態欄高度40,背景黑色,我們注意到微信的首頁4個子頁面都有這個系統狀態欄,這樣我們需要做一個整體框架的調整,沒有必要為4個子頁面都添加一個狀態欄,只需要在ViewShower上添加一個就可以,對應的ViewShower和子頁面的高度都變成1180.
<ignore_js_op>
對應設計器里index.ui, chats/index.ui, contacts/index.ui, discover/index.ui, me/index.ui 都要作相應的height,y屬性值的變化。效果如下:
<ignore_js_op>
2. 我們回到chats/index.ui ,先增加工具導航欄(高度80)和里面的標題和工具按鈕,這個需要增加加號的資源文件,因為這個文件是chats頁面專有的,所以存在image/chats/bar_add.png下。
我們看看真機效果,我們注意到頂部多出一塊黑色區域,這是設計器里的增加的狀態欄,因為這個頁面是從系統狀態欄開始往下繪制的,所以會把設計器里這一部分多出來,
<ignore_js_op>
要解決的方法是修改app.js,openPage增加一個statusBarState參數(API文檔),設置為transparent表示頁面從屏幕最頂端開始繪制。
1
2
3
4
5
6
7
8
9
|
var d
1
=
require
(
"deviceone"
)
;
var app
=
d
1.
sm
(
"do_App"
)
;
app.
on
(
"loaded"
,
function
(
)
{
this.openPage
(
{
statusBarState
:
"transparent"
}
)
;
}
)
;
|
再來看看真機效果圖:
<ignore_js_op>
3. 主體部分是一個do_ListView,接下來設置ListView的cell和數據。
ListView的cell指列表框的每一行,比如ListView有100行數據,實際上可見的屏幕永遠只能看到8,9行左右,所以我們手勢上下滑動的時候並沒有創建100行,而是重復使用這8,9行,只不過替換里面的數據而已。我們稱之為行模板,在DeviceOne里這種模板也是一個ui文件,比如這里我們在chats子目錄下新建一個chat_cell.ui,這個ui基本界面如下:
<ignore_js_op>
按照美工的設計尺寸我們來拖拽UI
<ignore_js_op>
這里同樣需要考慮純圓變形問題,需要設置好文字大小,前景色等屬性,大家可以看到里面有多個do_Label,do_ImageView組件,由於模板ui是靠后期綁定數據的,所以在設計階段都是空白的。
<ignore_js_op>
接下來我們需要設計chat_cell.ui對應的數據,通常為了用戶體驗,需要盡可能的減少網絡交互,頁面打開的時候通常先讀取本地的數據文件,把界面顯示出來,然后再考慮是否要進行網絡連接來獲取最新數據,所以App開發需要仔細考慮數據的本地化讀寫和數據的時效性的平衡。
DeviceOne的傳遞數據基本上都是標准的JSON格式。如下圖,chat_cell.ui里的組件屬性和JSON數據結構對應的關系
<ignore_js_op>
對應的映射關系的代碼在chat_cell.ui.js如下,我們可以看到映射關系的左邊是組件id.組件屬性名,右邊是數據JSON的key名稱:
01
02
03
04
05
06
07
08
09
10
11
12
|
//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
<ignore_js_op>
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里添加綁定數據的代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
//related to index.ui
var
storage = sm(
"do_Storage"
);
var
listdata = mm(
"do_ListData"
);
var
listview = ui(
"listview"
);
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
});
|
我們在真機上看看效果
<ignore_js_op>
在運行中有幾個細節:
* 上下滑動的時候,圖片不斷的刷新,原因是我們的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里添加代碼
01
02
03
04
05
06
07
08
09
10
|
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
{
}
});
|
其中chat_add_menu.ui 是彈出的菜單對應的ui文件,這個ui文件的根節點大小和chat/index.ui一樣,這樣確保我點擊任何空白處都可以關閉這個菜單(實際上是隱藏這個菜單),我們在這個ui文件里把對應的布局都拖拽好,其中需要添加4個資源png文件。
這里有個小技巧,頂部的三角形標記只能通過一個ImageView加載一個三角形圖標來實現。
<ignore_js_op>
我們再給chat_add_menu的根節點添加點擊事件,點擊的時候把自己隱藏,在chat_add_menu.js
1
2
3
4
|
var
root = ui(
"$"
);
root.on(
"touch"
,
function
(){
root.visible =
false
;
});
|
最后我們先看看真機效果,點擊加號彈出菜單,點擊任何地方都把菜單隱藏。
<ignore_js_op>
這一節暫時先到這里,我們先開始拖拽后幾個主頁面,那幾個頁面基本完成后再重新回到這一個頁面來細琢。
再附上最新代碼zip,源代碼也提交到GIT服務上了https://github.com/do-project/Fake-Weixin