日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Jetpack ViewBinding

發布時間:2023/12/16 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Jetpack ViewBinding 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • Jetpack ViewBinding
    • 概述
    • ViewBinding優點
    • 配置ViewBinding
    • 使用
      • 在Activity中使用
      • 在Fragment中使用
      • 在RecyclerView adapter中使用
      • 在include標簽中使用
        • 不使用merge標簽
        • 使用merge標簽
    • 封裝使用
      • 基類封裝,不使用反射
      • 基類封裝,使用反射
      • 委托實現
    • 源碼分析
    • 代碼下載

Jetpack ViewBinding

概述

官網文檔

通過視圖綁定功能,您可以更輕松地編寫可與視圖交互的代碼。在模塊中啟用視圖綁定之后,系統會為該模塊中的每個 XML 布局文件生成一個綁定類。綁定類的實例包含對在相應布局中具有 ID 的所有視圖的直接引用。

ViewBinding優點

ViewBinding優點

  • Null 安全:由于視圖綁定會創建對視圖的直接引用,因此不存在因視圖 ID 無效而引發 Null 指針異常的風險。
  • 類型安全:每個綁定類中的字段均具有與它們在 XML 文件中引用的視圖相匹配的類型。因此不存強制轉換導致的異常風險。

與findViewById區別

  • findViewById編寫過于冗余。
  • 類型仍然不安全。

與ButterKnife區別

  • 官宣不維護,推薦使用ViewBinding。
  • 類型仍然不安全。
  • 對組件化項目不友好。

與Kotlin Android Extensions

  • JetBrains廢棄該插件。
  • 類型仍然不安全。
  • 性能偏低。

配置ViewBinding

Android Studio3.6以上

android {viewBinding {enabled = true} }

Android Studio4.0以上

android {buildFeatures {viewBinding = true} }

如果需要忽略某個布局文件,需要添加tools:viewBindingIgnore="true"屬性到布局中

<LinearLayout...tools:viewBindingIgnore="true" >... </LinearLayout>

使用

當開啟ViewBinding后,系統會為該模塊中每個XML布局文件生成一個綁定類(轉換為駝峰命名并在末尾添加Binding),每個綁定類均包含根視圖已交具有id的所有視圖的引用。

例如:布局文件名為activity_main.xml,生成綁定類為ActivityMainBinding

在Activity中使用

使用流程

  • 開啟視圖綁定功能后,系統會為該模塊中的XML布局生成一個綁定類,每個綁定類都包含根布局和具有ID布局的引用。
  • 調用綁定類的inflate()方法獲取綁定類對象。
  • 調用綁定類對象的getRoot()方法獲取根布局傳遞到setContentView()。

XML布局

activity_view_binding_simple.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/tv_age"android:layout_width="wrap_content"android:layout_height="wrap_content" /><ImageViewandroid:id="@+id/iv_avatar"android:layout_width="wrap_content"android:layout_height="wrap_content" /><Buttonandroid:id="@+id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="確定" /> </LinearLayout>

Activity類

class ViewBindingSimpleActivity : BaseActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val viewBinding = ActivityViewBindingSimpleBinding.inflate(layoutInflater)setContentView(viewBinding.root)viewBinding.tvName.text = "hello world"viewBinding.tvAge.text = 18.toString()viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}} }

在Fragment中使用

方式一

  • 調用綁定類的inflate()方法,獲取綁定類對象。
  • 再調用getRoot()方法獲取根布局。
  • 在onCreateView()方法返回根布局,使其成為屏幕上的活動視圖。
  • 由于Fragment的存在時間比視圖長。因此需要在Fragment的onDestroyView()方法中清除對綁定類對象的所有引用。
class MyFragment : BaseFragment() {private var _viewBinding: FragmentMyBinding? = nullprivate val viewBindingget() = _viewBinding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {_viewBinding = FragmentMyBinding.inflate(inflater, container, false)return viewBinding.root}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewBinding.tvName.text = "hello world"viewBinding.tvAge.text = 18.toString()viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}override fun onDestroyView() {super.onDestroyView()_viewBinding = null} }

方式二

class MyFragment : BaseFragment() {private var _viewBinding: FragmentMyBinding? = nullprivate val viewBindingget() = _viewBinding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {return inflater.inflate(R.layout.fragment_my, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)_viewBinding = FragmentMyBinding.bind(view)viewBinding.tvName.text = "hello world"viewBinding.tvAge.text = 18.toString()viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}override fun onDestroyView() {super.onDestroyView()_viewBinding = null} }

在RecyclerView adapter中使用

