Kotlin中使用ViewModel的数据共享

本篇将讲述对于使用ViewModel来进行Fragment之间的数据共享,使用的技术为ViewModel,dataBinding

Kotlin中使用ViewModel的数据共享

本篇已Kotlin为主要语言

本篇将讲述对于使用ViewModel来进行Fragment之间的数据共享,使用的技术为ViewModel,dataBinding

依赖

对于ViewModel是属于Androidx包下的JetPack组件,所以只需要项目使用了Androidx,就可直接使用,无需引入额外依赖包

代码教程

在进行编码之前想要做一些准备工作,给项目开启dataBinding,在项目级的build.gradle中,defaultConfig闭包下

1
2
3
4
5
defaultConfig{
...
dataBinding.enabled = true
...
}

这样就开启了dataBinding了

  1. 创建ViewModel

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class MyViewModel : ViewModel() {
    var number: MutableLiveData<Int> = MutableLiveData(0)

    fun get(): MutableLiveData<Int> {
    return number
    }

    fun add(value: Int) {
    number.value = number.value?.plus(value)
    }

    fun set(value: Int) {
    number.value = value
    }
    }

    创建一个继承ViewModel的类,在这个类中创建一个MutableLiveData的变量,并给他设置获取和设置值的方法

  2. 在Activity中获取ViewModel

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    }
    }

    获取ViewModel是通过ViewModelProvider(this).get(刚刚创建的ViewModel::class.java)

    这里原本可以使用ViewModelProviders.of(Activity).get(刚刚创建的ViewModel::class.java),但是ViewModelProviders已经被淘汰了

  3. 接下来就是创建2个Fragment,并在Fragment中获取Activity的ViewModel

    1. BlankFragment

      image-20210104194137046

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    class BlankFragment : Fragment() {
    //这里创建了一个变量,将它的值设置为ActivityViewModel,括号里面只需要输入ViewModel类型即可
    private val viewModel by activityViewModels<MyViewModel>()
    //这个技就是当前项目的dataBinding(lateinit 代表等下进行初始化)
    private lateinit var binding: FragmentBlankBinding

    override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View {
    //在这里进行dataBinding的初始化
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false)
    initView()
    return binding.root
    }

    private fun initView() {
    //将获取完的ViewModel传给dataBinding(是因为xml文件设置了属性)
    binding.data = viewModel
    //设置统一的生命周期管理为父类Activity
    binding.lifecycleOwner = activity
    //点击按钮进行切换Fragment,是通过Navigation进行切换
    binding.button.setOnClickListener {
    val controller: NavController = Navigation.findNavController(it)
    controller.navigate(R.id.action_blankFragment_to_detailFragment)
    }
    //下面的?.let{}代表当前对象不为空的时候才会执行{}内的内容
    viewModel.number.value?.let { binding.seekBar.progress = it }
    binding.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
    override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {//如果插件的值变了
    viewModel.number.value = p1
    }

    override fun onStartTrackingTouch(p0: SeekBar?) {//刚开始触摸的时候
    }

    override fun onStopTrackingTouch(p0: SeekBar?) {//结束触摸
    }

    })
    }
    }

    ​ xml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    <?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">

    <data>

    <variable
    name="data"
    type="com.laboratory.navviewmodel.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
    android:id="@+id/Title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{String.valueOf(data.get())}"
    android:textSize="30sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.222" />

    <SeekBar
    android:id="@+id/seekBar"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="1.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.347" />

    <Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#858A8A"
    android:text="进入"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>

    使用ConstraintLayout是为了方便进行在Design模式下进行摆放控件,上面说的所需的内容是variable中定义的,控件中就可以使用**@{定义的名称.调用方法}**

    1. DetailFragment

      image-20210104194213106

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class DetailFragment : Fragment() {
    private lateinit var binding : FragmentDetailBinding
    private val viewModel by activityViewModels<MyViewModel>()

    override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View {
    binding = DataBindingUtil.inflate(inflater,R.layout.fragment_detail,container,false)
    initView()
    return binding.root
    }

    private fun initView() {
    binding.data = viewModel
    binding.lifecycleOwner = activity
    binding.button.setOnClickListener {
    val controller : NavController = Navigation.findNavController(it)
    controller.navigate(R.id.action_detailFragment_to_blankFragment)
    }
    }
    }

    和上面的Fragment功能差不多,多了两个按钮,进行增加删除

    xml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    <?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="data"
    type="com.laboratory.navviewmodel.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".DetailFragment">

    <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{String.valueOf(data.number)}"
    android:textSize="30sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.192" />

    <Button
    android:id="@+id/add"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="+"
    android:onClick="@{()->data.add(1)}"
    android:textSize="30sp"
    app:layout_constraintBottom_toBottomOf="@+id/reduce"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/reduce"
    app:layout_constraintTop_toTopOf="@+id/reduce"
    app:layout_constraintVertical_bias="0.0" />

    <Button
    android:id="@+id/reduce"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="-"
    android:onClick="@{()->data.add(-1)}"
    android:textSize="30sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/add"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.391" />

    <Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="返回"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.535" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>

    在使用了dataBinding中,连点击事件都可以在xml中设置,只需要想 onClick=”@{()->对象.方法}” 这样去使用

这样全局都是使用统一的ViewModel来获取数据,也就可以进行数据共享