Android 数据绑定 databinding

传统的MVC模式中,通过 findViewById() 初始化UI控件,当页面中的控件比较多时,就得进行重复而繁琐的初始化以及控件变量的管理,这时候就可以使用视图绑定来管理UI控件,省去这些重复的代码,而数据绑定则能够简化业务代码,实现声明式的UI。

使用数据绑定,需要在 build.gradle 中添加如下配置:

plugins {
    ...
    id 'kotlin-kapt'
}
android {
    ...
    dataBinding {
        enabled = true
    }
}

本文提供的例子使用的依赖如下;

dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'
    implementation 'androidx.appcompat:appcompat:1.5.0'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    implementation 'androidx.activity:activity-ktx:1.7.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
}

布局文件也有一些要求,得用 layout 作为根元素,layout 元素只能包含一个视图元素外加一个可选的数据元素,所以可以把原先的根布局放到 layout 元素中,或者新建布局文件时以 layout 作为根元素,这样数据绑定工具会帮助生成一个绑定类(binding class)。新生成的绑定类默认以布局文件命名,再加个Binding后缀。不过,命名格式不是蛇形式命名(snake_case),而是驼峰式命名(CamelCase)。

activity_main.xml




    

        

        

        
    

    

        

        

        

            
        

        

        

然后,就是代码中使用数据绑定了,主要使用 DataBindingUtil 初始化ViewBinding,使用Observable + ViewModel + LiveData 实现数据绑定,代码如下:

MainActivity.kt

package site.feiyuliuxing.databindingtest

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.CompoundButton
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
import site.feiyuliuxing.databindingtest.adapter.JListAdapter
import site.feiyuliuxing.databindingtest.databinding.ActivityMainBinding

class MainViewModel : ObservableViewModel() {
    val isRefreshing = MutableLiveData(false)

    val text = MutableLiveData("123456")
}

val mainHandler = Handler(Looper.getMainLooper())

class MainActivity : AppCompatActivity() {
    private val viewModel by viewModels()
    private val adapter = JListAdapter()

    private val stopRunnable = Runnable {
        adapter.updateItems((1..100).map { "item ${(Math.random() * 256).toInt()}" })
        viewModel.isRefreshing.value = false
    }

    fun startRefresh() {
        println(viewModel.text.value)
        viewModel.isRefreshing.value = true
        mainHandler.postDelayed(stopRunnable, 3000)
    }

    private fun stopRefresh() {
        mainHandler.removeCallbacks(stopRunnable)
        viewModel.isRefreshing.value = false
    }

    fun onRefreshCheckedChange(checkBox: CompoundButton, isChecked: Boolean) {
        if(isChecked) {
            startRefresh()
        } else {
            stopRefresh()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.recyclerView.adapter = adapter
        binding.viewModel = viewModel
        binding.activity = this
        binding.lifecycleOwner = this
    }

    fun update(view: View) {
        val isRefreshing = viewModel.isRefreshing.value ?: false
        if(isRefreshing) {
            stopRefresh()
        } else {
            startRefresh()
        }
    }
}

ObservableViewModel.kt

package site.feiyuliuxing.databindingtest

import androidx.databinding.Observable
import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.ViewModel

open class ObservableViewModel: ViewModel(), Observable {
    private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()

    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        callbacks.add(callback)
    }

    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        callbacks.remove(callback)
    }

    /**
     * Notifies listeners that all properties of this instance have changed.
     */
    fun notifyChange() {
        callbacks.notifyCallbacks(this, 0, null)
    }

    /**
     * Notifies listeners that a specific property has changed. The getter for the property
     * that changes should be marked with [Bindable] to generate a field in
     * `BR` to be used as `fieldId`.
     *
     * @param fieldId The generated BR id for the Bindable field.
     */
    fun notifyPropertyChanged(fieldId: Int) {
        callbacks.notifyCallbacks(this, fieldId, null)
    }
}

JListAdapter.kt

package site.feiyuliuxing.databindingtest.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import androidx.recyclerview.widget.RecyclerView
import site.feiyuliuxing.databindingtest.databinding.ListItemBinding

class JListAdapter : RecyclerView.Adapter() {
    private val items = ArrayList()

    fun updateItems(items: List) {
        this.items.clear()
        this.items.addAll(items)
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JViewHolder {
        val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context))
        val width = parent.context.resources.displayMetrics.widthPixels
        binding.root.layoutParams = ViewGroup.LayoutParams(width, LayoutParams.WRAP_CONTENT)
        return JViewHolder(binding)
    }

    override fun getItemCount(): Int {
        return items.size
    }

    override fun onBindViewHolder(holder: JViewHolder, position: Int) {
        holder.bind(items[position])
    }


    class JViewHolder(private val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: String) {
            binding.tvItemTitle.text = item
        }
    }
}

list_item.xml



    

        

            
        
    

总结:数据绑定基本的使用方式就是 Observable + ViewModel + LiveData,布局文件中声明变量,使用@{} 绑定数据,双向绑定使用@={},代码中初始化 ViewBinding 的时候给布局文件中声明的变量赋值并设置 lifecycleOwner,更多详细的内容参考官方文档《数据绑定使用入门》

你可能感兴趣的:(Android开发,android,ui)