Android View Binding使用

什么是View Binding

官方文檔

View Binding是Android Studio 3.6推出的新特性摔寨,目的是為了替代findViewById(內(nèi)部實現(xiàn)還是使用findViewById)犀变。。在啟動視圖綁定后,系統(tǒng)會為改模塊中的每個xml文件生成一個綁定類,綁定類的實例包含對在相應布局中具有 ID 的所有視圖的直接引用砰左。

View Binding 的優(yōu)點

  • Null安全:由于視圖綁定會創(chuàng)建對視圖的直接引用箭券,因此不存在因視圖 ID 無效而引發(fā) Null 指針異常的風險允华。此外井联,如果視圖僅出現(xiàn)在布局的某些配置中卜壕,則綁定類中包含其引用的字段會使用 @Nullable 標記。
  • 類型安全:每個綁定類中的字段均具有與它們在 XML 文件中引用的視圖相匹配的類型低矮。這意味著不存在發(fā)生類轉(zhuǎn)換異常的風險。

如何啟用View Binding 功能

android {
        ...
        viewBinding {
            enabled = true
        }
    }
} 

如果想在生成綁定類時忽略某個布局文件被冒,將 tools:viewBindingIgnore="true" 屬性添加到相應布局文件的根視圖中:

<?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:viewBindingIgnore="true">

    <TextView
        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>

生成的 ActivityTestBinding.java :
文件位置在 (app/build/generated/data_binding_base_class_source_out/debug/out/<包路徑>/databinding/ActivityTestBinding.java)

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

  private ActivityTestBinding(@NonNull ConstraintLayout rootView) {
    this.rootView = rootView;
  }

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

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

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

  @NonNull
  public static ActivityTestBinding bind(@NonNull View rootView) {
    if (rootView == null) {
      throw new NullPointerException("rootView");
    }

    return new ActivityTestBinding((ConstraintLayout) rootView);
  }
}

怎么去使用View Binding

為用視圖綁定功能后军掂,系統(tǒng)會為該模塊中包含的每個 XML 布局文件生成一個綁定類。這個類的類名是以xml布局文件名去掉下?lián)Q線后昨悼,單詞首字母大寫加上Binding命名的蝗锥。如activity_main.xml生成的類ActivityMainBinding.

  • 如何在Activity中設置綁定,請在ActivityonCreate()方法中執(zhí)行以下步驟:

    1. 調(diào)用生成的綁定類中包含的靜態(tài) inflate() 方法率触。此操作會創(chuàng)建該綁定類的實例以供 Activity 使用终议。

    2. 通過調(diào)用 getRoot() 方法或使用 Kotlin 屬性語法獲取對根視圖的引用。

    3. 將根視圖傳遞到setContentView()葱蝗,使其成為屏幕上的活動視圖穴张。

    //activity_main.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">
    
        <Button
            android:id="@+id/button"
            android:layout_width="100dp"
            android:layout_height="40dp"
            android:text="這是按鈕"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="100dp"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="Hello World!"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>  
    
    
    //MainActivity.java
    public class MainActivity extends AppCompatActivity {
        private ActivityMainBinding binding;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //關鍵代碼
            binding = ActivityMainBinding.inflate(getLayoutInflater());
            View rootView = binding.getRoot();
            setContentView(rootView);
            //如何使用
            binding.textView.setText("這是修改的");
            binding.button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.d("Main","點擊了按鈕");
                }
            });
    
        }
    }
    
    
  • 如何在Fragment中使用視圖綁定,請在FragmentonCreateView()方法中執(zhí)行以下步驟(注意:Fragment 的存在時間比其視圖長两曼。請務必在FragmentonDestroyView()方法中清除對綁定類實例的所有引用皂甘。)

    1. 調(diào)用生成的綁定類中包含的靜態(tài) inflate() 方法。此操作會創(chuàng)建該綁定類的實例以供 Fragment 使用悼凑。

    2. 通過調(diào)用 getRoot() 方法或使用 Kotlin 屬性語法獲取對根視圖的引用偿枕。

    3. onCreateView() 方法返回根視圖,使其成為屏幕上的活動視圖户辫。

    //fragment_my.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">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:text="這是Fragment按鈕"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="這是FragmentTextView"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    
    public class MyFragment extends Fragment {
      private FragmentMyBinding binding;
    
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          binding = FragmentMyBinding.inflate(inflater, container, false);
          return binding.getRoot();
      }
    
      @Override
      public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
          super.onViewCreated(view, savedInstanceState);
          binding.textView.setText("這是Fragment");
          binding.button.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  Log.d("Fragment", "點擊了按鈕");
              }
          });
      }
    
      @Override
      public void onDestroy() {
          super.onDestroy();
          binding = null;
      }
    
    
  • 如何在Adapter中使用視圖綁定渐夸,首先改造ViewHolder,讓其構造函數(shù)接收FruitItemBinding這個參數(shù)渔欢。但是注意墓塌,ViewHolder的父類RecyclerView.ViewHolder它只會接收View類型的參數(shù),因此我們需要調(diào)用binding.root獲得fruit_item.xml中根元素的實例傳給RecyclerView.ViewHolder。

    class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
    
    inner class ViewHolder(binding: FruitItemBinding) : RecyclerView.ViewHolder(binding.root) {
        val fruitImage: ImageView = binding.fruitImage
        val fruitName: TextView = binding.fruitName
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = FruitItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val fruit = fruitList[position]
        holder.fruitImage.setImageResource(fruit.imageId)
        holder.fruitName.text = fruit.name
    }
    
    override fun getItemCount() = fruitList.size
    
    
  • 如何在自定義組合控件中使用視圖綁定桃纯,直接inflate即可

    class MainTabView : FrameLayout {
    
    private var viewBinding = ItemTabMainBinding.inflate(LayoutInflater.from(context), this, true)
    private lateinit var tabEntity: TabEntity
    
    constructor(context: Context) : this(context, null)
    
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    
    fun getTabProperty() = tabEntity
    
    fun setup(tabEntity: TabEntity) {
        this.tabEntity = tabEntity
        setupInternal()
    }
    
    fun select(select: Boolean) {
        selectInternal(select)
    }
    
    private fun setupInternal() {
        Glide.with(viewBinding.ivTabNormal).load(tabEntity.normalIconRes).into(viewBinding.ivTabNormal)
        Glide.with(viewBinding.ivTabSelected).load(tabEntity.selectedIconRes).into(viewBinding.ivTabSelected)
    }
    
    private fun selectInternal(select: Boolean) {
        viewBinding.ivTabNormal.isVisible = !select
        viewBinding.ivTabSelected.isVisible = select
        viewBinding.vDot.isVisible = select
    }
    
    data class TabEntity(val tab: MainTab, val normalIconRes: Int, val selectedIconRes: Int)
    }
    

