Android視圖綁定ViewBinding的使用

前言

后臺(tái)讀者留言:能否寫一篇視圖綁定ViewBinding相關(guān)的內(nèi)容?

首先感謝這位讀者的提議,讓我抽出時(shí)間細(xì)看視圖綁定的內(nèi)容烙常,也打算在項(xiàng)目中使用該功能。當(dāng)然鹤盒,還有其他讀者提議的內(nèi)容我已記錄蚕脏,后期有時(shí)間也會(huì)陸續(xù)更新。話不多說(shuō)侦锯,我們開(kāi)始學(xué)習(xí)吧驼鞭!

概述

在我們的開(kāi)發(fā)過(guò)程中,需要獲取XML布局文件中的ViewId尺碰,以便其賦值顯示挣棕,我們習(xí)慣使用findViewById進(jìn)行操作译隘,可這樣會(huì)導(dǎo)致很多的模版代碼出現(xiàn)。直到Android大神 Jake Wharton開(kāi)源了Butter Knife框架洛心,通過(guò)Bind方式綁定獲取ViewId固耘。近兩年谷歌對(duì)Kotlin的支持,我們開(kāi)始使用 Android Kotlin extensions词身。在文件中導(dǎo)入布局文件直接引用viewId厅目。無(wú)需做其他額外操作,最為方便法严。

目前损敷,谷歌在 Android Studio 3.6 Canary 11 及更高版本中加入了新的視圖綁定方式ViewBinding。

注意:要使用ViewBinding功能渐夸,AndroidStudio至少要升級(jí)到3.6嗤锉。

分析

本文主要從以下方面對(duì)ViewBinding進(jìn)行分析:

  • 使用能解決什么問(wèn)題;
  • 使用流程墓塌;
  • 與之前方法的比較瘟忱;
  • 原理;

1.使用能解決什么問(wèn)題

顧名思義ViewBinding的意思就是如何將view與代碼綁定在一起苫幢。所以其主要解決如何安全優(yōu)雅地從代碼中引用到XML layout文件中的view控件的問(wèn)題访诱。直到目前為止,Android構(gòu)建用戶界面的主流方式仍然是使用XML格式的layout文件韩肝。

2.使用流程

  • 在要使用ViewBinding的 module 的gradle文件中開(kāi)啟ViewBinding

android {
    ……………
    viewBinding {
        enabled = true
    }
    ……………
}
  • 如果在使用的過(guò)程中開(kāi)發(fā)者不想為某個(gè)布局文件生成binding類触菜,則可以使用如下屬性添加到布局的根視圖中即可:

<androidx.constraintlayout.widget.ConstraintLayout
  …………
      tools:viewBindingIgnore="true" >
  …………
</androidx.constraintlayout.widget.ConstraintLayout>
  • 編譯此module獲得XML布局文件對(duì)應(yīng)的綁定類

在gradle文件中開(kāi)啟ViewBinding功能后,編譯器就會(huì)為此模塊下的每個(gè)布局文件都產(chǎn)生一個(gè)對(duì)應(yīng)的綁定類。

假設(shè)我們有如下XML布局文件


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    tools:viewBindingIgnore="true" >

    <ImageView
        android:id="@+id/img_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher_background"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

這個(gè)類名稱的命名規(guī)則為:XML布局文件名去掉下劃線哀峻,下劃線首字母大寫涡相,最后加上Binding。例如我有一個(gè)布局文件activity_main.xml,那對(duì)應(yīng)生成的類文件為ActivityMainBinding.java剩蟀。

生成的類文件位于Module路徑:
build\generated\data_binding_base_class_source_out\debug\out\包名\databinding下催蝗。

如下圖所示:


在這里插入圖片描述
  • 使用此生成類引用XML布局文件中的控件

調(diào)用生成類ActivityDescriptionBinding的inflate()方法獲得類實(shí)例對(duì)象,通過(guò)getRoot()方法可以獲得layout文件的最外層View,此例中是一個(gè)ConstraintLayout. 通過(guò)Activity的 setContentView()方法可以為Activity設(shè)置內(nèi)容育特。layout文件中只要是有id的view, 在這個(gè)生成類中都會(huì)對(duì)應(yīng)的生成一個(gè) public final 的屬性丙号,例如:


<TextView
        android:id="@+id/tv_content"
        ...
        />

對(duì)應(yīng)的生成字段為:

 @NonNull
 public final TextView tvContent;

