我是靠谱客的博主 飘逸饼干,最近开发中收集的这篇文章主要介绍【Android Jetpack】ViewModel——在Fragment之间共享数据1. 前言2. 实现方式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 1. 前言
  • 2. 实现方式
    • 2.1 方式一:借助Activity
      • 2.1.1 实现案例
      • 2.1.2 缺点
    • 2.2 方式二:使用本地广播
    • 2.3 方式三:使用ViewModel
    • 2.4 其他

1. 前言

在上篇【Android Jetpack】ViewModel——配置更改保留状态数据中简单使用了ViewModel,结合databinding可以将数据很轻松的展示在UI控件上,而不需要过多的配置、数据状态保存和恢复。而ViewModel能做到的不仅是在设备配置发生改变的时候状态保存,还可以用作Fragment之间的数据共享。可以查阅:官方文档

2. 实现方式

Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。这里在案例中配置两个Fragment,一个Fragment中输入内容,另一个中显示。比如:
在这里插入图片描述
实现也比较简单,也就是创建Fragment然后在Activityxml布局文件中引入:

<fragment
	android:id="@+id/left_fragment"
	android:name="com.weizu.jetpackdemo.LeftFragment"
	android:layout_width="200dp"
	android:layout_height="wrap_content"
	/>

2.1 方式一:借助Activity

2.1.1 实现案例

为了达到预期的效果,那么就需要借助Activity这个中间商,在Activity中监听到LeftFragment的输入事件,然后将结果反馈到RrightFragment中。但由于EditText或者TextView均在Fragment页面中,而不在Activity中。故而就需要借助接口来实现回调。比如下面改造一下LeftFragment的代码:

class LeftFragment : Fragment() {

    private var listener: EditTextInputListener? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val root = inflater.inflate(R.layout.fragment_left, container, false)
        val editText = root.findViewById<EditText>(R.id.left_fragment_edit_text)
        editText.addTextChangedListener(object : TextWatcher{
            override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {}
            override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {}
            override fun afterTextChanged(s: Editable?) {
                listener?.onTextChanged(s)
            }
        })
        return root
    }

    fun setOnTextChangeListener(l: EditTextInputListener){
        this.listener = l;
    }

    interface EditTextInputListener{
        fun onTextChanged(s: Editable?)
    }
}

然后在Activity中为LeftFragment设置监听:

val leftFragment = supportFragmentManager.findFragmentById(R.id.left_fragment) as LeftFragment
val rightFragment = supportFragmentManager.findFragmentById(R.id.right_fragment) as RightFragment

leftFragment.setOnTextChangeListener(object : LeftFragment.EditTextInputListener {
    override fun onTextChanged(s: Editable?) {
        // callback
        rightFragment.setText(s)
    }
})

类似的,在RightFragment暴露设置的方法即可,然后就可以做到两个Fragment之间的通讯。
在这里插入图片描述

2.1.2 缺点

这种方式具有明显的缺点,即当两个Fragment均需要对方的数据的时候,那么就需要这两个Fragment均定义对应的接口,然后再在Activity中进行设置监听接口的示例,以达到数据的共享。

  • 咋一看没啥问题,但是随着Fragment的增多这就无疑不合适了,因为需要在Activity中写对应的多个接口实现以及定义接口;
  • 当所需要传递(共享)的数据增多的时候,通过参数传递的方式不太合适;

2.2 方式二:使用本地广播

Fragment比较多的时候,就可以使用本地广播进行传递数据。需要注意的是:

  • 本地广播需要使用动态注册;

LeftFragment中发送本地广播:

class LeftFragment : Fragment() {

    private val localBroadcastManager by lazy {
        this.context?.let {
            LocalBroadcastManager.getInstance(
                it
            )
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val root = inflater.inflate(R.layout.fragment_left, container, false)
        val editText = root.findViewById<EditText>(R.id.left_fragment_edit_text)
        editText.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {}
            override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {}
            override fun afterTextChanged(s: Editable?) {
                sendBroadcast(s)
            }
        })
        return root
    }

    fun sendBroadcast(s: Editable?) {
        context?.apply {
            localBroadcastManager?.sendBroadcast(Intent().apply {
                this.action = "leftfragment"
                this.putExtra("msg", s.toString())
            })
        }
    }
}

