概述
文章目录
- 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
然后在Activity
的xml
布局文件中引入:
<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 其他
当然,还有很多其余的方式可以实现,比如基于文件类的。可以使用SP
,DataStore
、File
、SQLite
等,甚至可以借助Socket
等方式。这里只是为了引出ViewModel
这种方式,就不再继续展开。
最后
以上就是飘逸饼干为你收集整理的【Android Jetpack】ViewModel——在Fragment之间共享数据1. 前言2. 实现方式的全部内容,希望文章能够帮你解决【Android Jetpack】ViewModel——在Fragment之间共享数据1. 前言2. 实现方式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复