那就可以直接使用對(duì)象實(shí)例訪問(wèn)了,如下代碼所示:

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding activityMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //設(shè)置布局文件
        activityMainBinding = ActivityMainBinding.inflate(LayoutInflater.from(this));
        setContentView(activityMainBinding.getRoot());

        //設(shè)置文本
        activityMainBinding.tvContent.setText("要么停止成長(zhǎng)缰冤,要么不斷向前");
        //設(shè)置圖片
        activityMainBinding.imgShow.setImageResource(R.drawable.img);
    }
}

3.與之前方法的比較

目前Android開(kāi)發(fā)中完成View映射的方法主要有 findViewById犬缨、 ButterKnife, 如果使用kotlin的話還可以使用Kotlin Android Extensions。

這些方式的各方面對(duì)比如下:


在這里插入圖片描述

ViewBinding對(duì)比以上方法有如下幾點(diǎn)優(yōu)勢(shì):

  • Type safety:findViewById, ButterKnife 均存在類型轉(zhuǎn)換問(wèn)題棉浸,例如不小心將一個(gè)TextView錯(cuò)誤的賦值給一個(gè)Button變量怀薛,都會(huì)報(bào)錯(cuò),這一錯(cuò)誤很容易出現(xiàn)迷郑,關(guān)鍵在錯(cuò)誤還出現(xiàn)在運(yùn)行時(shí)乾戏,而不是編譯時(shí)迂苛!

    而ViewBinding中三热,產(chǎn)生的binding類中的屬性是依據(jù)XML layout文件生成的鼓择,所以類型不會(huì)錯(cuò),生成的時(shí)候已經(jīng)處理好了就漾。

  • Null safety: findViewById, ButterKnife與Kotlin Android Extensions 均存在Null不安全問(wèn)題呐能。這個(gè)什么意思呢?就是在我們?cè)L問(wèn)那個(gè)View的時(shí)候它不存在抑堡。為什么會(huì)出現(xiàn)這種情況呢摆出?例如不小心使用了錯(cuò)誤的Id,或者訪問(wèn)的時(shí)候那個(gè)view還不存在。

使用了錯(cuò)誤Id這個(gè)估計(jì)大家都有此類經(jīng)歷首妖,但是訪問(wèn)時(shí)候那個(gè)view不存在怎么理解呢偎漫?例如我們?cè)谑謾C(jī)橫屏和豎屏的時(shí)候分別使用一套XML layout文件,假設(shè)橫屏中包含了一個(gè)豎屏中沒(méi)有的view,那么在屏幕從橫屏旋轉(zhuǎn)到豎屏的時(shí)候有缆,NullPointer問(wèn)題就出現(xiàn)了象踊。

而ViewBinding中, 產(chǎn)生的binding類中的屬性是依據(jù)XML layout文件生成的棚壁,所以Id不會(huì)錯(cuò)杯矩。而且其將僅存在某一個(gè)配置下的layout文件的那些view對(duì)應(yīng)的字段標(biāo)記為@Nullable ,例如本例中的:

@NonNull
 public final TextView tvContent;

而且,生成類中還會(huì)很貼心的給你加上詳細(xì)的注釋袖外。這一切都是為了提醒程序員史隆,注意對(duì)這個(gè)view特別處理,它在某些情況下為Null曼验。

  • 簡(jiǎn)潔優(yōu)雅: 將綁定view的模板代碼自動(dòng)生成到了其他類中泌射,使controlor類(Activity,F(xiàn)ragment)更加清晰了鬓照。

4.原理

通過(guò)上面分析熔酷,估計(jì)你對(duì)其原理也猜的的八九不離十了。就是Google在那個(gè)用來(lái)編譯的gradle插件中增加了新功能颖杏,當(dāng)某個(gè)module開(kāi)啟ViewBinding功能后纯陨,編譯的時(shí)候就去掃描此模塊下的layout文件,生成對(duì)應(yīng)的binding類留储。那些你所熟悉的findViewById操作都是在這個(gè)自動(dòng)生成的類里面呢翼抠,如下所示:


public final class ActivityMainBinding implements ViewBinding {
  @NonNull
  private final ConstraintLayout rootView;

  @NonNull
  public final ImageView imgShow;

  @NonNull
  public final TextView tvContent;

  private ActivityMainBinding(@NonNull ConstraintLayout rootView, @NonNull ImageView imgShow,
      @NonNull TextView tvContent) {
    this.rootView = rootView;
    this.imgShow = imgShow;
    this.tvContent = tvContent;
  }

  @Override
  @NonNull
  public ConstraintLayout getRoot() {
    return rootView;
  }