class MyAdapter(context: Context, private val data: ArrayList<String>) :RecyclerView.Adapter<MyAdapter.ViewHolder>() {val layoutInflater: LayoutInflater = LayoutInflater.from(context)class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {val text: TextView = itemView.findViewById(R.id.text)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val viewBinding = ItemTextBinding.inflate(layoutInflater, parent, false)return ViewHolder(viewBinding.root)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.text.text = data[position]}override fun getItemCount(): Int {return data.size} }

在include標簽中使用

ViewBinding可以與<include>標簽一起使用

不使用merge標簽

  • 一定要給<include>標簽定義id,使用該id訪問布局中的控件。

XML布局

title_bar.xml

<?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:background="#2196F3"android:minHeight="50dp"android:padding="10dp"><TextViewandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:text="返回" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="標題位置" /><TextViewandroid:id="@+id/confirm"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:text="確定" /></RelativeLayout>

Activity的XML布局

activity_vbinclude.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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=".IncludeActivity"><includeandroid:id="@+id/titleBar"layout="@layout/titlebar" /></LinearLayout>

在include標簽中使用

public class IncludeActivity extends AppCompatActivity {private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);context = this;ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());binding.titleBar.title.setText("這是一個標題");binding.titleBar.back.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "返回", Toast.LENGTH_SHORT).show();}});binding.titleBar.confirm.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "確定", Toast.LENGTH_SHORT).show();}});} }

使用merge標簽

  • <merge>標簽有利于減少布局層次。
  • 需要使用bind()方法綁定根視圖。
  • 不能給<include>標簽設置id。

布局:detail_layout.xml

<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"><ImageViewandroid:id="@+id/ivDetail"android:layout_width="100dp"android:layout_height="100dp"android:scaleType="fitXY" /> </merge>

Activity的XML布局

<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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"android:orientation="vertical"tools:context=".IncludeActivity"><include layout="@layout/detail_layout" /></LinearLayout>

在include標簽中使用

package com.example.viewbindingdemo;import android.content.Context; import android.os.Bundle; import android.view.View; import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.example.viewbindingdemo.databinding.ActivityIncludeBinding; import com.example.viewbindingdemo.databinding.DetailLayoutBinding;public class IncludeActivity extends AppCompatActivity {private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);context = this;ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());DetailLayoutBinding detailBinding = DetailLayoutBinding.bind(binding.getRoot());detailBinding.ivDetail.setImageResource(R.mipmap.ic_launcher);} }

封裝使用

基類封裝,不使用反射

基類封裝

abstract class BindingActivity<VB : ViewBinding> : BaseActivity() {private lateinit var _viewBinding: VBprotected val mViewBinding get() = _viewBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)_viewBinding = getViewBinding()setContentView(_viewBinding.root)}abstract fun getViewBinding(): VB }abstract class BindingFragment<VB : ViewBinding> : BaseFragment() {private lateinit var _viewBinding: VBprotected val mViewBinding get() = _viewBindingoverride fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {_viewBinding = getViewBinding(inflater, container)return _viewBinding.root}abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB }

使用

class OneActivity : BindingActivity<ActivityOneBinding>() { override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 18.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}override fun getViewBinding(): ActivityOneBinding {return ActivityOneBinding.inflate(layoutInflater)} }class OneFragment : BindingFragment<FragmentOneBinding>() {override fun getViewBinding(inflater: LayoutInflater,container: ViewGroup?): FragmentOneBinding {return FragmentOneBinding.inflate(inflater, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 18.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}} }

基類封裝,使用反射

基類封裝

abstract class BindingActivity<VB : ViewBinding> : BaseActivity() {private lateinit var _viewBinding: VBprotected val mViewBinding get() = _viewBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mContext = thisval type = javaClass.genericSuperclassif (type is ParameterizedType) {val clz = type.actualTypeArguments[0] as Class<*>val method = clz.getMethod("inflate", LayoutInflater::class.java)_viewBinding = method.invoke(null, layoutInflater) as VBsetContentView(_viewBinding.root)}} }abstract class BindingFragment<VB : ViewBinding> : BaseFragment() {private var _viewBinding: VB? = nullprotected val mViewBinding get() = _viewBinding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {val type = javaClass.genericSuperclassif (type is ParameterizedType) {val clz = type.actualTypeArguments[0] as Class<*>val method = clz.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)_viewBinding = method.invoke(null, inflater, container, false) as VB}return mViewBinding.root}override fun onDestroyView() {super.onDestroyView()_viewBinding = null} }

使用

class TwoActivity : BindingActivity<ActivityTwoBinding>() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 28.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello2", Toast.LENGTH_SHORT).show()}} }class TwoFragment : BindingFragment<FragmentTwoBinding>() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 28.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello2", Toast.LENGTH_SHORT).show()}} }

