DataBinding結(jié)合BaseActivity和多狀態(tài)布局使用的踩坑記

轉(zhuǎn)載請注明出處 : http://www.reibang.com/p/be740a2eab3a
一般來說诫硕,大部分項目里都會用到多狀態(tài)布局,可以很方便的切換讀取中刊侯,錯誤章办,空內(nèi)容等界面,而且有時候我們會直接把多狀態(tài)布局寫到BaseActivity的根布局里滨彻,比如:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <xxx.xxx.xxx.widget.MultipleStatusLayout
        android:id="@+id/multiple_status_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</merge>

在BaseActivity的onCreate中:

    protected ViewGroup mContentView;
    protected MultipleStatusLayout mMultipleStatusLayout;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.activity_base_core);
        mContentView = (ViewGroup) findViewById(android.R.id.content);
        mMultipleStatusLayout = (MultipleStatusLayout) findViewById(R.id.multiple_status_layout);
    }

然后再重寫一下setContentView方法:

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        if (mMultipleStatusLayout != null) {
            View view = LayoutInflater.from(this).inflate(layoutResID, mMultipleStatusLayout, false);
            mMultipleStatusLayout.removeAllViews();
            mMultipleStatusLayout.addView(view);
        }
    }

這樣寫法有一些好處藕届,相當于給每個activity根布局外面都包了一層多狀態(tài)布局,不用再寫多狀態(tài)布局的xml布局和代碼亭饵,直接調(diào)用父類的即可翰舌,代碼侵入性相當小。

但是如果項目中用了DataBinding就不一樣了冬骚,當然我們像往常一樣在Activity里寫DataBindingUtil.setContentView(this, R.layout.activity_main);時椅贱,果然發(fā)生了這樣的異常:

Caused by: java.lang.RuntimeException: view tag isn't correct on view:null
at com.xxx.xxx.databinding.ActivityMainBinding.bind(ActivityMainBinding.java:98)
at android.databinding.DataBinderMapper.getDataBinder(DataBinderMapper.java:11)
at android.databinding.DataBindingUtil.bind(DataBindingUtil.java:185)
at android.databinding.DataBindingUtil.bindToAddedViews(DataBindingUtil.java:299)
at android.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:279)
at android.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:261)

很好,那我們一起來裝個B只冻,看下ActivityMainBinding的源碼庇麦,看看有沒有辦法兩全其美的解決這個問題,首先定位到bind方法中:

    public static ActivityMainBinding bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {
        if (!"layout/activity_main".equals(view.getTag())) {
            throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
        }
        return new ActivityMain2Binding(bindingComponent, view);
    }
WTF

如此順利就找到了問題所在喜德?不過山橄,只看這里,好像依然不知道怎么解決舍悯,比如第一個參數(shù)view是什么航棱?

那么我們就從異常堆棧信息的棧底開始看:

    public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId) {
        return setContentView(activity, layoutId, sDefaultComponent);
    }
    public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId,
            DataBindingComponent bindingComponent) {
        activity.setContentView(layoutId);
        View decorView = activity.getWindow().getDecorView();
        ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
        return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
    }

到這來說還是很簡單的,先調(diào)用了activity.setContentView萌衬,之后findViewById拿到contentView傳遞到bindToAddedViews饮醇。

    private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
            ViewGroup parent, int startChildren, int layoutId) {
        final int endChildren = parent.getChildCount();
        final int childrenAdded = endChildren - startChildren;
        if (childrenAdded == 1) {
            final View childView = parent.getChildAt(endChildren - 1);
            return bind(component, childView, layoutId);
        } else {
            final View[] children = new View[childrenAdded];
            for (int i = 0; i < childrenAdded; i++) {
                children[i] = parent.getChildAt(i + startChildren);
            }
            return bind(component, children, layoutId);
        }
    }

