Jetpack系列(八) — Data Bidnding && View Binding

Jetpack系列(八) — Data Bidnding && View Binding

Hilt 简单介绍

初步印象

Data Bidnding , 包含View Binding 所有功能,支持双向绑定

View Binding 仅仅支持绑定 Viewkotlin-android-extensions 插件的替代方案

Data BidndingView Binding可以同时存在

Data Bidnding性能劣于View Binding, 因此一些项目当中仅仅使用View Binding

基本概念

我只打算使用View Binding替代findViewById()Data Bidnding了解即可

基本使用

View Binding 基本使用

  1. viewBinding 元素添加到其 build.gradle 文件中
android {
    ...
    viewBinding {
        enabled = true
    }
}
复制代码
  1. 在 Activity 中使用视图绑定

    • 在 Activity 的 onCreate() 方法,调用生成的绑定类中包含的静态 inflate() 方法
    • binding.控件Id 操作控件
    // MainActivity
    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // 自动生成
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
        }    
    }
    复制代码
  2. 在 Fragment 中使用视图绑定

    • 请在 Fragment 的 onCreateView()方法中执行,调用生成的绑定类中包含的静态 inflate() 方法
    class DeepLinkFragment : Fragment() {
    
        private var _binding: FragmentDeepLinkBinding? = null
        private val binding
            get() = _binding!!
    
        private val mainViewModel: MainViewModel by activityViewModels()
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            _binding = FragmentDeepLinkBinding.inflate(inflater, container, false)
            return binding.root
        }
     
        override fun onDestroy() {
            super.onDestroy()
            _binding = null
        }
    }
    复制代码
  3. 在Adapter中使用ViewBinding

    • 通过绑定类inflate 方法传入ViewHolder
    // WordListAdapter.kt
    class WordListAdapter : ListAdapter<Word, WordViewHolder>(WORDS_COMPARATOR) {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder =
            WordViewHolder.create(parent)
    
        override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
            val item = getItem(position)
            holder.bind(item)
        }
    
        companion object {
            private val WORDS_COMPARATOR = object : DiffUtil.ItemCallback<Word>() {
                override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean =
                    oldItem == newItem
    
                override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean =
                    oldItem.word == newItem.word
    
            }
        }
    
    }
    
    class WordViewHolder(binding: ItemWordBinding) : RecyclerView.ViewHolder(binding.root) {
    
        private val textView: TextView = binding.textView
    
        fun bind(word: Word?) {
            textView.text = word?.word?:""
        }
    
        companion object {
            fun create(parent: ViewGroup): WordViewHolder {
                val binding =
                    ItemWordBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                return WordViewHolder(binding)
            }
        }
    }
    复制代码

