Android Jetpack应用指南学习笔记7————LiveData+ViewModel实现Fragment之间的通信
在AndroidJetpack应用指南学习笔记5中LiveData与ViewModel可以结合使用,ViewModel能够将数据从Activity中剥离出来。只要Activity不被销毁,ViewModel会一直存在,并且独立于Activity的配置变化,即旋转屏幕导致的Activity重建,不会影响到ViewModel。
Fragment可以看作是Activity的子页面,即,一个Activity中可以包含多个Fragment,这些Fragment彼此独立,但是又都属于同一个Activity。
基于这些组件的特性,我们可以巧妙地利用ViewModel和LiveData,实现同一个Activity中的不同Fragment间通信。
实现步骤如下:
1.定义ViewModel及LiveData。
/** * @author: njb * @date: 2020/9/2 0002 0:43 * @desc: */ public class MyDataViewModel extends ViewModel { private MutableLiveData<Integer> progress; public LiveData<Integer> getProgress() { if (progress == null) { progress = new MutableLiveData<>(); } return progress; } @Override protected void onCleared() { super.onCleared(); progress = null; } }
2.fragment_one.xml布局代码:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_above="@+id/seekBar" android:text="Fragment_One" android:textColor="@color/colorPrimary"/> <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:max="100" android:layout_centerInParent="true"/> </RelativeLayout>
2.1.fragment_two.xml代码:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_above="@+id/seekBar" android:text="Fragment_two" android:textColor="@color/colorPrimary"/> <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:max="100" android:layout_centerInParent="true"/> </RelativeLayout>
3.OneFragment和TwoFragment实现Fragment间的通信
package com.example.livedataandfragment.fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.example.livedataandfragment.R; import com.example.livedataandfragment.viewmodel.MyDataViewModel; /** * @author: njb * @date: 2020/9/2 0002 0:45 * @desc: */ public class OneFragment extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View parentView = inflater.inflate(R.layout.fragment_one, container, false); final SeekBar seekBar = parentView.findViewById(R.id.seekBar); //注意:这里ViewModelProviders.of(getActivity())这里的参数需要是Activity,而不能是Fragment,否则收不到监听 final MyDataViewModel myDataViewModel = ViewModelProviders.of(getActivity()).get(MyDataViewModel.class); final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) myDataViewModel.getProgress(); //通过observe方法观察ViewModel中字段数据的变化,并在变化时,得到通知 liveData.observe(getActivity(), new Observer<Integer>() { @Override public void onChanged(@Nullable Integer progress) { seekBar.setProgress(progress); } }); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { //用户操作SeekBar时,更新ViewModel中的数据 liveData.setValue(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); return parentView; } }
package com.example.livedataandfragment.fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.example.livedataandfragment.R; import com.example.livedataandfragment.viewmodel.MyDataViewModel; /** * @author: njb * @date: 2020/9/4 0004 1:08 * @desc: */ public class TwoFragment extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View parentView = inflater.inflate(R.layout.fragment_two, container, false); final SeekBar seekBar = parentView.findViewById(R.id.seekBar); //注意:这里ViewModelProviders.of(getActivity())这里的参数需要是Activity,而不能是Fragment,否则收不到监听 final MyDataViewModel myDataViewModel = ViewModelProviders.of(getActivity()).get(MyDataViewModel.class); final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) myDataViewModel.getProgress(); //通过observe方法观察ViewModel中字段数据的变化,并在变化时,得到通知 liveData.observe(getActivity(), new Observer<Integer>() { @Override public void onChanged(@Nullable Integer progress) { seekBar.setProgress(progress); } }); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { //用户操作SeekBar时,更新ViewModel中的数据 liveData.setValue(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); return parentView; } }
4.activity_main.xml代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <fragment android:id="@+id/fragmentOne" android:name="com.example.livedataandfragment.fragment.OneFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/colorPrimary" /> <fragment android:id="@+id/fragmentTwo" android:name="com.example.livedataandfragment.fragment.TwoFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout>
5.实现的效果图如下:
6.小结:可以看到,无论是滑动OneFragment还是TwoFragment中的SeekBar,另外一个Fragment中的SeekBar也会跟着滑动。滑动SeekBar时,通过LiveData.setValue(),修改了ViewModel中LiveData包装的数据(progress字段)。由于Fragment通过LiveData.observe()方法,监听了数据的变化,所以progress字段被修改后,Fragment能够第一事件收到通知,进而更新UI。这就是利用ViewMode和LiveData实现Fragment间通信的原理。另外,从演示图中,我们还能看到,屏幕旋转后SeekBar的进度与旋转前保持一直,数据并未丢失,这也是ViewModel带来的好处之一。