Android开发——布局性能优化的一些技巧(一)
0. 前言
上一篇我們分析了為什么LinearLayout會比RelativeLayout性能更高,意義在于分析了這兩種布局的實現(xiàn)源碼,算是對一個小結(jié)論的證明過程,但是對布局性能的優(yōu)化效果,對這兩種布局的選擇遠不如減少布局層級、避免過分繪制、按需加載等效果明顯。所以本篇將著重總結(jié)Android布局性能優(yōu)化的各種技巧。本文原創(chuàng),轉(zhuǎn)載請注明出處:http://blog.csdn.net/seu_calvin/article/details/52923827。
?
1.? ?<include/>
<include>標簽可以在一個布局中引入另外一個布局,通常適合于界面布局復(fù)雜、不同界面有共用布局的APP中,比如頂部布局、側(cè)邊欄布局、底部Tab欄布局、ListView的item布局等,將這些公共布局抽取出來再通過<include>標簽引用,既可以使代碼結(jié)構(gòu)清晰,又可統(tǒng)一修改使用。
比如先寫一個公共的標題欄標題欄title_bar.xml(這里就不具體實現(xiàn)了),并在我們的主xml文件里調(diào)用<include>來使用這個公共布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><include layout="@layout/title_bar"/>
</RelativeLayout>
當然include也可以使用layout屬性來設(shè)置布局文件的寬高和位置,但需要注意的是,必須要復(fù)寫android:layout_width和android:layout_height屬性才能使用其它屬性(如:android:layout_grivity、android:layout_align...、android:id等),這樣可以避免include引用中的子組件屬性影響到include的布局效果。
比如下面這個例子給我們include進的組件設(shè)置高度和位置:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_margin="10dp"><include layout="@layout/title_bar" /><includeandroid:layout_width="match_parent"android:layout_height="60dp"android:layout_alignParentBottom="true"layout="@layout/ title_bar"/></RelativeLayout>
</RelativeLayout>
2.? ?減少嵌套
這個問題我們在LinearLayout和RelativeLayout的性能對對比中已經(jīng)解釋過了,在不響應(yīng)層級深度的情況下,使用Linearlayout而不是RelativeLayout。為開發(fā)者默認新建RelativeLayout是希望開發(fā)者能采用盡量少的View層級,因為很多效果是需要多層LinearLayout的嵌套,這必然不如一層的RelativeLayout性能更好。為了確保文章的實時更新,實時修改可能出錯的地方,請確保這篇是原文,而不是無腦轉(zhuǎn)載來的“原創(chuàng)文”,原文鏈接為SEU_Calvin的博客。
3.? <merge/>
<merge/>標簽通過減少View樹的層級來優(yōu)化Android的布局。先來用個例子演示一下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="merge標簽使用" />
</RelativeLayout>
運行后使用“DDMS -> DumpView Hierarchy for UI Automator”工具,截圖如下:
?
最下面兩層RelativeLayout與TextView就是布局中的內(nèi)容,上面的FrameLayout是ActivitysetContentView添加的頂層視圖。下面我們將上述布局代碼中的RelativeLayout修改為merge標簽再查看層級結(jié)構(gòu)如下:
?
(1)從結(jié)果來看,FrameLayout下面直接就是TextView,與之前的相比少了一層RelativeLayout但效果相同。這個例子中TextView不需要指定任何針對父視圖的布局屬性,只用于添加到父視圖上并顯示,這種情況就可以使用<merge/>標簽優(yōu)化。但是我們一般很少遇到這種情況。為了確保文章的實時更新,實時修改可能出錯的地方,請確保這篇是原文,而不是無腦轉(zhuǎn)載來的“原創(chuàng)文”,原文鏈接為SEU_Calvin的博客。
(2)更多的<merge/>標簽使用情景是在LinearLayout里面嵌入一個布局(比如使用了include),而恰恰這個布局的根節(jié)點也是LinearLayout,這樣就多了一層沒有用的嵌套,增加了View深度,這個時候如果我們使用merge根標簽就修飾被嵌入的布局的根標簽就可以避免此問題。
4.? ?ViewStub
一個最最最可能使用到的場景就是請求網(wǎng)絡(luò)加載列表,如果網(wǎng)絡(luò)異常或者加載失敗,我們可以顯示一個用于提示用戶的View,上面可以點擊重新加載。當網(wǎng)絡(luò)正常時,我們就沒有理由顯示這個提示View。但是如果我們通過代碼邏輯這種方式實現(xiàn)動態(tài)更改這個View的可見性(GONE或者VISIBLE),在Inflate布局的時候View仍然會被Inflate,也就是說仍然會創(chuàng)建對象,會被實例化且耗費內(nèi)存資源。
為了解決這個性能問題,ViewStub應(yīng)運而生,ViewStub是一個輕量級的View,看不見、不占布局位置、占用資源非常小。當ViewStub被設(shè)置為可見或調(diào)用了ViewStub.inflate()的時候,ViewStub所指向的布局才會被Inflate和實例化,然后ViewStub的布局屬性都會傳給它所指向的布局,ViewStub控件本身就不存在了(ViewStub對象會被置空),取而代之的是被inflate的Layout,因此它也被稱做惰性控件。為了確保文章的實時更新,實時修改可能出錯的地方,請確保這篇是原文,而不是無腦轉(zhuǎn)載來的“原創(chuàng)文”,原文鏈接為SEU_Calvin的博客。
綜上ViewStub的原理,就可以使用它來方便的在運行時,決定要不要顯示某個布局。使用實例如下:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" >……<ViewStubandroid:layout_gravity="center"android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/hint_fail_view"android:inflatedId="@+id/hint_fail_view"android:layout="@layout/fail_view"/>
</merge>
android:layout="@layout/fail_view"指向頁面加載失敗的布局文件,里面包含一個id為tv的TextView。
當出現(xiàn)網(wǎng)絡(luò)異常時,我們在代碼里這樣使用ViewStub:
private View hintFailView;
if (網(wǎng)絡(luò)異常) {if (hintFailView == null) {ViewStub viewStub = (ViewStub)this.findViewById(R.id.hint_fail_view);hintFailView = viewStub.inflate(); //注意這里TextView textView = (TextView) hintFailView.findViewById(R.id.tv);textView.setText("網(wǎng)絡(luò)異常");}hintFailView.setVisibility(View.VISIBLE);
}else{//網(wǎng)絡(luò)正常if (hintFailView!= null) {hintFailView.setVisibility(View.GONE);}//業(yè)務(wù)邏輯
}
5. ?避免OverDraw
一個簡單的過度繪制例子是父控件和其上的子控件都設(shè)置了Background,那么人們是看不到被子控件所覆蓋的那部分父控件背景的,這就造成了OverDraw,我們可以通過設(shè)置-開發(fā)者選項-顯示GPU過度繪制來查看應(yīng)用是否存在嚴重的OverDraw問題。
如果你發(fā)現(xiàn)應(yīng)用中有些色塊為紅色,那么你可要去優(yōu)化它了,你需要去根據(jù)顏色提示去找到你過度繪制的地方,需要注意的是在我們有自己的背景色的情況下,頂層View的背景色我們可以置空來優(yōu)化。
setContentView(R.layout.activity_overdraw_01);
getWindow().setBackgroundDrawable(null);除了去除不必要的背景色,還有一個防止OverDraw的方法,那就是使用畫布的clipRect()方法來去除自定義View中不必要的重疊繪制。
在看clipRect方法之前先看看如果將res目錄下的圖片文件轉(zhuǎn)換為bitmap對象,這里總結(jié)了兩種方法,大家可以參考使用:
InputStream is = this.getContext().getResources().openRawResource(R.drawable.icon);
Bitmap mBitmap = BitmapFactory.decodeStream(is);
//或者使用BitmapDrawable
Bitmap mBitmap = new BitmapDrawable(is).getBitmap();clipRect方法可以截取畫布中的一個矩形區(qū)域,在此區(qū)域外的將不再繪制顯示。實例如下:
/*
*author SEU_Calvin in 2016/10
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = mBmp.getWidth();
int height = mBmp.getHeight();canvas.save();
mPaint.setColor(Color.CYAN);
//先在屏幕的0,0處繪制一個與我們Bitmap寬高相等的藍色矩形
canvas.drawRect(0, 0, width, height, mPaint);
canvas.restore();canvas.save();
//裁剪畫布,左上角為0,0 右下角為指定寬高的2倍和1.5倍
canvas.clipRect(0, 0, width*2, height*3/2);
//以width,height為左上角繪制我們的Bitmap,由于圖片的下半部分在裁剪畫布之外所以不顯示
canvas.drawBitmap(mBmp, width, height, mPaint);
canvas.restore();
}結(jié)果如下所示,工作過程已經(jīng)在代碼的注釋里寫的很清楚了。
6.? 其他小技巧
為了控制篇幅,將一些看了讓人感到驚艷的布局優(yōu)化小技巧總結(jié)分享到了布局性能優(yōu)化的一些技巧(二),希望可以幫助到你~
最后希望各位看官老爺們多點贊支持~
轉(zhuǎn)載于:https://www.cnblogs.com/qitian1/p/6461464.html
總結(jié)
以上是生活随笔為你收集整理的Android开发——布局性能优化的一些技巧(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现在的猪苗多少钱一头
- 下一篇: SQL Server Lock Esca