Android 優(yōu)化之布局優(yōu)化

布局優(yōu)化的思想主要就是盡可能地減少布局文件的層級,減少系統(tǒng)繪制的工作量,這也符合扁平化設(shè)計。

使用<include> 標簽

<include> 標簽可以將一個指定的布局文件加載到另一個布局文件中,適合的場景是出現(xiàn)了公共頁面模塊,例如自定義的標題欄。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    
    <include layout="@layout/view_title"/>
    
    <TextView
        android:background="#fffff0"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

通過在這里使用 <include> 標簽,@layout/titlebar 指定了引入的布局為 view_title,達到了復用的目的,view_title 布局如下:

<?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="44dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:padding="5dp"
        android:text="返回"
        android:textSize="15dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:gravity="center"
        android:padding="5dp"
        android:text="標題"
        android:textSize="15dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:padding="5dp"
        android:text="完成"
        android:textSize="15dp" />

</LinearLayout>

<include> 標簽只支持以 android:layout_* 開頭的屬性,例如 layout_width、layout_height,不過指定其他 layout_* 屬性的話,android:layout_width 和 anroid:layout_height 必須存在。不過 android:id 這是一個例外,可以在 <include> 標簽重新指定,而且以這個為準,無論被包含的布局文件的根元素是否指定了 id 屬性。

使用<merge> 標簽

<merge> 標簽一般和 <include> 標簽可以防止在引用布局文件時產(chǎn)生多余的布局嵌套,如下:

<?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">

    <include layout="@layout/view_merge" />

</LinearLayout>

使用 <merge> 標簽前:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">

    <TextView
        android:gravity="center"
        android:text="this is merge test."
        android:layout_width="match_parent"
        android:layout_height="50dp" />

</LinearLayout>

使用 <merge> 標簽后:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">

    <TextView
        android:gravity="center"
        android:text="this is merge test."
        android:layout_width="match_parent"
        android:layout_height="50dp" />

</merge>

這樣就可以去掉了那多余的 LinearLayout,同理,當繼承了 RelativeLayout 等ViewGroup 自定義 View 時,如果 xml 頂級標簽和繼承的 ViewGroup 效果相同時,應該把 xml 的頂級標簽替換為 <include> 標簽。例如:

public class MergeView extends LinearLayout {
    public MergeView(Context context) {
        this(context, null);
    }

    public MergeView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MergeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        LayoutInflater.from(context).inflate(R.layout.view_merge, this, true);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:gravity="center"
        android:text="this is merge test."
        android:layout_width="match_parent"
        android:layout_height="50dp" />

</merge>

MergeView 通過 LayoutInflater 加載 view_merge 布局,而 view_merge 布局使用 <merge> 標簽,達到了減少嵌套層級的目的。

使用<ViewStub> 標簽

<ViewStub> 繼承了 View,非常輕量且寬/高都是 0 ,ViewStub 的意義在于按需加載布局文件,在日常開發(fā)中,有很多布局文件在正常情況下不會顯示,有些人可能會把不顯示的布局設(shè)置為 GONE 或者 INVISIBLE,但這樣設(shè)置它們還是加載在布局當中的,每個元素還時擁有著自己的寬、高、背景等等屬性。如果使用 ViewStub 的話可以做到在使用的時候再加載,提高了程序初始化的性能。比如下面的一個示例是網(wǎng)絡出現(xiàn)錯誤時才會加載的頁面:

public class NetworkErrorActivity extends AppCompatActivity {

    private TextView mContextTv;
    private TextView mErrorTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network_error);
        mContextTv = findViewById(R.id.content_tv);
        mContextTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mErrorTv == null) {
                    mErrorTv = (TextView) ((ViewStub) findViewById(R.id.error_stub)).inflate();
                }
            }
        });
    }
}

布局activity_network_error:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".layoutoptim.NetworkErrorActivity">

    <TextView
        android:id="@+id/content_tv"
        android:text="正常顯示頁面"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ViewStub
        android:id="@+id/error_stub"
        android:inflatedId="@+id/error_tv"
        android:layout="@layout/view_network_error"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

布局 error_stub:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@android:color/white"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:text="網(wǎng)絡出錯了">

</TextView>

在 NetworkErrorActivity 的 mContextTv 的點擊事件里去加載了 ViewStub 布局,加載完成后 ViewStub 就會被它內(nèi)部的布局替換掉,這個時候 ViewStub 就不再是布局中的一部分了,這也導致了 inflate() 方法只能被調(diào)用一次,再次調(diào)用則會拋出異常,所以要做好相應的處理。ViewStub 中的 id 即 ViewStub 的 id,inflatedId 則是要被加載布局的根元素的 id。如果在被加載布局的根元素內(nèi)部指定了 id 的同時指定 inflatedId ,以 inflatedId 為主。遺憾的是,ViewStub 目前所加載的布局不支持 <merge> 標簽,這有可能導致加載出來的布局出現(xiàn)多余的嵌套結(jié)構(gòu)。

Double Taxation

通常情況下,系統(tǒng)會在一次遍歷中快速執(zhí)行測量或布局階段。但在一些情況比較復雜的布局中,在最終放置元素之前,系統(tǒng)可能必須對層次結(jié)構(gòu)中需要多次遍歷才能完成最終的效果。必須執(zhí)行不止一次 “測量和布局”的情況稱為 Double Taxation。例如,當使用 RelativeLayout 時,系統(tǒng)會執(zhí)行以下操作:

  1. 執(zhí)行一次“測量和布局”遍歷,在此過程中,框架會根據(jù)每個子對象的請求計算對象的位置和大小。
  2. 結(jié)合數(shù)據(jù)和對象的權(quán)重確定關(guān)聯(lián)視圖的恰當位置。
  3. 執(zhí)行第二次遍歷,以最終確定對象的位置。
  4. 進入渲染過程的下一階段。

使用 ConstraintLayout 布局

ConstraintLayout 可以很好地解決復雜頁面的嵌套問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容