android merge標籤 的使用 和 遇到的坑

醉過才知酒濃發表於2018-12-07

前言

在寫一個功能時用到了動態載入  merge 標籤的佈局  由於原來都是直接在xml 用 include 直接使用  沒有在程式碼裡使用過  也沒有仔細的瞭解過原理,所以直接掉坑裡了   直接是用了平常佈局的
複製程式碼

var view =     LayoutInflater.from(mContext)
.inflate(R.layout.dialog_commom_default_content,null)

layout.addView(view)
複製程式碼

來使用 結果一執行到這段程式碼就崩潰了,然後就仔細的瞭解了一下 merge 原理 請看下文:

merge簡介

merge標籤是用來減少ui層級,優化佈局的。
merge在一般情況下是用來配合include使用的
複製程式碼

如下:


layout_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/app_bg"
    android:gravity="center_horizontal">

    <include layout="@layout/title"/>
</LinearLayout>

layout_title.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="@string/delete"/>
</merge>
複製程式碼

這樣使用的。 merge標籤是怎麼來減少層級的呢 ,在LayoutInflater的inflate()函式中解析時 發現解析的是 merge標籤時會把merge 裡的子元素直接新增到 merge的 父控制元件裡 這樣就減少了一個沒有比較的容器控制元件了。 而且 merge 標籤必須是根元素

merge遇到的坑

1.

在使用merge時給標籤設定id 然後再程式碼裡用findViewById去獲取這個id view時 會出現崩潰的情況,這就是因為是用merge標籤 在 LayoutInflater的inflate()函式中時直接將其中的子元素新增到了 merge 標籤的 parent 中了,而merge 因為只是一個標籤 所以是沒有新增到 parent 裡的,所以在執行時就沒有這個merge 所以就會報錯。

在程式碼中通過程式碼新增 merge 標籤佈局 最先的時候是

var frameLayout =getView<FrameLayout>(R.id.fl_content)
if (!::view.isInitialized){
setConentLayout(LayoutInflater.from(mContext)
.inflate(R.layout.dialog_commom_default_content,null))
}
frameLayout?.addView(view,frameLayout.layoutParams )
複製程式碼

但是一執行這程式碼就會崩潰 這個是候用debug看了 在哪行程式碼發生的錯誤 是在

setConentLayout(LayoutInflater.from(mContext)
.inflate(R.layout.dialog_commom_default_content,null))
複製程式碼

2.

這裡發生的錯誤,檢視了一下原始碼 發現是因為inflate()函式中 新增的佈局是 merge 佈局是需要新增 在inflate()函式中傳入 ViewGroup
因為在LayoutInflater 的inflate函式中有這個判斷 當解析的是 merge標籤時 如果這個 ViewGroup 為null 或attachToRoot 為false 就會直接丟擲錯誤

if (TAG_MERGE.equals(name)) {
    if (root == null || !attachToRoot) {
        throw new InflateException("<merge /> can be used only with a valid "
                + "ViewGroup root and attachToRoot=true");
    }

    rInflate(parser, root, inflaterContext, attrs, false);
}
複製程式碼

解決了這個問題 程式碼改成這樣

var frameLayout =getView<FrameLayout>(R.id.fl_content)
if (!::view.isInitialized){
setConentLayout(LayoutInflater.from(mContext)
.inflate(R.layout.dialog_commom_default_content,frameLayout,true))
}
frameLayout?.addView(view,frameLayout.layoutParams )

複製程式碼

我本以為程式就可以愉快的跑下去了

3.但是我在一次執行時又報錯了 這次錯誤是在

frameLayout?.addView(view,frameLayout.layoutParams )
複製程式碼

這一行裡 ,但是我找了半天也沒有找到問題所在 ,然後仔細看了一下原始碼 發現是因為 在

LayoutInflater.from(mContext)
.inflate(R.layout.dialog_commom_default_content,frameLayout,true)
複製程式碼

的時候其實 merge 佈局裡的元素就已經新增進佈局了 而且 這個返回的 View 就是我們傳入的 ViewGroup 所以我的這段程式碼 就是 把自己新增進自己 所以報錯了

最後我改成以下程式碼就ok了

var frameLayout =getView<FrameLayout>(R.id.fl_content)
if (!::view.isInitialized){
setConentLayout(LayoutInflater.from(mContext)
.inflate(R.layout.dialog_commom_default_content,frameLayout,true))
}
複製程式碼

引用www.jianshu.com/p/cf3751333… www.androidchina.net/2485.html

相關文章