Livedata+viewmodel+Fragment

发布时间 2023-04-23 18:56:08作者: ZZX11
title:LiveData viewmodel 实现Fragment间的通信

使用ViewModel+LiveData实现同一个Activity不同Fragment间的通信。

1.将两个Fragment等比例放置在Activity的布局文件中。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/one_fragment"
        android:name="com.test.mytest.OneFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/two_fragment"
        app:layout_constraintTop_toTopOf="parent" />

    <fragment
        android:id="@+id/two_fragment"
        android:name="com.test.mytest.TwoFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/one_fragment" />

2.定义Livedata和ViewModel

public class ShareDataViewModel 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;
    }
}

3.编写Fragment的代码,实现具体的通信。这里以OneFragment为例,TwoFragment也是类似的代码。

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 ShareDataViewModel shareDataViewModel = ViewModelProviders.of(getActivity()).get(ShareDataViewModel.class);
        final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>)shareDataViewModel.getProgress();
        //通过observe方法观察ViewModel中字段数据的变化,并在变化时,得到通知
        liveData.observe(this, 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);
            }
        });
        return parentView;
    }
}

4.在Fragment的布局文件中放置一个SeekBar控件。

<?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"/>

    <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>

可以看到,无论是滑动OneFragment还是TwoFragment中的SeekBar,另外一个Fragment中的SeekBar也会跟着滑动。滑动SeekBar时,通过LiveData.setValue(),修改了ViewModel中LiveData包装的数据(progress字段)。由于Fragment通过LiveData.observe()方法,监听了数据的变化,所以progress字段被修改后,Fragment能够第一事件收到通知,进而更新UI。这就是利用ViewMode和LiveData实现Fragment间通信的原理。另外,从演示图中,我们还能看到,屏幕旋转后SeekBar的进度与旋转前保持一直,数据并未丢失,这也是ViewModel带来的好处之一。