美文网首页
四篇文章带你快速入门Jetpck(中)之ViewModel,Da

四篇文章带你快速入门Jetpck(中)之ViewModel,Da

作者: Cache技术分享 | 来源:发表于2021-01-06 06:42 被阅读0次

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

Jetpack

Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。

Android Architecture Component (AAC)

image.png

官方推荐架构

img

请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。

ViewModel

ViewModel应该可以算是JetPack组建中最重要的组件之一。

ViewModel是专门用于存放与界面相关的数据。

ViewModel的生命周期贯穿于Activity整个生命周期,只有Activity销毁时,ViewModel才会销毁。

添加依赖

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上下文的引用的类

image.png

向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没有注册到工厂类")
        }
    }
}

示例

ViewModelActivity

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;

  • 绑定xmldata格式:@{}@={}(双向绑定)

  • ??判空

  • ?:三目运算符

  • @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))
}

项目代码

相关文章

网友评论

      本文标题:四篇文章带你快速入门Jetpck(中)之ViewModel,Da

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