使用View Binding 寫的基類

    public class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
      protected T viewBinding;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
          Class cls = (Class) type.getActualTypeArguments()[0];
          try {
              Method inflate = cls.getDeclaredMethod("inflate", LayoutInflater.class);
              viewBinding = (T) inflate.invoke(null, getLayoutInflater());
              setContentView(viewBinding.getRoot());
          } catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) {
              e.printStackTrace();
          }
      }
    }
    //使用
    public class MainActivity extends BaseActivity<ActivityMainBinding> {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            viewBinding.button.setText("這是 MainActivity ViewBinding");
            viewBinding.button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.d("MainView","點擊按鈕");
                }
            });
        }
    }
    public class BaseFragment<T extends ViewBinding> extends Fragment {
        protected T viewBinding;

        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
            Class cls = (Class) type.getActualTypeArguments()[0];
            try {
                Method inflate = cls.getDeclaredMethod("inflate", LayoutInflater.class, ViewGroup.class, boolean.class);
                viewBinding = (T) inflate.invoke(null, inflater, container, false);
            }  catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) {
                e.printStackTrace();
            }
            return viewBinding.getRoot();
        }
    }
    //使用
    public class MainFragment extends BaseFragment<FragmentMainBinding>{
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            viewBinding.button.setText("這是 MainFragment ViewBinding");
            viewBinding.button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.d("MainView","點擊按鈕");
                }
            });
        }
    }
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酷誓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子态坦,更是在濱河造成了極大的恐慌盐数,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伞梯,死亡現(xiàn)場離奇詭異玫氢,居然都是意外死亡,警方通過查閱死者的電腦和手機谜诫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門漾峡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喻旷,你說我怎么就攤上這事生逸。” “怎么了且预?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵槽袄,是天一觀的道長。 經(jīng)常有香客問我锋谐,道長遍尺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任涮拗,我火速辦了婚禮乾戏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘三热。我一直安慰自己鼓择,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布就漾。 她就那樣靜靜地躺著惯退,像睡著了一般。 火紅的嫁衣襯著肌膚如雪从藤。 梳的紋絲不亂的頭發(fā)上催跪,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音夷野,去河邊找鬼懊蒸。 笑死,一個胖子當著我的面吹牛悯搔,可吹牛的內(nèi)容都是我干的骑丸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼通危!你這毒婦竟也來了铸豁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤菊碟,失蹤者是張志新(化名)和其女友劉穎节芥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逆害,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡头镊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了魄幕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片相艇。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖纯陨,靈堂內(nèi)的尸體忽然破棺而出坛芽,到底是詐尸還是另有隱情,我是刑警寧澤翼抠,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布咙轩,位于F島的核電站,受9級特大地震影響机久,放射性物質(zhì)發(fā)生泄漏臭墨。R本人自食惡果不足惜赔嚎,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一膘盖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尤误,春花似錦侠畔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尤勋,卻和暖如春喘落,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背最冰。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工瘦棋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暖哨。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓赌朋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子沛慢,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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