Android ViewModel与DataBinding

MVVM双向数据绑定

    • ViewModel
        • 实现ViewModel
        • ViewModel生命周期
        • 在Frgment之间数据共享
    • DataBinding数据双向绑定

ViewModel

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

安装包版本,如果版本太低的话,下面语句会报错

new ViewModelProvider(this).get(MyViewModel.class);

ViewModel可以解决以下问题

  • Android 框架可以管理界面控制器(如 Activity 和 Fragment)的生命周期。如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。

  • 界面控制器经常需要进行可能需要一些时间才能返回的异步调用。

  • 诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。

实现ViewModel

架构组件为界面控制器提供了 [ViewModel] 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 [ViewModel]对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。例如,如果您需要在应用中显示用户列表,请确保将获取和保留该用户列表的责任分配给 [ViewModel],而不是 Activity 或 Fragment,如以下示例代码所示:

public class MyViewModel extends ViewModel {
    private MutableLiveData<User> user;
    public MutableLiveData<User> getUsers() {
        if (user == null) {
            user = new MutableLiveData<User>();
        }
        return user;
    }
}

然后,您可以从 Activity 访问该列表,如下所示:

public class MainActivity extends AppCompatActivity {
    MyViewModel model;
    TextView name, pwd;
    Button login;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        name = findViewById(R.id.editTextTextPersonName);
        pwd = findViewById(R.id.editTextTextPassword);
        login = findViewById(R.id.button);

        model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            //更新UI视图
            String username = users.getUsername();
            String password = users.getPassword();
            Log.d("aaa", username + ":" + password);
        });

如果重新创建了该 Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同。当所有者 Activity 完成时,框架会调用 [ViewModel] 对象的 [onCleared()]方法,以便它可以清理资源。

ViewModel生命周期

Android ViewModel与DataBinding_第1张图片

您通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 [ViewModel]。系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。[ViewModel]存在的时间范围是从您首次请求 [ViewModel] 直到 Activity 完成并销毁。

在Frgment之间数据共享

Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。想象一下拆分视图 (master-detail) Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。

可以使用 [ViewModel] 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 [ViewModel] 来处理此类通信,如以下示例代码所示:

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(getViewLifecycleOwner(), item -> {
           // Update the UI.
        });
    }
}

请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 [ViewModelProvider] 时,它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。

此方法具有以下优势:

  • Activity 不需要执行任何操作,也不需要对此通信有任何了解。
  • 除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
  • 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。

DataBinding数据双向绑定

定义ViewModel

public class ViewModelWithLiveData extends ViewModel {
    private MutableLiveData<Integer> LikedNumber;

    public MutableLiveData<Integer> getLikedNumber() {
        if (LikedNumber == null) {
            LikedNumber = new MutableLiveData<>();
            LikedNumber.setValue(0);
        }
        return LikedNumber;
    }

    public void add(int n) {
        LikedNumber.setValue(LikedNumber.getValue() + n);
    }
}

在build.gardle添加DataBinding配置

android {
	...
	dataBinding {
        enabled = true
    }
    ...
}

MainActivity进行数据绑定

public class MainActivity extends AppCompatActivity {
    ViewModelWithLiveData viewModelWithLiveData;
    ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //绑定layout组件
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        viewModelWithLiveData = new ViewModelProvider(this).get(ViewModelWithLiveData.class);
        //layout绑定数据
        binding.setNumber(viewModelWithLiveData);
        binding.setLifecycleOwner(this);

    }
}

Layout进行改造


<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="number"
            type="com.suian.androidtest.ViewModelWithLiveData" />
    data>

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

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

        <ImageButton
            android:id="@+id/imageButton3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="80dp"
            android:layout_marginTop="63dp"
            android:contentDescription="@string/img1"
            android:onClick="@{()->number.add(1)}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView"
            app:srcCompat="@drawable/ic_baseline_accessibility_24" />

        <ImageButton
            android:id="@+id/imageButton4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="63dp"
            android:layout_marginEnd="62dp"
            android:contentDescription="@string/img2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView"
            android:onClick="@{()->number.add(-1)}"
            app:srcCompat="@drawable/ic_baseline_directions_run_24" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="52dp"
            android:text="@string/button1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.107"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageButton3" />

    androidx.constraintlayout.widget.ConstraintLayout>
layout>

你可能感兴趣的:(android,学习,识坑笔记,android,ViewModel,DataBinding,MVVM,数据绑定)