美文网首页
《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