性能优化--布局优化技巧
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1.重用布局文件
1.)include標(biāo)簽
首先用得最多的應(yīng)該是include,按照官方的意思,include就是為了解決重復(fù)定義相同布局的問題。使用起來很方便,我這里就不舉例子。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_title_parent_id" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <include android:id="@+id/my_title_ly" layout="@layout/titlebar" /> </LinearLayout>注意事項:
?1.如果你只想單獨(dú)修改某個布局文件的titleBar而其他布局文件的titleBar不受影響的話,我們可以使用覆寫<include>屬性的方式
在<include>標(biāo)簽當(dāng)中,我們是可以覆寫所有l(wèi)ayout屬性的,即include中指定的layout屬性將會覆蓋掉titlebar中根視圖指定的layout屬性。因此,這里我們希望將titlebar的高度設(shè)置成wrap_content,就可以這樣寫:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_title_parent_id" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <include android:id="@+id/my_title_ly" android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/titlebar" /> </LinearLayout>而非layout屬性則無法在<include>標(biāo)簽當(dāng)中進(jìn)行覆寫。另外需要注意的是,如果我們想要在<include>標(biāo)簽當(dāng)中覆寫layout屬性,必須要將layout_width和layout_height這兩個屬性也進(jìn)行覆寫,否則覆寫效果將不會生效。
2.使用include最常見的問題就是findViewById查找不到目標(biāo)控件,這個問題出現(xiàn)的前提是在include時設(shè)置了id,而在findViewById時卻用了被include進(jìn)來的布局的根元素id。例如上述例子中,include時設(shè)置了該布局的id為my_title_ly,而my_title_layout.xml中的根視圖的id為my_title_parent_id。此時如果通過findViewById來找my_title_parent_id這個控件,然后再查找my_title_parent_id下的子控件則會拋出空指針。代碼如下 :
View titleView = findViewById(R.id.my_title_parent_id) ; // 此時 titleView 為空,找不到。此時空指針TextView titleTextView = (TextView)titleView.findViewById(R.id.title_tv) ; titleTextView.setText("new Title");正確的方式:
// 使用include時設(shè)置的id,即R.id.my_title_ly View titleView = findViewById(R.id.my_title_ly) ; // 通過titleView找子控件 TextView titleTextView = (TextView)titleView.findViewById(R.id.title_tv) ; titleTextView.setText("new Title");或者更簡單的直接查找它的子控件:
TextView titleTextView = (TextView)findViewById(R.id.title_tv) ; titleTextView.setText("new Title");通過看源碼出現(xiàn)這樣的原因是:解析過程中會首先判斷include標(biāo)簽的id如果不是View.NO_ID的話會把該id設(shè)置給被引入的布局根元素的id,即此時在我們的例子中被引入的id為my_title_parent_id的根元素RelativeLayout的id被設(shè)置成了include標(biāo)簽中的id,即RelativeLayout的id被動態(tài)修改成了”my_title_ly”。因此此時我們再通過“my_title_parent_id”這個id來查找根元素就會找不到了!?
所以結(jié)論就是: 如果include中設(shè)置了id,那么就通過include的id來查找被include布局根元素的View;如果include中沒有設(shè)置Id, 而被include的布局的根元素設(shè)置了id,那么通過該根元素的id來查找該view即可。拿到根元素后查找其子控件都是一樣的。
2.)Merge標(biāo)簽
<merge>標(biāo)簽是作為<include>標(biāo)簽的一種輔助擴(kuò)展來使用的,它的主要作用是為了防止在引用布局文件時產(chǎn)生多余的布局嵌套。大家都知道,Android去解析和展示一個布局是需要消耗時間的,布局嵌套的越多,那么解析起來就越耗時,性能也就越差,因此我們在編寫布局文件時應(yīng)該讓嵌套的層數(shù)越少越好。
比如我們寫一個公共的確定和取消按鈕ok_cancel_layout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:id="@+id/ok" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:text="OK" /> <Button android:id="@+id/cancel" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="10dp" android:text="Cancel" /> </LinearLayout>然后再另一個界面main.xml去引用
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/edit" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="10dp" android:hint="Edit something here" /> <include layout="@layout/ok_cancel_layout"/> </LinearLayout>目前main.xml這個界面當(dāng)中其實已經(jīng)存在著多余的布局嵌套了!感覺還沒寫幾行代碼呢,怎么這就已經(jīng)有多余的布局嵌套了?不信的話我們可以通過View Hierarchy工具來查看一下,如下圖所示:
相信大家已經(jīng)可以看出來了吧,這個內(nèi)部的LinearLayout就是一個多余的布局嵌套,實際上并不需要這樣一層,讓兩個按鈕直接包含在外部的LinearLayout當(dāng)中就可以了。而這個多余的布局嵌套其實就是由于布局引入所導(dǎo)致的,那么應(yīng)該怎樣優(yōu)化掉這個問題呢?當(dāng)然就是使用<merge>標(biāo)簽來完成了,修改ok_cancel_layout.xml中的代碼,如下所示:
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:id="@+id/ok" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:text="OK" /> <Button android:id="@+id/cancel" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="10dp" android:text="Cancel" /> </merge>我們在通過View Hierarchy工具來查看一下,如下圖所示:
可以看到,這里我們將ok_cancel_layout最外層的LinearLayout布局刪除掉,換用了<merge>標(biāo)簽,這就表示當(dāng)有任何一個地方去include這個布局時,會將<merge>標(biāo)簽內(nèi)包含的內(nèi)容直接填充到include的位置,不會再添加任何額外的布局結(jié)構(gòu).
讓我意想不到的是Merge竟然是個activity,并且還動態(tài)的加載了LinearLayout對象.
/** * Exercise <merge /> tag in XML files. */ public class Merge extends Activity { private LinearLayout mLayout; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); mLayout = new LinearLayout(this); mLayout.setOrientation(LinearLayout.VERTICAL); LayoutInflater.from(this).inflate(R.layout.merge_tag, mLayout); setContentView(mLayout); } public ViewGroup getLayout() { return mLayout; } }通過看源碼發(fā)現(xiàn):在解析的過程中,如果是merge標(biāo)簽,那么第一步:首先獲取merge標(biāo)簽的parent ,第二步:獲取布局參數(shù) ,第三部:遞歸解析每個子元素 ,第四步:將子元素直接添加到merge標(biāo)簽的parent view中 ,這樣就保證了不會引入額外的層級。
3.)ViewStub標(biāo)簽
根據(jù)官方的解釋大致可翻譯成:其實ViewStub就是一個寬高都為0的一個View,它默認(rèn)是不可見的,只有通過調(diào)用setVisibility函數(shù)或者Inflate函數(shù)才會將其要裝載的目標(biāo)布局給加載出來,從而達(dá)到延遲加載的效果,這個要被加載的布局通過android:layout屬性來設(shè)置。
有的時候我們會遇到這樣的場景,就是某個布局當(dāng)中的元素非常多,但并不是所有元素都一起顯示出來的,而是普通情況下只顯示部分常用的元素,而那些不常用的元素只有在用戶進(jìn)行特定操作的情況下才會顯示出來。
這里舉個大家都非常熟悉的例子,我們在添加聯(lián)系人的時候其實可以編輯的字段真的非常多,姓名、電話、email、傳真、住址、昵稱等等等等,但其實基本上大家最常用的就是填一個姓名,填一個電話而已。那么將這么多繁雜的字段都一起顯示在界面上其實并不是一種很好的做法,因為大多數(shù)人都是用不到這些字段的。比較聰明的做法就是把最常用的姓名和電話顯示在界面上,然后給用戶提供一個添加更多字段的選項,當(dāng)用戶真的有需要去添加其它信息的時候,我們才將另外的元素顯示到界面上。
說到實現(xiàn)這樣一個功能,我相信大多數(shù)人的第一反應(yīng)就是將不常用的元素使用INVISIBLE或者GONE進(jìn)行隱藏,然后當(dāng)用戶需要使用這些元素的時候再把它們置成VISIBLE顯示出來。使用這種方式肯定可以實現(xiàn)功能的,但是性能方面就表現(xiàn)得一般了,因為即使是將元素進(jìn)行隱藏,它們其實還是在布局當(dāng)中的,每個元素還擁有著自己的寬、高、背景等等屬性,解析布局的時候也會將這些隱藏的元素一一解析出來。
那么我們?nèi)绾尾拍茏屵@些不常用的元素僅在需要時才去加載呢?Android為此提供了一種非常輕量級的控件,ViewStub。ViewStub雖說也是View的一種,但是它沒有大小,沒有繪制功能,也不參與布局,資源消耗非常低,將它放置在布局當(dāng)中基本可以認(rèn)為是完全不會影響性能的。
還是舉例說明例子1:比如我們在添加三個EditText構(gòu)成另一個布局edittext_extra_layout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/edit_extra1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:hint="Extra field 1" /> <EditText android:id="@+id/edit_extra2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="10dp" android:hint="Extra field 2" /> <EditText android:id="@+id/edit_extra3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="10dp" android:hint="Extra field 3" /> </LinearLayout>接下來我們修改main.xml文件中的代碼,如下所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/edit" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="10dp" android:hint="@string/edit_something_here" /> <Button android:id="@+id/more" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_marginRight="20dp" android:layout_marginBottom="10dp" android:text="More" /> <ViewStub android:id="@+id/view_stub" android:layout="@layout/profile_extra" android:layout_width="match_parent" android:layout_height="wrap_content" /> <include layout="@layout/ok_cancel_layout" /> </LinearLayout>可以看到,這里我們新增了一個More Button,這個按鈕就是用于去加載那些不常用的元素的,然后在Button的下面定義了一個ViewStub。在ViewStub控件中,我們先是通過id屬性給它指定了一個唯一標(biāo)識,又通過layout屬性將profile_extra布局傳入進(jìn)來,接著給ViewStub指定了一個寬高。注意,雖然ViewStub是不占用任何空間的,但是每個布局都必須要指定layout_width和layout_height屬性,否則運(yùn)行就會報錯。
private EditText editExtra1; private EditText editExtra2; private EditText editExtra3; public void onMoreClick() { ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub); if (viewStub != null) { View inflatedView = viewStub.inflate(); editExtra1 = (EditText) inflatedView.findViewById(R.id.edit_extra1); editExtra2 = (EditText) inflatedView.findViewById(R.id.edit_extra2); editExtra3 = (EditText) inflatedView.findViewById(R.id.edit_extra3); } }調(diào)用inflate()方法之后會將加載出來的布局進(jìn)行返回,之后我們就可以對這個布局進(jìn)行任意的操作了,再次隱藏顯示,或者獲取子元素的實例等。注意這里我對ViewStub的實例進(jìn)行了一個非空判斷,這是因為ViewStub在XML中定義的id只在一開始有效,一旦ViewStub中指定的布局加載之后,這個id也就失敗了,那么此時findViewById()得到的值也會是空。
例子2
<ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/stub_comm_lv" android:layout="@layout/my_comment_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" / <?xml version="1.0" encoding="utf-8"?> <ListView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/my_comm_lv" android:layout_height="match_parent" > </ListView> public class MainActivity extends Activity { public void onCreate(Bundle b){ // main.xml中包含上面的ViewStub setContentView(R.layout.main); // 方式1,獲取ViewStub, ViewStub listStub = (ViewStub) findViewById(R.id.stub_import); // 加載評論列表布局 listStub.setVisibility(View.VISIBLE); // 獲取到評論ListView,注意這里是通過ViewStub的inflatedId來獲取 ListView commLv = findViewById(R.id.stub_comm_lv); if ( listStub.getVisibility() == View.VISIBLE ) { // 已經(jīng)加載, 否則還沒有加載 } } }通過setVisibility(View.VISIBILITY)來加載評論列表,此時你要獲取到評論ListView對象的話,則需要通過findViewById來查找,而這個id并不是ViewStub的id。?
這是為什么呢 ?
通過看源碼原因是:
1、加載目標(biāo)布局
2、如果ViewStub的inflatedId不是NO_ID則把inflatedId設(shè)置為目標(biāo)布局根元素的id,即評論ListView的id
3、將ViewStub自身從parent中移除
4、將目標(biāo)布局的根元素添加到parent中
?
轉(zhuǎn)載于:https://my.oschina.net/quguangle/blog/1218057
總結(jié)
以上是生活随笔為你收集整理的性能优化--布局优化技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序实现支付功能
- 下一篇: 如何安装部署秋色园QBlog站点