Data Binding 基本使用

  1. 修改 build.gradle 文件

    dataBinding {
        enabled = true
    }
    复制代码
  2. 生成data binding布局,参见知识点三

    • 这里我实现一下点击刷新的效果
    • 点击事件onClick="@{()-> model.updateTaps()}"
    <?xml version="1.0" encoding="utf-8"?>
    <layout 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">
    
        <data>
            <variable
                name="model"
                type="com.huang.myapplication.viewmodel.MainViewModel" />
        </data>
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/home_contain"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".ui.main.home.HomeFragment">
     
            <Button
                android:id="@+id/btn_add"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginStart="10dp"
                android:layout_marginTop="10dp"
                android:layout_marginEnd="10dp"
                android:text="@string/refresh"
                android:onClick="@{()-> model.updateTaps()}"
                app:layout_constraintHorizontal_weight="1"
                app:layout_constraintLeft_toRightOf="@+id/btn_next"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    
            <TextView
                android:id="@+id/tv_count"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:text="@{model.taps}"
                app:layout_constraintBottom_toBottomOf="@+id/tv_current_count"
                app:layout_constraintLeft_toRightOf="@+id/tv_current_count"
                app:layout_constraintTop_toTopOf="@+id/tv_current_count" />
    
     
        </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>
    复制代码
  3. viewmodel当中观察数据

    class MainViewModel @Inject constructor(
        private val repository: WordRepository
    ) : ViewModel() {
    
        private var _tapCount = 0
        private var _taps = MutableLiveData<String>("$_tapCount taps")
       
        val taps: LiveData<String> =  _taps
    
        fun updateTaps() {
            _tapCount++
            _taps.value = "$_tapCount taps"
        }
    }
    复制代码
  4. 绑定视图

    • Activity生成binding 视图类

       // 方式一: 
       val binding: ActivityXxxBinding = DataBindingUtil.setContentView(
           this, R.layout.activity_main)
      
       // 方式二: 
       val binding: ActivityXxxBinding = ActivityMainBinding.inflate(getLayoutInflater())
      复制代码
    • FragmentListViewRecyclerView 适配器中使用数据绑定项

      // 方式一:  
      val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
      
      // 方式二: 
      val listItemBinding = DataBindingUtil.inflate(layoutInflater,R.layout.list_item, viewGroup, false)
      复制代码
    • 可以使用 LiveData对象作为数据绑定来源,自动将数据变化通知给界面,需要binding.lifecycleOwner = this 设置视图绑定类的生命周期

      class HomeFragment : Fragment() {
      
          private var _binding: FragmentHomeBinding? = null
          private val binding
              get() = _binding!!
      
          private lateinit var adapter: WordListAdapter
      
          private val mainViewModel: MainViewModel by activityViewModels()
      
          override fun onCreateView(
              inflater: LayoutInflater, container: ViewGroup?,
              savedInstanceState: Bundle?
          ): View {
              _binding = DataBindingUtil.inflate(layoutInflater, R.layout.fragment_home,container, false)
              setHasOptionsMenu(true)
              binding.lifecycleOwner = this
              return binding.root
          }
      
          override fun onDestroy() {
              super.onDestroy()
              _binding = null
          }
      }
      复制代码

相关知识点

知识点一:ViewBinding 忽略文件

  • 一旦启动了ViewBinding功能之后,Android Studio会自动为我们所编写的每一个布局文件都生成一个对应的Binding类

  • Binding类的命名规则是将布局文件按驼峰方式重命名后,再加上Binding作为结尾。

  • 如果有些布局文件你不希望为它生成对应的Binding类,可以在该布局文件的根元素位置加入tools:viewBindingIgnore="true"

    <LinearLayout
        ...
        tools:viewBindingIgnore="true" >
        ...
    </LinearLayout>
    复制代码

知识点二:View Bindinginclude 布局

  • View Binding 使用需要个include 增加id, 通过Id 查找

    <include
             android:id="@+id/includeTitle"
             layout="@layout/include_title" />
    
    // InOutFragment.kt
    binding.includeTitle.toolbarTitle.text = "出入作业"
    复制代码

知识点三:快速生成Data binding 布局

  • alt + Enter 选择cover data to data biding layout

知识点四: 绑定表达式(Data bidng

  • 算术运算符 + - / * %
  • 符串连接运算符 +
  • 逻辑运算符 && ||
  • 二元运算符 & | ^
  • 一元运算符 + - ! ~
  • 移位运算符 >> >>> <<
  • 比较运算符 == > < >= <=(请注意,< 需要转义为 <
  • instanceof
  • 分组运算符 ()
  • 字面量运算符 – 字符、字符串、数字、null
  • 类型转换
  • 方法调用
  • 字段访问
  • 数组访问 []
  • 三元运算符 ?:

知识五: 双向绑定 (Data bidng

  • @={} 表示法(其中重要的是包含“=”符号)可接收属性的数据更改并同时监听用户更新

相关链接

Jetpack系列(一) — Navigation

Jetpack系列(二) — Lifecycle

Jetpack系列(三) — LiveData

Jetpack系列(四) — ViewModel

Jetpack系列(五) — Room

Jetpack系列(六) — Paging3

Jetpack系列(七) — Hilt

参考资料

官网一

官网二

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享