美文网首页
《Android编程权威指南》之数据绑定与MVVM(二)

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

作者: 夜远曦白 | 来源:发表于2021-11-21 14:12 被阅读0次

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

第一篇地址:

https://juejin.cn/post/7032485144078319653

六、导入 assets

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

class BeatBox(private val assets: AssetManager) {

    fun loadSounds(): List<String> {
        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<Sound>

    init {
        sounds = loadSounds()
    }

    fun loadSounds(): List<Sound> {
        val soundNames: Array<String>

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

        val sounds = mutableListOf<Sound>()
        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<Sound>):RecyclerView.Adapter<SoundHolder>(){
        ...
        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
}
  • 绑定至视图模型
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.pyn.beatbox.SoundViewModel" />
    </data>

    <Button
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:text="@{viewModel.title}"
        tools:text="Sound name" />

</layout>
  • 关联使用视图模型
    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<String?> = MutableLiveData()

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

        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(二)

      本文链接:https://www.haomeiwen.com/subject/sgvptrtx.html