bindToAddedViews中主要邏輯是找出contentView的所有子view,并再一步傳遞到bind方法中秕豫。
一般來說朴艰,contentView的子view只有一個,就是setContentView時傳遞進來的xml中的根布局混移,如果調(diào)用addContentView就應該會有多個了祠墅,這里暫時只分析一個的情況。

    static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
            int layoutId) {
        return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
    }
    public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {
        switch(layoutId) {
                case com.xxx.xxx.R.layout.activity_main:
                    return com.xxx.xxx.databinding.ActivityMainBinding.bind(view, bindingComponent);
        }
        return null;
    }

突然之間真相大白歌径,我們搞清楚了bind方法中第一個參數(shù)view就是setContentView時xml中的根布局view毁嗦。

那一開始的異常是怎么回事呢?其實很簡單啦回铛,因為此時contentView的子view根本不是setContentView時傳遞進來xml中的根布局view狗准,而是我們寫在BaseActivity里的MultipleStatusLayout芯急。

知道了問題,解決起來當然就簡單了驶俊,我們可以把MultipleStatusLayout的id設置為android.R.id.content娶耍,這樣DataBindingUtil的setContentView方法得到的contentView就是MultipleStatusLayout,它的子view自然就是xml中的根布局view饼酿。
我們可以把這一句加在BaseActivity的setContentView中榕酒,改造后的代碼如下:

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        if (mMultipleStatusLayout != null) {
            View view = LayoutInflater.from(this).inflate(layoutResID, mMultipleStatusLayout, false);
            mMultipleStatusLayout.setId(android.R.id.content);
            mContentView.setId(View.NO_ID);
            mMultipleStatusLayout.removeAllViews();
            mMultipleStatusLayout.addView(view);
        }
    }

當然,別忘了在DataBindingUtil.setContentView(this, R.layout.activity_main)后再把id改回去故俐,不過即使不改回去想鹰,好像也是沒什么問題的。

ok药版,完結(jié)撒花~~

ps:可能有同學會問辑舷,這個tag到底是怎么回事,為什么xml的根布局view會有一個tag槽片,自己分明沒有寫啊何缓,DataBinding的源碼里也沒看到哪里有setTag?其實要講這個tag就要講DataBinding的實現(xiàn)原理了还栓,推薦感興趣的同學看下這篇文章碌廓,講的很詳細:http://www.reibang.com/p/de4d50b88437

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市剩盒,隨后出現(xiàn)的幾起案子谷婆,更是在濱河造成了極大的恐慌,老刑警劉巖辽聊,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纪挎,死亡現(xiàn)場離奇詭異,居然都是意外死亡跟匆,警方通過查閱死者的電腦和手機异袄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贾铝,“玉大人隙轻,你說我怎么就攤上這事」缚” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵敛瓷,是天一觀的道長叁巨。 經(jīng)常有香客問我,道長呐籽,這世上最難降的妖魔是什么锋勺? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任蚀瘸,我火速辦了婚禮,結(jié)果婚禮上庶橱,老公的妹妹穿的比我還像新娘贮勃。我一直安慰自己,他們只是感情好苏章,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布寂嘉。 她就那樣靜靜地躺著,像睡著了一般枫绅。 火紅的嫁衣襯著肌膚如雪泉孩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天并淋,我揣著相機與錄音寓搬,去河邊找鬼。 笑死县耽,一個胖子當著我的面吹牛句喷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兔毙,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼脏嚷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞒御?” 一聲冷哼從身側(cè)響起父叙,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肴裙,沒想到半個月后趾唱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡蜻懦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年甜癞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宛乃。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡悠咱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出征炼,到底是詐尸還是另有隱情析既,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布谆奥,位于F島的核電站眼坏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酸些。R本人自食惡果不足惜宰译,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一檐蚜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沿侈,春花似錦闯第、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至智厌,卻和暖如春诲泌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铣鹏。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工敷扫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诚卸。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓葵第,卻偏偏與公主長得像,于是被迫代替她去往敵國和親合溺。 傳聞我的和親對象是個殘疾皇子卒密,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容