  @NonNull
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, null, false);
  }

  @NonNull
  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup parent, boolean attachToParent) {
    View root = inflater.inflate(R.layout.activity_main, parent, false);
    if (attachToParent) {
      parent.addView(root);
    }
    return bind(root);
  }

  @NonNull
  public static ActivityMainBinding bind(@NonNull View rootView) {
    // The body of this method is generated in a way you would not otherwise write.
    // This is done to optimize the compiled bytecode for size and performance.
    String missingId;
    missingId: {
      ImageView imgShow = rootView.findViewById(R.id.img_show);
      if (imgShow == null) {
        missingId = "imgShow";
        break missingId;
      }
      TextView tvContent = rootView.findViewById(R.id.tv_content);
      if (tvContent == null) {
        missingId = "tvContent";
        break missingId;
      }
      return new ActivityMainBinding((ConstraintLayout) rootView, imgShow, tvContent);
    }
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }
}

其中核心代碼是bind(@NonNull View rootView)方法,除此之外還有兩個(gè)inflate()重載方法获讳,一般情況下我們使用這兩個(gè)方法獲得binding類的實(shí)例阴颖,這些方法都是public static的,通過(guò)bind(@NonNull View rootView)這個(gè)方法應(yīng)該可以實(shí)現(xiàn)延遲綁定丐膝,但是其使用場(chǎng)景應(yīng)該很少量愧。

總結(jié)

目前ViewBinding的功能還不夠完善钾菊,比如XML中使用了 inClude 標(biāo)簽時(shí)無(wú)法對(duì)view進(jìn)行引用。但總體來(lái)說(shuō)已經(jīng)很不錯(cuò)了偎肃。相比較于 findViewById 和 Butter Knife兩種方式還是方便很多的煞烫。而且 ViewBinding 在使用的過(guò)程中不存在類型轉(zhuǎn)換以及空指針異常的問(wèn)題。因?yàn)樵诮壎愔幸呀?jīng)全部定義好了累颂,開(kāi)發(fā)者直接使用就可以滞详。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市紊馏,隨后出現(xiàn)的幾起案子料饥,更是在濱河造成了極大的恐慌,老刑警劉巖朱监,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岸啡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡赫编,警方通過(guò)查閱死者的電腦和手機(jī)巡蘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沛慢,“玉大人赡若,你說(shuō)我怎么就攤上這事⊥偶祝” “怎么了逾冬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)躺苦。 經(jīng)常有香客問(wèn)我身腻,道長(zhǎng),這世上最難降的妖魔是什么匹厘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任现拒,我火速辦了婚禮英遭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己鹃锈,他們只是感情好攀例,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布振坚。 她就那樣靜靜地躺著购撼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匕累。 梳的紋絲不亂的頭發(fā)上陵刹,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音欢嘿,去河邊找鬼衰琐。 笑死也糊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羡宙。 我是一名探鬼主播狸剃,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辛辨!你這毒婦竟也來(lái)了捕捂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斗搞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后慷妙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體僻焚,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年膝擂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虑啤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡架馋,死狀恐怖狞山,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叉寂,我是刑警寧澤萍启,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站屏鳍,受9級(jí)特大地震影響勘纯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钓瞭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一驳遵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧山涡,春花似錦堤结、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至系吩,卻和暖如春来庭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穿挨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工月弛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肴盏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓帽衙,卻偏偏與公主長(zhǎng)得像菜皂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子厉萝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 本文首發(fā)于微信公眾號(hào)「Android開(kāi)發(fā)之旅」恍飘,歡迎關(guān)注 ,獲取更多技術(shù)干貨 Jetpack版Wan-Androi...
    李四爺_閱讀 5,524評(píng)論 0 1
  • ● What通過(guò)視圖綁定谴垫,可以更好的編寫與視圖進(jìn)行交互的代碼章母。在模塊中啟用視圖綁定以后,系統(tǒng)會(huì)為模塊中的每一個(gè)布局...
    和萊閱讀 766評(píng)論 0 0
  • 什么是View Binding 官方文檔[https://developer.android.google.cn/...
    lq_ios閱讀 69,585評(píng)論 7 64
  • 推薦指數(shù): 6.0 書(shū)籍主旨關(guān)鍵詞:特權(quán)翩剪、焦點(diǎn)乳怎、注意力、語(yǔ)言聯(lián)想前弯、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析蚪缀,社會(huì)...
    Jenaral閱讀 5,701評(píng)論 0 5
  • 城空了询枚,有樹(shù)長(zhǎng)出來(lái) 我的城死了 鑄起它的人,殺死它的人 不愿因?yàn)檫@件事而驕傲 一座城的終結(jié) 永遠(yuǎn)因?yàn)榻K結(jié)這件事而顯...
    于十六閱讀 2,850評(píng)論 6 17