《Android编程权威指南》之数据绑定与MVVM(二)

《Android编程权威指南》第 19 章第二篇,补充完 BeatBox 应用啦。

第一篇地址:

https://juejin.cn/post/7032485144078319653

六、导入 assets

创建 BeatBox 类,AssetManager 类可以访问 assets。

class BeatBox(private val assets: AssetManager) {

    fun loadSounds(): List {
        try {
            val soundNames = assets.list(SOUNDS_FOLDER)!!
            Log.d(TAG, "Found ${soundNames.size} sounds")
            return soundNames.asList()
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e(TAG, "Could not list assets", e)
            return emptyList()
        }
    }
}

AssetManager.list(String) 能列出指定目录下的所有文件名,传入声音资源所在的目录,就能看到其中的所有.wav文件。

在 MainActivity 中创建 BeatBox 实例,并调用 loadSounds() 函数。

        beatBox = BeatBox(assets)
        beatBox.loadSounds()

运行结果如下,可以看到已经读到 assets 里的文件。

assets

七、使用 assets

  • 创建 Sound 管理类,使用 String.split(String).last() 分离出文件名,再使用 String.removeSuffix(String) 删除.wav后缀。
private const val WAV = ".wav"

class Sound(val assetPath: String) {
    val name = assetPath.split("/").last().removeSuffix(WAV)
}
  • 在 BeatBox.loadSounds() 中创建 Sound 对象集合。
class BeatBox(private val assets: AssetManager) {

    private val sounds: List

    init {
        sounds = loadSounds()
    }

    fun loadSounds(): List {
        val soundNames: Array

        try {
            soundNames = assets.list(SOUNDS_FOLDER)!!
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e(TAG, "Could not list assets", e)
            return emptyList()
        }

        val sounds = mutableListOf()
        soundNames.forEach { fileName ->
            val assetPath = "$SOUNDS_FOLDER/$fileName"
            val sound = Sound(assetPath)
            sounds.add(sound)
        }
        return sounds
    }
}
  • 绑定 Sound 对象集合

    private inner class SoundAdapter(private val sounds:List):RecyclerView.Adapter(){
        ...
        override fun getItemCount() = sounds.size
    }
  • 传入声音资源(MainActivity.kt)
 adapter = SoundAdapter(beatBox.sounds)

运行结果:

使用assets

八、绑定数据

  • 创建 SoundViewModel 类并添加绑定函数。
class SoundViewModel {

    var sound: Sound? = null
        set(sound) {
            field = sound
        }

    val title: String?
        get() = sound?.name
}
  • 绑定至视图模型



    
        
    

    
  • 关联使用视图模型
    private inner class SoundHolder(private val binding:ListItemSoundBinding):RecyclerView.ViewHolder(binding.root){

        init {
            binding.viewModel = SoundViewModel()
        }
        
        fun bind(sound:Sound){
            binding.apply {
                viewModel?.sound = sound
                executePendingBindings()
            }
        }
    }
...
        override fun onBindViewHolder(holder: SoundHolder, position: Int) {
            val sound = sounds[position]
            holder.bind(sound)
        }
  • 绑定数据观察
class SoundViewModel : BaseObservable() {

    var sound: Sound? = null
        set(sound) {
            field = sound
            notifyChange()
        }

    @get:Bindable
    val title: String?
        get() = sound?.name
}

调用 notifyChange(),就是通知绑定类,视图模型对象上所有可绑定属性都已更新。

运行结果:

demo

九、深入学习:数据绑定再探

有关数据绑定(DataBinding)库更加深入的介绍请参考:

https://developer.android.com/topic/libraries/data-binding

lambda 表达式「布局里面也可以使用 lambda 表达式写短回调」

比如给 item 中的 button 添加点击时间可以写成:

 android:onClick="@{() -> viewModel.onButtonClick()}"

数据绑定还有一些方便的语法可用。最方便的一个是使用单引号代替双引号,它还有 null 自动处理机制。

数据绑定默认会把绑定表达式解读为属性函数调用。

比如要定义一个 app:isGone 属性,基于某个布尔值来设置所有 View 的可见性,可以这么做:

@BindingAdapter("app:isGone")
fun bindIsGone(view: View, isGone: Boolean) {
    view.visibility = if (isGone) View.GONE else View.VISIBLE
}

TextViewBindingAdapter 就为 TextView 提供了一些特别的属性操作。你可以在Android Studio 里看看它们的源码。当然也有搜到 AutoCompleteTextViewBindingAdapter、CheckedTextViewBindingAdapter 这些类,可自行查查看看。

十、深入学习:LiveData和数据绑定

class SoundViewModel{

    val title :MutableLiveData = MutableLiveData()

    var sound: Sound? = null
        set(sound) {
            field = sound
            title.postValue(sound?.name)
        }
}
 private inner class SoundAdapter(private val sounds:List):RecyclerView.Adapter(){

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
            SoundHolder{
                ...
                binding.lifecycleOwner = this@MainActivity
                return SoundHolder(binding)
        }
        ...
    }

其他

BeatBox 项目 Demo 地址:

https://github.com/visiongem/AndroidGuideApp/tree/master/BeatBox

你可能感兴趣的:(《Android编程权威指南》之数据绑定与MVVM(二))