四篇文章帶你快速入門Jetpck(中)之ViewModel,DataBinding

 

文章目錄

  • 四篇文章帶你快速入門Jetpck(中)之ViewModel,DataBinding
    • Jetpack
      • 官方推薦架構
    • ViewModel
      • 添加依賴
      • 建立ViewModel
      • 初始化ViewModel的方法
      • 生命週期自動感知
      • 向ViewModel傳遞參數
      • 示例
    • DataBinding
      • 添加依賴
      • UI改造
      • UI中關聯xml的DataBinding
      • xml中變量的使用
      • 在RecyclerView中使用DataBinding
      • 高階用法
      • 示例

 

 
四篇文章帶你快速入門Jetpck(中)之ViewModel,DataBinding

Jetpack

Jetpack 是一個由多個庫組成的套件,可幫助開發者遵循最佳作法,減小樣板代碼並編寫可在各類 Android 版本和設備中一致運行的代碼,讓開發者精力集中編寫重要的代碼。java

Android Architecture Component (AAC)。android

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gJ2Z0xIA-1609886408779)(802EFBD516374D929CED25E922A960EC)]

官方推薦架構

img

請注意,每一個組件僅依賴於其下一級的組件。例如,Activity 和 Fragment 僅依賴於視圖模型。存儲區是惟一依賴於其餘多個類的類;在本例中,存儲區依賴於持久性數據模型和遠程後端數據源。後端

ViewModel

ViewModel應該能夠算是JetPack組建中最重要的組件之一。架構

ViewModel是專門用於存放與界面相關的數據。app

ViewModel的生命週期貫穿於Activity整個生命週期,只有Activity銷燬時,ViewModel纔會銷燬。ide

添加依賴

implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01'
implementation 'androidx.activity:activity-ktx:1.2.0-beta01'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.3.0-beta01'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01'

建立ViewModel

class ViewModelOne : ViewModel() {}

初始化ViewModel的方法

val viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class,
{ viewModelStore },
{ defaultViewModelProviderFactory })
val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory }

非懶加載模式必需要等Activity 在OnCreate後才能初始化,不然運行報錯。函數

val viewModelPro = ViewModelProvider(
    viewModelStore,
    defaultViewModelProviderFactory
).get(ViewModelOne::class.java)

生命週期自動感知

onClear回調函數處理資源回收closeWithRuntimeException佈局

ViewModel絕對不能引用View、Lifecycle或任何可能包含對Activity上下文的引用的類gradle

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CqvFO2nU-1609886408782)(D4DE2B861DE54DFD82F41FA30430575B)]

向ViewModel傳遞參數

class VmFactory(private val count: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ViewModelOne::class.java)) {
            return ViewModelOne() as T
        } else if (modelClass.isAssignableFrom(ViewModelTwo::class.java)) {
            return ViewModelTwo(count) as T
        } else {
            throw ClassNotFoundException("class $modelClass ViewModel沒有註冊到工廠類")
        }
    }
}

示例

ViewModelActivityui

class ViewModelActivity : AppCompatActivity() {

    val TAG = this.javaClass.simpleName
    val viewModelOne by ViewModelLazy<ViewModelOne>(ViewModelOne::class,
        { viewModelStore },
        { defaultViewModelProviderFactory })
    val viewModelOnes by viewModels<ViewModelOne> { defaultViewModelProviderFactory }


    //android view model
    val viewModelTwo by viewModels<ViewModelTwo> { VmFactory(10) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)

        val viewModelPro = ViewModelProvider(
            viewModelStore,
            defaultViewModelProviderFactory
        ).get(ViewModelOne::class.java)

        Log.d(TAG, "viewModelOne ${viewModelOne.getNow()}")
        Log.d(TAG, "viewModelOnes ${viewModelOnes.getNow()}")
        Log.d(TAG, "viewModelPro ${viewModelPro.getNow()}")
        Log.d(TAG, "viewModelTwo ${viewModelTwo.getNow()}")
        btn_again.setOnClickListener {
            startActivity(Intent(this, ViewModelActivity::class.java))
        }
    }
}

ViewModelKt

class ViewModelOne : ViewModel() {
    val TAG = this.javaClass.simpleName
    var counter = 0

    init {
        Log.d(TAG, "ViewModelOne建立")
    }