委托實現

封裝

//Activity ViewBindinginline fun <reified VB : ViewBinding> ComponentActivity.viewBindings(noinline factory: (LayoutInflater) -> VB,setContentView: Boolean = true ) = ActivityViewBindingDelegate1(factory, setContentView)inline fun <reified VB : ViewBinding> ComponentActivity.viewBindings(setContentView: Boolean = true) =ActivityViewBindingDelegate2(VB::class.java, setContentView)class ActivityViewBindingDelegate1<VB : ViewBinding>(private val factory: (LayoutInflater) -> VB,private val setContentView: Boolean, ) : ReadOnlyProperty<ComponentActivity, VB> {private var viewBinding: VB? = nulloverride fun getValue(thisRef: ComponentActivity, property: KProperty<*>): VB {viewBinding?.let { return it }viewBinding = factory(thisRef.layoutInflater).also { viewBinding ->if (setContentView) thisRef.setContentView(viewBinding.root)}return viewBinding!!} }class ActivityViewBindingDelegate2<VB : ViewBinding>(private val clazz: Class<VB>,private val setContentView: Boolean, ) : ReadOnlyProperty<ComponentActivity, VB> {private var viewBinding: VB? = nulloverride fun getValue(thisRef: ComponentActivity, property: KProperty<*>): VB {viewBinding?.let { return it }val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)viewBinding =(inflateMethod.invoke(null, thisRef.layoutInflater) as VB).also { viewBinding ->if (setContentView) thisRef.setContentView(viewBinding.root)}return viewBinding!!} } //Fragment ViewBindinginline fun <reified VB : ViewBinding> Fragment.viewBindings(noinline factory: (View) -> VB) =FragmentViewBindingDelegate1(factory)inline fun <reified VB : ViewBinding> Fragment.viewBindings() =FragmentViewBindingDelegate2(VB::class.java)class FragmentViewBindingDelegate1<VB : ViewBinding>(private val factory: (View) -> VB, ) : ReadOnlyProperty<Fragment, VB> {private var viewBinding: VB? = nulloverride fun getValue(thisRef: Fragment, property: KProperty<*>): VB {viewBinding?.let { return it }val lifecycle = thisRef.viewLifecycleOwner.lifecycleviewBinding = factory(thisRef.requireView())if (lifecycle.currentState == Lifecycle.State.DESTROYED) {Log.w("TAG","Access to viewBinding after Lifecycle is destroyed or hasn't created yet. The instance of viewBinding will be not cached.")} else {thisRef.viewLifecycleOwnerLiveData.observe(thisRef) { viewLifecycleOwner ->viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {override fun onDestroy(owner: LifecycleOwner) {viewBinding = null}})}}return viewBinding!!} }class FragmentViewBindingDelegate2<VB : ViewBinding>(clazz: Class<VB>, ) : ReadOnlyProperty<Fragment, VB> {private var viewBinding: VB? = nullprivate val bindMethod = clazz.getMethod("bind", View::class.java)override fun getValue(thisRef: Fragment, property: KProperty<*>): VB {viewBinding?.let { return it }val lifecycle = thisRef.viewLifecycleOwner.lifecycleviewBinding = bindMethod.invoke(null, thisRef.requireView()) as VBif (lifecycle.currentState == Lifecycle.State.DESTROYED) {Log.w("TAG","Access to viewBinding after Lifecycle is destroyed or hasn't created yet. The instance of viewBinding will be not cached.")} else {thisRef.viewLifecycleOwnerLiveData.observe(thisRef) { viewLifecycleOwner ->viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {override fun onDestroy(owner: LifecycleOwner) {viewBinding = null}})}}return viewBinding!!} }

使用

class ThreeActivity : BaseActivity() {//方式一private val viewBinding: ActivityThreeBinding by viewBindings(ActivityThreeBinding::inflate)//方式二// private val viewBinding: ActivityThreeBinding by viewBindings()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.tvName.text = "小白"viewBinding.tvAge.text = 38.toString()viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello3", Toast.LENGTH_SHORT).show()}} }class ThreeFragment : BaseFragment(R.layout.fragment_three) {//方式一 // private val viewBinding: FragmentThreeBinding by viewBindings(FragmentThreeBinding::bind)//方式二private val viewBinding: FragmentThreeBinding by viewBindings()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.tvName.text = "小白33"viewBinding.tvAge.text = 338.toString()viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello33", Toast.LENGTH_SHORT).show()}} }

源碼分析

代碼下載

總結

以上是生活随笔為你收集整理的Jetpack ViewBinding的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。