㈠ android 布局優化之 ViewStub、 include、merge
文章、布局優化神器 include 、merge、ViewStub 標簽詳解
Tips:
include 標簽使用:
//include_test.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:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/textview"
android:textSize="24sp"/>
<EditText
android:id="@+id/editText"
android:hint="@string/divide"
android:layout_width="300dp"
android:layout_height="wrap_content"/>
</LinearLayout>---------------------------------------------------------------
//include_text_relative
<?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="wrap_content"
android:gravity="center_horizontal"
>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="TextView_Relative"
android:textSize="24sp"/>
<EditText
android:id="@+id/editText"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_below="@+id/textView"
android:hint="@string/divide"/>
</RelativeLayout>
---------------------------------------------------------------
//include_toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tb_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#00f"
app:theme="@style/AppTheme"
app:title="這是一個 ToolBar"app:titleTextColor="@android:color/white"/>
<?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"
>
<!--測試 layout 和<include>都設置 ID 的情況-->
<include
android:id="@+id/tb_toolbar"
layout="@layout/include_toolbar"/>
<!--如果只有單個 include 這樣寫就可以,載入的布局的子 View,直接 findViewByID 就能找
到-->
<include layout="@layout/include_text"/>
<!--如果有多個 include,需要添加 ID 屬性-->
<include
android:id="@+id/include_text1"
layout="@layout/include_text"/>
<!--這個 layout 用 RelativeLayout 實現-->
<!--如果要使用 layout_margin 這樣的屬性,要同時加上 layout_w/h 屬性,不然沒反應-->
<include
android:id="@+id/include_text2"
layout="@layout/include_text_relative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="50dp"/>
</LinearLayout>
private void initView() {
//如果 include 布局根容器和 include 標簽中的 id 設置的是不同的值,這里獲取的
mToolbar 值將為 nullToolbar mToolbar = (Toolbar) findViewById(R.id.tb_toolbar);
setSupportActionBar(mToolbar);
//普通 include 標簽用法,直接拿子 View 屬性實現
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("不加 ID 實現的 include 標簽");
//多個 include 標簽用法,添加 ID,findViewByID 找到 layout,再找子控制項
View view_include = findViewById(R.id.include_text1);
TextView view_include_textView = (TextView) view_include.findViewById(R.id.textView);
view_include_textView.setText("加了 ID 實現的 include 標簽");
//多個 include 標簽用法,添加 ID,findViewByID 找到 layout,再找子控制項
View view_include_Relative = findViewById(R.id.include_text2);
TextView view_textView_relative =(TextView) view_include_Relative.findViewById(R.id.textView);
view_textView_relative.setText("加了 ID 實現的 include 標簽(RelaviteLayout)");
}
include 標簽 Demo 效果圖
==include 使用注意==
merge標簽主要用於輔助include標簽,在使用include後可能導致布局嵌套過
多,多餘的 layout 節點或導致解析變慢(可通過 hierarchy viewer 工具查看布
局的嵌套情況)官方文檔說明:merge 用於消除視圖層次結構中的冗餘視圖,例如根布局是
Linearlayout,那麼我們又 include 一個 LinerLayout 布局就沒意義了,反而會
減慢 UI 載入速度
merge 標簽常用場景: .
根布局是 FrameLayout 且不需要設置 background 或 padding 等屬性,可
以用merge代替,因為 Activity的 ContentView 父元素就是FrameLayout,
所以可以用 merge 消除只剩一個.
.某布局作為子布局被其他布局include時,使用merge當作該布局的頂節點,
這樣在被引入時頂結點會自動被忽略,而將其子節點全部合並到主布局中.
.自定義 View 如果繼承 LinearLayout(ViewGroup),建議讓自定義 View 的
布局文件根布局設置成 merge,這樣能少一層結點.
.merge 標簽使用:
在 XML 布局文件的根布局如 RelativeLayout 直接改成 merge 即可
merge 標簽使用前後 Hierarchy Viewer 截圖
FrameLayout 替換 merge 前
FrameLayout 替換 merge 後
include 標簽對應 layout 根布局 替換 merge 前
include 標簽對應 layout 根布局 替換 merge 後
==merge 使用注意==
ViewStub 標簽最大的優點是當你需要時才會載入,使用它並不會影響 UI 初始
化時的性能.各種不常用的布局像進度條、顯示錯誤消息等可以使用 ViewStub
標簽,以減少內存使用量,加快渲染速度.ViewStub 是一個不可見的,實際上是把
寬高設置為 0 的 View.效果有點類似普通的 view.setVisible(),但性能體驗提高
不少
第一次初始化時,初始化的是 ViewStub View,當我們調用 inflate()或
setVisibility()後會被 remove 掉,然後在將其中的 layout 加到當前 view
hierarchy 中
ViewStub 官方文檔鏈接
//官方例子
<ViewStub
android:id="@+id/stub_import"
<!--android:inflateId:重寫 ViewStub 的父布局控制項的 Id-->
android:inflatedId="@+id/panel_import"
< <!--android:layout:設置 ViewStub 被 inflate 的布局-->
android:layout="@layout/progress_overlay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
-------------------------------------------------//當你想載入布局時,可以使用下面其中一種方法:
((ViewStub)
findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub)
findViewById(R.id.stub_import)).inflate();
判斷 ViewStub(做單例)是否已經載入過:
1. 如果通過 setVisibility 來載入,那麼通過判斷可見性即可;
2. 如果通過 inflate()來載入,判斷 ViewStub 的 ID 是否為 null 來判斷
(findViewById(…))
public class ViewStubActivity extends AppCompatActivity
implements View.OnClickListener, ViewStub.OnInflateListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);setContentView(R.layout.activity_view_stub);
}
private View networkErrorView;
private void showNetError() {
// not repeated infalte
if (networkErrorView != null) {
//setVisibility()方式載入布局,載入次數不限
networkErrorView.setVisibility(View.VISIBLE);
return;
}
//inflate()方式載入布局,只能載入一次
ViewStub stub = (ViewStub)
findViewById(R.id.network_error_layout);
stub.setOnInflateListener(this);networkErrorView = stub.inflate();
Button networkSetting = (Button)
networkErrorView.findViewById(R.id.network_miss);
networkSetting.setOnClickListener(this);
Button refresh = (Button)
findViewById(R.id.network_refresh);
refresh.setOnClickListener(this);
}
private void showNormal() {
if (networkErrorView != null) {
networkErrorView.setVisibility(View.GONE);
}
}
public void show(View view) {showNetError();
}
public void refresh(View view) {
showNormal();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.network_miss:
Toast.makeText(this, "network_miss",
Toast.LENGTH_SHORT).show();
break;
case R.id.network_refresh:
Toast.makeText(this, "network_refresh",
Toast.LENGTH_SHORT).show();break;
}
}
/**
*
* @param stub 當前待 inflate 的 ViewStub 控制項
* @param inflated 當前被 inflate 的 View 視圖
*/
@Override
public void onInflate(ViewStub stub, View inflated) {
}
}
ViewStub 標簽 Demo 效果圖:
ViewStub 標簽使用前後 Hierarchy Viewer 截圖
ViewStub 標簽使用前:
ViewStub 標簽使用後:
==ViewStub 標簽使用注意==
擴展:
Space 組件
在 ConstraintLayout 出來前,我們寫布局都會使用到大量的 margin 或
padding,但是這種方式可讀性會很差,加一個布局嵌套又會損耗性能
鑒於這種情況,我們可以使用space,使用方式和View一樣,不過主要用來佔位置,
不會有任何顯示效果