    fun getNow(): String {
        return "ViewModelOne: ${System.currentTimeMillis()}"
    }

    override fun onCleared() {
        super.onCleared()
        Log.d(TAG, "ViewModelOne銷燬")
    }
}

class ViewModelTwo(count: Int) : ViewModel() {
    val TAG = this.javaClass.simpleName
    var counter = 0

    init {
        Log.d(TAG, "ViewModelTwo建立")
        counter = count
    }

    fun getNow(): String {
        return "ViewModelTwo: ${System.currentTimeMillis()}  ${this.counter}"
    }

    override fun onCleared() {
        super.onCleared()
        Log.d(TAG, "ViewModelTwo銷燬")
    }
}

VmFactory

class VmFactory(private val count: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ViewModelOne::class.java)) {
            return ViewModelOne() as T
        } else if (modelClass.isAssignableFrom(ViewModelTwo::class.java)) {
            return ViewModelTwo(count) as T
        } else {
            throw ClassNotFoundException("class $modelClass ViewModel沒有註冊到工廠類")
        }
    }
}

ViewModel.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_again"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="啓動界面" />
        
    </LinearLayout>

</LinearLayout>

DataBinding

添加依賴

//在module的build.gradle中
apply plugin: 'kotlin-kapt'//必須
android{
  //AS 4.0 如下,
  dataBinding{
    enabled true
  }
  //AS 4.1以後
  bindingFeature{
		dataBinding = true
		// for view binding :
		// viewBinding = true
  }
}

UI改造

<layout>
	<data class="">
    
  </data>
  <!-- 原有的UI的xml佈局放在layout標籤內便可。data標籤內存放用於xml的數據變量,類型 -->
  <LinearLayout>
  
  </LinearLayout>
  
</layout>

UI中關聯xml的DataBinding

val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>(
    this,
    R.layout.activity_data_binding
)

xml中變量的使用

  • variable聲明變量;import導入類型;對於xml的特殊符號須要轉義相似&amp;

  • 綁定xml與data格式:@{}、@={}(雙向綁定)

  • ??判空

  • ?:三目運算符

  • @string/str_name資源引用,可用佔位符format

  • +拼接字符,使用**``**反引號

  • default設置默認值

  • include綁定

  • 點擊事件

在RecyclerView中使用DataBinding

implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha06'
<androidx.recyclerview.widget.RecyclerView
    app:adapter="@{adapter}"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    tools:itemCount="5"
    tools:listitem="@layout/item_data_binding" />

高階用法

  1. @BindingConversion

    若是屬性不能匹配類型參數將自動根據類型參數匹配到該註解修飾的方法來轉換

@BindingConversion
fun str2Color(str: String): Drawable {
   return when (str) {
       "red" -> {
           ColorDrawable(Color.RED)
       }
       "blue" -> ColorDrawable(Color.BLUE)
       else -> {
           ColorDrawable(Color.YELLOW)
       }
   }
}
  1. @BindingAdapter

    設置自定義屬性. 能夠覆蓋系統原有屬性

@BindingAdapter("android:textColor", requireAll = false)
fun getColor(view: TextView, type: Int) {
   val color = when (type) {
       0 -> R.color.colorAccent
       1 -> R.color.colorPrimaryDark
       2 -> android.R.color.holo_red_dark
       3 -> android.R.color.holo_orange_dark
       else -> R.color.colorPrimary
   }
   view.setTextColor(view.context.resources.getColor(color))
}
  1. @Bindable

    設置數據刷新視圖. 自動生成BR的ID

    註解纔會自動在build目錄BR類中生成entry, 要求方法名必須以get開頭

示例

DatabindingActivity

class DataBindingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityDataBindingBinding>(
            this,
            R.layout.activity_data_binding
        )
        binding.apply {
            name = null
            address = "TianJin 天津"

            observeName = ObservableField("observeName")

            user = User()
            observeUser = ObserveUser()
            observeFieldUser = ObserveFieldUser()

            btnChange.setOnClickListener {
                name = "姚鑫"
                address = null

                user?.name = "孫悟空"

                observeUser?.age = 18
                observeUser?.name = "張曼玉"

                observeFieldUser?.name?.set("林志玲")
            }

            adapter = DataBindingAdapter()

            info = ItemBean(2,"Activity Item")

        }


    }
}