RightFragment中接收广播,注册一个自定义接收器,然后处理:

class RightFragment : Fragment() {

    private var textView: TextView? = null

    private val localBroadcastManager by lazy {
        this.context?.let {
            LocalBroadcastManager.getInstance(
                it
            )
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val root = inflater.inflate(R.layout.fragment_right, container, false)
        textView = root.findViewById(R.id.right_fragment_text_view)

        localBroadcastManager?.registerReceiver(MyReceiver(), IntentFilter().apply {
            this.addAction("leftfragment")
        })
        return root
    }

    inner class MyReceiver: BroadcastReceiver(){
        override fun onReceive(p0: Context?, intent: Intent?) {
            textView?.text = intent?.extras?.get("msg").toString()
        }
    }
}

效果和第一种类似。

2.3 方式三:使用ViewModel

此时在Activity中不需要什么配置,同样的,还是在xml中引入两个Fragment

<fragment
    android:id="@+id/left_fragment"
    android:name="com.weizu.jetpackdemo.LeftFragment"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/guideline2"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<fragment
    android:id="@+id/right_fragment"
    android:name="com.weizu.jetpackdemo.RightFragment"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="@+id/guideline2"
    app:layout_constraintTop_toTopOf="parent" />

然后在对应的LeftFragment以及RightFragment中设置ViewModel以共享数据。首先是LeftFragment的布局文件:

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

    <data>
        <variable
            name="data"
            type="com.weizu.jetpackdemo.MyViewModel" />
    </data>

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

        <EditText
            app:addTextChangedListener="@{ data.textWatcher }"
            android:id="@+id/left_fragment_edit_text"
            android:hint="输入文本"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

LeftFragment中设置databinding,然后返回视图对象:

class LeftFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // databinding到Fragment
        val binding =
            DataBindingUtil.inflate<FragmentLeftBinding>(
                inflater,
                R.layout.fragment_left,
                container,
                false
            )

        val myViewModel = ViewModelProvider(
            requireActivity(),
            ViewModelProvider.NewInstanceFactory()
        ).get(MyViewModel::class.java)

        binding.data = myViewModel
        binding.lifecycleOwner = requireActivity()

        return binding.root
    }
}

RightFragment中同理:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".RightFragment">

        <TextView
            android:id="@+id/right_fragment_text_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Right Fragment" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

然后在RgihtFragment中配置观察:

class RightFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding =
            DataBindingUtil.inflate<FragmentRightBinding>(
                inflater,
                R.layout.fragment_right,
                container,
                false
            )

        val myViewModel = ViewModelProvider(
            requireActivity(),
            ViewModelProvider.NewInstanceFactory()
        ).get(MyViewModel::class.java)

        // 绑定观察
        myViewModel.getInputValue().observe(requireActivity()) {
            binding.rightFragmentTextView.text = myViewModel.getInputValue().value.toString()
        }
        return binding.root
    }
}

运行即可看见和方式一、二同样的效果:
在这里插入图片描述
因为我们虽然在两个Fragment中均获得了ViewModel实例,即:

val myViewModel = ViewModelProvider(
    requireActivity(),
    ViewModelProvider.NewInstanceFactory()
).get(MyViewModel::class.java)

但是,打印其地址可以知道,这两个对象为同一个实例。也就是当这两个Fragment各自获取 ViewModel时,它们会收到相同的 ViewModel实例。而它们均依托于宿主Activity,故而可以完成对LiveData数据的监听和设置。

2.4 其他

当然,还有很多其余的方式可以实现,比如基于文件类的。可以使用SPDataStoreFileSQLite等,甚至可以借助Socket等方式。这里只是为了引出ViewModel这种方式,就不再继续展开。

最后

以上就是飘逸饼干为你收集整理的【Android Jetpack】ViewModel——在Fragment之间共享数据1. 前言2. 实现方式的全部内容,希望文章能够帮你解决【Android Jetpack】ViewModel——在Fragment之间共享数据1. 前言2. 实现方式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部