android提高UI的流暢度
Android中所有的界面繪制工作都是在UI線(xiàn)程中進(jìn)行的婴洼,提高UI流暢度的最核心根本在于釋放UI線(xiàn)程。即:不在主線(xiàn)程中做耗時(shí)的操作。
很多人都知道彰亥,耗時(shí)的操作要放到子線(xiàn)程中去做惶室,比如訪(fǎng)問(wèn)網(wǎng)絡(luò)减江,比如讀寫(xiě)sd卡凌那。像這類(lèi)操作大家都會(huì)很自然的想到使用子線(xiàn)程來(lái)完成耗時(shí)的操作阻问,等操作結(jié)束之后拭抬,再通過(guò)Handler通知主線(xiàn)程進(jìn)行界面的更新部默。這是非常正確的方法。但是有一類(lèi)方法造虎,它必須得運(yùn)行在在UI線(xiàn)程中傅蹂,就是布局文件的加載。如果這類(lèi)方法花的時(shí)間太多了累奈,也是會(huì)對(duì)流暢度產(chǎn)生很大的影響贬派。今天我們就來(lái)講講布局文件的優(yōu)化。
加載布局文件澎媒,是必須在UI線(xiàn)程中完成的搞乏。我們通常是在onCreate方法中直調(diào)用setContentView,傳入一個(gè)布局文件的id值戒努,或者是通過(guò)LayoutInflater來(lái)將某一個(gè)布局文件轉(zhuǎn)化成View對(duì)象请敦。其實(shí)這兩種方式的本質(zhì)都是一樣的镐躲,都是將xml文件轉(zhuǎn)換成View對(duì)象。
我們現(xiàn)在要做的事侍筛,就是如何讓xml文件轉(zhuǎn)換成View對(duì)象所花的時(shí)間最少萤皂。做到了這點(diǎn),就可以很大程度的提高UI的流暢度匣椰。
1裆熙、優(yōu)化布局, 減少布局的嵌套層級(jí)
** a、使用drawableXXX屬性**
如果要實(shí)現(xiàn)這樣一個(gè)效果禽笑,布局文件可以這樣寫(xiě)
<LinearLayout orientation="vertical">
<ImageView/>
<TextView/>
</LinearLayout>
優(yōu)化后:
<TextView drawableBottom="@drawable/contact"/>
直接一個(gè)TextView就搞定入录,不需要在外面多一層LinearLayout
** b、多使用RelativeLayout佳镜,少使用LinearLayout**
如果這樣的布局使用LinearLayout來(lái)做的話(huà)僚稿,那么會(huì)是以下這個(gè)效果
<LinearLayout orientation="horizontal">
<ImageView/>
<LinearLayout orientation="vertical">
<LinearLayout orientation="horizontal">
<TextView/>
<TextView/>
</LinearLayout>
<TextView/>
<LinearLayout orientation="horizontal">
<TextView/>
<TextView/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
這樣就莫名其妙的多出了好多個(gè)LinearLayout.
這樣過(guò)多的LinearLayout嵌套LinearLayout,會(huì)造成UI加載的非常慢蟀伸。這樣的布局完全可以使用一個(gè)RelativeLayout來(lái)完成蚀同,里面的子元素根據(jù)相對(duì)于其他控件的位置即可確定。
嵌套使用LinearLayout很容易會(huì)導(dǎo)致視圖層級(jí)過(guò)深啊掏。如果使用layout_weight這個(gè)參數(shù)不斷的進(jìn)行嵌套蠢络,有可能會(huì)讓各個(gè)子View付出計(jì)算兩次的昂貴代價(jià)
優(yōu)化后代碼:
<RelativeLayout>
<Image id=avatar layout_alignParentLeft=true />
<TextView id=name layout_alignParentTop=true layout_toRightOf=@id/avatar />
<TextView id=location layout_alignParentTop=true layout_toRightOf=@id/name />
<TextView id=desc layout_below=@id/location layout_toRightOf=@id/avatar />
....
</RelativeLayout>
** c、使用merge標(biāo)簽**
使用merge標(biāo)簽也是能夠減少一些布局的層次脖律。merge標(biāo)簽經(jīng)常會(huì)和include標(biāo)簽相聯(lián)系谢肾。
那么什么時(shí)候使用merge標(biāo)簽?zāi)兀肯旅媾e例子說(shuō)明小泉。
<LinearLayout orientation="vertical">
......
<include layout="@layout/include_view_layout"/>
......
</LinearLayout>
而include_view_layout.xml 的代碼如下:
<LinearLayout orientation="vertical">
<Button/>
<Button/>
</LinearLayout>
我們看到Button的父控件是LinearLayout芦疏,而include的父控件也是LinearLayout,這樣子的布局最終的結(jié)果是
<LinearLayout orientation="vertical">
<LinearLayout orientation="vertical">
<Button/>
<Button/>
</LinearLayout>
</LinearLayout>
紅色部分的LinearLayout完全是多余微姊,于是這時(shí)候酸茴,我們就可以在include_view_layout.xml文件中使用merge標(biāo)簽了。如下:
<merge>
<Button/>
<Button/>
</merge>
這樣兢交,在加載這個(gè)include標(biāo)簽的時(shí)候薪捍,系統(tǒng)會(huì)忽略merge標(biāo)簽,直接將merge標(biāo)簽內(nèi)的元素添加到外層的LinearLayout去了配喳,達(dá)到減少層級(jí)的效果酪穿。
2、延遲加載
在開(kāi)發(fā)某些功能時(shí)候晴裹,有時(shí)候需要?jiǎng)討B(tài)的根據(jù)條件來(lái)判斷顯示哪一個(gè)View被济,不顯示哪一個(gè)View。一般的做法是將所有的View都寫(xiě)在布局文件中去涧团,然后根據(jù)條件再來(lái)設(shè)置他們的可見(jiàn)度Visibility為GONE或者VISIBLE只磷。這種做法邏輯簡(jiǎn)單经磅,便于理解。但是缺點(diǎn)就是那些不顯示出來(lái)的View也占用了內(nèi)存钮追,消耗了inflate的時(shí)間预厌。因?yàn)橐粋€(gè)View,不論他的Visibility的值是什么元媚,它都會(huì)被inflate出來(lái)轧叽,并占用內(nèi)存空間。這時(shí)候其實(shí)就可以用到延遲加載的控件ViewStub了惠毁。
ViewStub是一個(gè)非常輕量級(jí)的控件犹芹,它占的資源非常小。注意鞠绰,是ViewStub這個(gè)對(duì)象所占的資源小,但是你可以為ViewStub指定一個(gè)布局文件飒焦,這個(gè)布局文件被inflate的時(shí)候占的空間有可能很大蜈膨。默認(rèn)的情況下,ViewStub的所指定的布局文件是不被inflate的牺荠,只有當(dāng)你調(diào)用了ViewStub的inflate方法時(shí)翁巍,ViewStub所指向的布局文件才會(huì)被inflate。所以ViewStub是一個(gè)延遲加載的控件休雌。
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<ViewStub
android:id="@+id/viewstub1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/viewstub_layout1"/>
<ViewStub
android:id="@+id/viewstub2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/viewstub_layout2"/>
</LinearLayout>
在java代碼中使用
ViewStub stub1 = (ViewStub) findViewById(R.id.viewstub1);
ViewStub stub2 = (ViewStub) findViewById(R.id.viewstub2);
if(isLogin()) {
stub1.inflate();
} else {
stub2.inflate();
}
這樣就不會(huì)有浪費(fèi)資源空間去加載沒(méi)必要的控件了灶壶。
3、減少inflate的次數(shù)
這個(gè)的典型例子就是ListView的優(yōu)化杈曲。我們說(shuō)ListView的優(yōu)化驰凛,實(shí)際上說(shuō)的就是Adapter中g(shù)etView方法的優(yōu)化,我們來(lái)看一個(gè)沒(méi)有優(yōu)化過(guò)的getView方法担扑。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyItem product = list.get(position);
convertView = getLayoutInflater()
.inflate(R.layout.item_record, null);
TextView tvDate = (TextView) convertView
.findViewById(R.id.tvDate);
TextView tvYongtu = (TextView) convertView
.findViewById(R.id.tvYongtu);
TextView tvMoney = (TextView) convertView
.findViewById(R.id.tvMoney);
tvDate.setText(product.detaildate);
tvYongtu.setText(product.auditmessage);
tvMoney.setText(product.detailmoney);
return convertView;
}
我們知道恰响,ListView中的每一個(gè)Item被顯示出來(lái)都要調(diào)用getView方法,這個(gè)Item如果滑出屏幕涌献,又滑回來(lái)胚宦,重新顯示在界面上的時(shí)候,又會(huì)再次調(diào)用getView方法燕垃。所以getView是不斷的被調(diào)用的枢劝。而上面的代碼,只要調(diào)用了getView方法卜壕,就一定會(huì)去inflate一個(gè)布局文件您旁,真簡(jiǎn)直就是不敢想象的非常耗時(shí)的操作。于是印叁,利用系統(tǒng)給我們的緩存convertView進(jìn)行判斷被冒,可以大大減少inflate的次數(shù)军掂。其實(shí),findViewById也是一個(gè)很耗時(shí)的操作昨悼,我們可以利用ViewHolder來(lái)減少findViewById的次數(shù)蝗锥。優(yōu)化后的代碼如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyItem product = list.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = getLayoutInflater().inflate(
R.layout.item_record, null);
holder = new ViewHolder();
holder.tvDate = (TextView) convertView
.findViewById(R.id.tvDate);
holder.tvYongtu = (TextView) convertView
.findViewById(R.id.tvYongtu);
holder.tvMoney = (TextView) convertView
.findViewById(R.id.tvMoney);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvDate.setText(product.detaildate);
holder.tvYongtu.setText(product.auditmessage);
holder.tvMoney.setText(product.detailmoney);
return convertView;
}
static class ViewHolder {
TextView tvDate;
TextView tvYongtu;
TextView tvMoney;
}