DatabindingActivity.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="name"
            type="String" />

        <variable
            name="address"
            type="String" />

        <variable
            name="observeName"
            type="androidx.databinding.ObservableField&lt;String>" />

        <variable
            name="user"
            type="com.yx.androidseniorpreparetest.fifth.User" />

        <variable
            name="observeUser"
            type="com.yx.androidseniorpreparetest.fifth.ObserveUser" />

        <variable
            name="observeFieldUser"
            type="com.yx.androidseniorpreparetest.fifth.ObserveFieldUser" />

        <variable
            name="adapter"
            type="androidx.recyclerview.widget.RecyclerView.Adapter" />

        <variable
            name="info"
            type="com.yx.androidseniorpreparetest.fifth.ItemBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="演示DataBinding"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{name}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{name??`Null Name`}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{address,default=`tianjin`}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{name==null?`null`:`nonull`}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{@string/str_name(name)}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{observeName}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@={observeName}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@={user.name}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{observeUser.name + observeUser.age}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@={observeUser.age+``}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@={observeFieldUser.name}"
            android:textColor="#000000"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btn_change"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@{`pink`}"
            android:gravity="center"

            android:text="改變值"
            android:textColor="#000000"
            android:textSize="20sp" />

        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:adapter="@{adapter}"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:itemCount="5"
            tools:listitem="@layout/item_data_binding" />


        <include
            info="@{info}"
            layout="@layout/item_data_binding" />
    </LinearLayout>

</layout>

user

class User {
    var name = "姚鑫"
    var age = 30
    var desc = "DataBinding真好用!"
}

class ObserveUser : BaseObservable() {
    var age = 31
    var name = ""
        set(value) {
            notifyPropertyChanged(BR.name)
            field = value + "yao"
        }
        @Bindable
        get() {
            return "$field 姚鑫Name"
        }
    var desc = "這裏是ObserveUser描述"
        set(value) {
            notifyPropertyChanged(BR.desc)
            field = value + "xin"
        }
        @Bindable
        get() {
            return "$field 姚鑫Desc"
        }
    var str = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.str)
        }
        @Bindable
        get() {
            return "age $age name $name desc $desc"
        }
}

class ObserveFieldUser {
    var name = ObservableField("姚鑫")
    var age = ObservableInt(30)
    var desc = ObservableField("DataBinding真好用!")
    var str = "age $age name $name desc $desc"
}

ItemBean

data class ItemBean(val type: Int, val text: String)

DataBindingAdapter

class DataBindingAdapter : RecyclerView.Adapter<DataBindingAdapter.DataBindingViewHolder>() {
    private val mList = mutableListOf<ItemBean>()

    init {
        for (i in 0..5) {
            mList.add(ItemBean(i, "明細 $i"))
        }
    }

    class DataBindingViewHolder(private val binding: ItemDataBindingBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(bean: ItemBean) {
            binding.info = bean
            binding.executePendingBindings()
        }

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataBindingViewHolder {
        return DataBindingViewHolder(
            ItemDataBindingBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

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

    override fun getItemCount(): Int = mList.size
}

ItemDataBinding.xml

<?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>

        <import type="com.yx.androidseniorpreparetest.fifth.DataBindingUtilsKt" />

        <variable
            name="info"
            type="com.yx.androidseniorpreparetest.fifth.ItemBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{DataBindingUtilsKt.getTitle(info.text)}"
            android:textColor="@{info.type}"
            android:textSize="20sp"
            tools:text="content" />
    </LinearLayout>
</layout>

DataBindingUtils

@BindingConversion
fun str2Color(str: String): Drawable {
    return when (str) {
        "red" -> {
            ColorDrawable(Color.RED)
        }
        "blue" -> ColorDrawable(Color.BLUE)
        else -> {
            ColorDrawable(Color.YELLOW)
        }
    }
}


fun getTitle(type: String): String {
    return "type:$type"
}

@BindingAdapter("android:textColor", requireAll = false)
fun getColor(view: TextView, type: Int) {
    val color = when (type) {
        0 -> R.color.colorAccent
        1 -> R.color.colorPrimaryDark
        2 -> android.R.color.holo_red_dark
        3 -> android.R.color.holo_orange_dark
        else -> R.color.colorPrimary
    }
    view.setTextColor(view.context.resources.getColor(color))
}

項目代碼

相關文章
相關標籤/搜索