Android-DataBinding使用

1 Nov 2018

最近在学习使用jetpack里的组件,本项目学习DataBinding模块。
DataBinding是在XML中设置要绑定的数据变量,通过ViewModel更新数据。

相关文档:

jetpack官网:
https://developer.android.com/jetpack/
data-binding官方文档:
https://developer.android.com/topic/libraries/data-binding/

学习项目:

https://github.com/pulque/DataBinding

学习说明:

1.ViewModel,负责更新XML文件中的界面数据。
2.ConstraintLayout,约束布局。
3.RecyclerView,列表显示。
4.cardview,卡片样式。

代码流程:

1.ViewModel

ViewModel的不同之处在于界面布局文件XML。
<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="viewmodel"
            type="com.lizheblogs.databinding.ui.main.MainViewModel" />

        <variable
            name="fragment"
            type="com.lizheblogs.databinding.ui.main.MainFragment" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.main.MainFragment">

        <Button
            android:id="@+id/butCrash"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> fragment.onClickCrash()}"
            android:text="崩溃" />

        <Button
            android:id="@+id/but1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> fragment.onClickMain1()}"
            android:text="跳转列表测试"
            app:layout_constraintTop_toBottomOf="@+id/butCrash" />

        <TextView
            android:id="@+id/message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{() -> viewmodel.onClickMessage()}"
            android:text="@{viewmodel.name, default=app}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>

解释:
layout标签建立后,AS会生成一个以XML名+Binding的对象,可以用这个对象直接操作XML中的控件。
data标签中是引用的变量和对象
@{}中绑定的数据或者调用的方法。

MainViewModel中值得一提的是ObservableField
public final ObservableField<String> name;

public MainViewModel() {
    name = new ObservableField<>();
    name.set("123456");
}

public void onClickMessage() {
    name.set("789");
    Log.e("===============", "onClickMessage");
}
解释:
当set的时候,ObservableField会通知UI更新数据。

2.结合RecyclerView

RecyclerView比ListView更加灵活,绑定数据相类似。
需要Adapter和Holder来适配数据。

BaseHolder:
public class BaseHolder extends RecyclerView.ViewHolder {

    private ViewDataBinding mBinding;

    BaseHolder(View v) {
        super(v);
        mBinding = DataBindingUtil.bind(v);
    }

    public void setBinding(int variableId, Object object) {
        mBinding.setVariable(variableId, object);
        mBinding.executePendingBindings();
    }
}
解释:
使用ViewDataBinding绑定数据。

BaseAdapter:
public class BaseAdapter<T> extends 
                    RecyclerView.Adapter<BaseHolder> {
    private List<T> listData;
    private int layoutId;
    private int variableId;

    public BaseAdapter(int layoutId, 
                       int variableId, 
                       List<T> listData) {
        this.listData = listData;
        this.variableId = variableId;
        this.layoutId = layoutId;
    }

    @NonNull
    @Override
    public BaseHolder onCreateViewHolder(@NonNull ViewGroup parent, 
                                         int viewType) {
        return new BaseHolder(LayoutInflater.from(parent.getContext())
                .inflate(layoutId, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull BaseHolder holder, 
                                 int position) {
        holder.setBinding(variableId, listData.get(position));
    }

    @Override
  	  public int getItemCount() {
        return listData.size();
    }
}
解释:
使用ViewDataBinding绑定数据。

ListFragment:
public class ListFragment extends Fragment {

    private ListFragmentBinding binding;

    public static ListFragment newInstance() {
        return new ListFragment();
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, 
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, 
                             R.layout.list_fragment, container, false);
        return binding.getRoot();
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ListViewModel mViewModel = ViewModelProviders.of(this)
                                   .get(ListViewModel.class);
        binding.setViewModel(mViewModel);
        GridLayoutManager linearLayoutManager
                           = new GridLayoutManager(getContext(), 1);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        binding.recyclerView.setLayoutManager(linearLayoutManager);
        binding.recyclerView.setItemAnimator(new DefaultItemAnimator());
        BaseAdapter<Product> productAdapter = new BaseAdapter<>
        (R.layout.list_item, BR.product, mViewModel.getArrayList());
        mViewModel.setAdapter(productAdapter);
        binding.recyclerView.setAdapter(productAdapter);
    }

}
解释:
ViewModel获取Adapter是为了当数据变化的时候,更新界面。

学习结果:

学习用ViewDataBinding绑定数据,用RecyclerView显示列表数据

总结:

ViewModel只是将数据缓存到内存中,退出或崩溃数据不会保留。
RecyclerView的点击事件,在viewHolder.itemView中设置。

进阶:

RecyclerView的交互和数据更新。
Fragment中的逻辑需要进一步分离。
进阶参考:
https://github.com/googlesamples/android-architecture-components/