《Android編程權(quán)威指南》之數(shù)據(jù)綁定與MVVM(二)

《Android編程權(quán)威指南》第 19 章第二篇,補充完 BeatBox 應用啦。

第一篇地址:

https://juejin.cn/post/7032485144078319653

六、導入 assets

創(chuàng)建 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 中創(chuàng)建 BeatBox 實例,并調(diào)用 loadSounds() 函數(shù)。

        beatBox = BeatBox(assets)
        beatBox.loadSounds()

運行結(jié)果如下,可以看到已經(jīng)讀到 assets 里的文件。

assets

七、使用 assets

  • 創(chuàng)建 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() 中創(chuàng)建 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)

運行結(jié)果:

使用assets

八、綁定數(shù)據(jù)

  • 創(chuàng)建 SoundViewModel 類并添加綁定函數(shù)。
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>
  • 關聯(lián)使用視圖模型
    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)
        }
  • 綁定數(shù)據(jù)觀察
class SoundViewModel : BaseObservable() {

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

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

調(diào)用 notifyChange(),就是通知綁定類,視圖模型對象上所有可綁定屬性都已更新。

運行結(jié)果:

demo

九、深入學習:數(shù)據(jù)綁定再探

有關數(shù)據(jù)綁定(DataBinding)庫更加深入的介紹請參考:

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

lambda 表達式「布局里面也可以使用 lambda 表達式寫短回調(diào)」

比如給 item 中的 button 添加點擊時間可以寫成:

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

數(shù)據(jù)綁定還有一些方便的語法可用。最方便的一個是使用單引號代替雙引號,它還有 null 自動處理機制。

數(shù)據(jù)綁定默認會把綁定表達式解讀為屬性函數(shù)調(diào)用。

比如要定義一個 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和數(shù)據(jù)綁定

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容