<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
