性能优化--布局优化技巧
2019獨角獸企業重金招聘Python工程師標準>>>
1.重用布局文件
1.)include標簽
首先用得最多的應該是include,按照官方的意思,include就是為了解決重復定義相同布局的問題。使用起來很方便,我這里就不舉例子。
<?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.如果你只想單獨修改某個布局文件的titleBar而其他布局文件的titleBar不受影響的話,我們可以使用覆寫<include>屬性的方式
在<include>標簽當中,我們是可以覆寫所有layout屬性的,即include中指定的layout屬性將會覆蓋掉titlebar中根視圖指定的layout屬性。因此,這里我們希望將titlebar的高度設置成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>標簽當中進行覆寫。另外需要注意的是,如果我們想要在<include>標簽當中覆寫layout屬性,必須要將layout_width和layout_height這兩個屬性也進行覆寫,否則覆寫效果將不會生效。
2.使用include最常見的問題就是findViewById查找不到目標控件,這個問題出現的前提是在include時設置了id,而在findViewById時卻用了被include進來的布局的根元素id。例如上述例子中,include時設置了該布局的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時設置的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");通過看源碼出現這樣的原因是:解析過程中會首先判斷include標簽的id如果不是View.NO_ID的話會把該id設置給被引入的布局根元素的id,即此時在我們的例子中被引入的id為my_title_parent_id的根元素RelativeLayout的id被設置成了include標簽中的id,即RelativeLayout的id被動態修改成了”my_title_ly”。因此此時我們再通過“my_title_parent_id”這個id來查找根元素就會找不到了!?
所以結論就是: 如果include中設置了id,那么就通過include的id來查找被include布局根元素的View;如果include中沒有設置Id, 而被include的布局的根元素設置了id,那么通過該根元素的id來查找該view即可。拿到根元素后查找其子控件都是一樣的。
2.)Merge標簽
<merge>標簽是作為<include>標簽的一種輔助擴展來使用的,它的主要作用是為了防止在引用布局文件時產生多余的布局嵌套。大家都知道,Android去解析和展示一個布局是需要消耗時間的,布局嵌套的越多,那么解析起來就越耗時,性能也就越差,因此我們在編寫布局文件時應該讓嵌套的層數越少越好。
比如我們寫一個公共的確定和取消按鈕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這個界面當中其實已經存在著多余的布局嵌套了!感覺還沒寫幾行代碼呢,怎么這就已經有多余的布局嵌套了?不信的話我們可以通過View Hierarchy工具來查看一下,如下圖所示:
相信大家已經可以看出來了吧,這個內部的LinearLayout就是一個多余的布局嵌套,實際上并不需要這樣一層,讓兩個按鈕直接包含在外部的LinearLayout當中就可以了。而這個多余的布局嵌套其實就是由于布局引入所導致的,那么應該怎樣優化掉這個問題呢?當然就是使用<merge>標簽來完成了,修改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>標簽,這就表示當有任何一個地方去include這個布局時,會將<merge>標簽內包含的內容直接填充到include的位置,不會再添加任何額外的布局結構.
讓我意想不到的是Merge竟然是個activity,并且還動態的加載了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; } }通過看源碼發現:在解析的過程中,如果是merge標簽,那么第一步:首先獲取merge標簽的parent ,第二步:獲取布局參數 ,第三部:遞歸解析每個子元素 ,第四步:將子元素直接添加到merge標簽的parent view中 ,這樣就保證了不會引入額外的層級。
3.)ViewStub標簽
根據官方的解釋大致可翻譯成:其實ViewStub就是一個寬高都為0的一個View,它默認是不可見的,只有通過調用setVisibility函數或者Inflate函數才會將其要裝載的目標布局給加載出來,從而達到延遲加載的效果,這個要被加載的布局通過android:layout屬性來設置。
有的時候我們會遇到這樣的場景,就是某個布局當中的元素非常多,但并不是所有元素都一起顯示出來的,而是普通情況下只顯示部分常用的元素,而那些不常用的元素只有在用戶進行特定操作的情況下才會顯示出來。
這里舉個大家都非常熟悉的例子,我們在添加聯系人的時候其實可以編輯的字段真的非常多,姓名、電話、email、傳真、住址、昵稱等等等等,但其實基本上大家最常用的就是填一個姓名,填一個電話而已。那么將這么多繁雜的字段都一起顯示在界面上其實并不是一種很好的做法,因為大多數人都是用不到這些字段的。比較聰明的做法就是把最常用的姓名和電話顯示在界面上,然后給用戶提供一個添加更多字段的選項,當用戶真的有需要去添加其它信息的時候,我們才將另外的元素顯示到界面上。
說到實現這樣一個功能,我相信大多數人的第一反應就是將不常用的元素使用INVISIBLE或者GONE進行隱藏,然后當用戶需要使用這些元素的時候再把它們置成VISIBLE顯示出來。使用這種方式肯定可以實現功能的,但是性能方面就表現得一般了,因為即使是將元素進行隱藏,它們其實還是在布局當中的,每個元素還擁有著自己的寬、高、背景等等屬性,解析布局的時候也會將這些隱藏的元素一一解析出來。
那么我們如何才能讓這些不常用的元素僅在需要時才去加載呢?Android為此提供了一種非常輕量級的控件,ViewStub。ViewStub雖說也是View的一種,但是它沒有大小,沒有繪制功能,也不參與布局,資源消耗非常低,將它放置在布局當中基本可以認為是完全不會影響性能的。
還是舉例說明例子1:比如我們在添加三個EditText構成另一個布局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屬性給它指定了一個唯一標識,又通過layout屬性將profile_extra布局傳入進來,接著給ViewStub指定了一個寬高。注意,雖然ViewStub是不占用任何空間的,但是每個布局都必須要指定layout_width和layout_height屬性,否則運行就會報錯。
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); } }調用inflate()方法之后會將加載出來的布局進行返回,之后我們就可以對這個布局進行任意的操作了,再次隱藏顯示,或者獲取子元素的實例等。注意這里我對ViewStub的實例進行了一個非空判斷,這是因為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 ) { // 已經加載, 否則還沒有加載 } } }通過setVisibility(View.VISIBILITY)來加載評論列表,此時你要獲取到評論ListView對象的話,則需要通過findViewById來查找,而這個id并不是ViewStub的id。?
這是為什么呢 ?
通過看源碼原因是:
1、加載目標布局
2、如果ViewStub的inflatedId不是NO_ID則把inflatedId設置為目標布局根元素的id,即評論ListView的id
3、將ViewStub自身從parent中移除
4、將目標布局的根元素添加到parent中
?
轉載于:https://my.oschina.net/quguangle/blog/1218057
總結
以上是生活随笔為你收集整理的性能优化--布局优化技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序实现支付功能
- 下一篇: 如何安装部署秋色园QBlog站点