MVVM实现RecyclerView数据绑定

使用过databing的开发人员,想必对它能够支持自定义属性并不陌生。本文也就是在此基础上,实现对RecyclerView的数据绑定。本文主要涉及到实体类:

  • UserBean:用户信息
  • BindAdapter:自定义Adapter
  • BindRecyclerView:自定义RecyclerView
  • OnRecyclerItemClickListener:事件回调接口
  • RecyclerViewBinding:自定义绑定属性

一、集成

在项目的gradle中配置如下:

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

二、布局文件

1、activity_rv.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="com.xinyartech.mvvm.UserBean"/>
        <import type="androidx.databinding.ObservableList"/>

        <variable
            name="userList"
            type="ObservableList&lt;UserBean>" />

        <variable
            name="presenter"
            type="com.xinyartech.mvvm.DataBindingPresenter" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:id="@+id/load"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="加载更多"
            android:onClick="@{(view)->presenter.loadMore(userList)}"
            />

        <Button
            android:id="@+id/gc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="验证gc"
            />

        <com.xinyartech.mvvm.rv.BindRecyclerView
            android:layout_below="@+id/load"
            android:id="@+id/leftRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutId="@{@layout/adapter_item2}"
            app:list="@{userList}"
            app:itemClickListener="@{(item) -> presenter.itemClick((UserBean)item)}"
            />
    </RelativeLayout>
</layout>
2、adapter_item2.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="com.xinyartech.mvvm.rv.OnRecyclerItemClickListener"/>
        <variable
            name="item"
            type="com.xinyartech.mvvm.UserBean" />

        <variable
            name="itemOneClickListener"
            type="OnRecyclerItemClickListener" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:orientation="vertical">

        <TextView
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.userName}" />

    </LinearLayout>
</layout>

三、UserBean

public class UserBean extends BaseObservable {

    //方式1 BaseObservable+@Bindable+notifyPropertyChanged
    public String userName;


    //方式2 ObservableField
    public ObservableField<String> pwd = new ObservableField<>();

    @Bindable
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
        notifyPropertyChanged(BR.userName);
    }
}

有两种方式进行双向绑定实现

  • 实体类集成BaseObservable,并在getUserName()方法上添加@Bindable注解,在setUserName()方法内,通过notifyPropertyChanged()进行数据更新
  • 使用ObservableField标识属性。
    这里推荐使用第二种,简单易操作。

四、BinderAdapter

代码有点多,先贴出来,一步步理解。

public abstract class BindAdapter<T> extends RecyclerView.Adapter<BindAdapter.BindHolder> {


    private List<T> list = new ArrayList<>();
    private ObservableList<T> observableList;

    private OnRecyclerItemClickListener itemOneClickListener;

    public BindAdapter(final RecyclerView view, ObservableList<T> observableList) {

        this.observableList = observableList;
        this.list.addAll(observableList);

        //添加数据监听
        observableList.addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<T>>() {
            @Override
            public void onChanged(ObservableList<T> sender) {

            }

            @Override
            public void onItemRangeChanged(ObservableList<T> sender, int positionStart,
                                           int itemCount) {
                notifyItemRangeChanged(positionStart,itemCount);
            }

            @Override
            public void onItemRangeInserted(final ObservableList<T> sender, final int positionStart,
                                            final int itemCount) {
                view.post(new Runnable() {
                    @Override
                    public void run() {
                        list.add(positionStart, sender.get(positionStart));
                        notifyItemRangeInserted(positionStart, itemCount);
                        view.scrollToPosition(positionStart);
                    }
                });

            }

            @Override
            public void onItemRangeMoved(ObservableList<T> sender, int fromPosition,
                                         int toPosition, int itemCount) {

            }

            @Override
            public void onItemRangeRemoved(ObservableList<T> sender, final int positionStart, final int itemCount) {
                view.post(new Runnable() {
                    @Override
                    public void run() {
                        if (itemCount == 1) {
                            list.remove(positionStart);
                            notifyItemRangeRemoved(positionStart, itemCount);

                            notifyItemRangeChanged(positionStart, getItemCount() - positionStart, new Object());

                        } else {
                            list.clear();
                            notifyDataSetChanged();
                        }
                    }
                });
            }
        });
    }

    public OnRecyclerItemClickListener getItemOneClickListener() {
        return itemOneClickListener;
    }

    public ObservableList<T> getObservableList() {
        return observableList;
    }

    public void setObservableList(ObservableList<T> observableList) {
        this.observableList = observableList;
    }

    public void setItemOneClickListener(OnRecyclerItemClickListener itemOneClickListener) {
        this.itemOneClickListener = itemOneClickListener;
    }

    public abstract int onCreateViewHolderLayoutId();

    @NonNull
    @Override
    public BindHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ViewDataBinding binding =
                DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                        onCreateViewHolderLayoutId(), parent, false);


        return new BindAdapter.BindHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull BindHolder holder, final int position) {
        holder.bind(this.list.get(holder.getAdapterPosition()),
                new OnRecyclerItemClickListener() {
                    @Override
                    public void onRecyclerItemClick(Object item) {
                        if (itemOneClickListener != null) {

                            itemOneClickListener.onRecyclerItemClick(item);
                        }
                    }
                });

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (itemOneClickListener != null) {
                    itemOneClickListener.onRecyclerItemClick(getItem(position));
                }
            }
        });
    }

    public T getItem(int position) {
        if (position < this.list.size()) {
            return this.list.get(position);
        } else {
            return null;
        }
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    /**
     * 自定义ViewHolder
     */
    public static class BindHolder extends RecyclerView.ViewHolder {

        ViewDataBinding mBinding;

        public BindHolder(ViewDataBinding binding) {
            super(binding.getRoot());
            this.mBinding = binding;
        }

        public void bind(Object obj,
                         OnRecyclerItemClickListener itemOneClickListener) {
            this.mBinding.setVariable(BR.item, obj);
            this.mBinding.setVariable(BR.itemOneClickListener, itemOneClickListener);
            this.mBinding.executePendingBindings();

        }
    }
}

这里将代码分解成以下一个部分:

  • 自定义viewHodler
  • 绑定item布局
  • 添加绑定数据监听
  • 添加item点击事件监听
1、自定义ViewHolder、添加item点击事件
 public static class BindHolder extends RecyclerView.ViewHolder {

        ViewDataBinding mBinding;

        public BindHolder(ViewDataBinding binding) {
            super(binding.getRoot());
            this.mBinding = binding;
        }

        public void bind(Object obj,
                         OnRecyclerItemClickListener itemOneClickListener) {
            this.mBinding.setVariable(BR.item, obj);
            this.mBinding.setVariable(BR.itemOneClickListener, itemOneClickListener);
            this.mBinding.executePendingBindings();

        }
    }

赋值item和条目点击OnRecyclerItemClickListener ,点击事件需要配合holder.itemView.setOnClickListener使用。

2、绑定item布局
@Override
    public BindHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ViewDataBinding binding =
                DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                        onCreateViewHolderLayoutId(), parent, false);


        return new BindAdapter.BindHolder(binding);
    }

其中 onCreateViewHolderLayoutId() == R.layout.adapter_item2.xml

3、添加绑定数据监听

通过设置 observableList.addOnListChangedCallback 监听list数据集合变化,并在回调中处理adapter的数据更新,代码如上。

五、BindRecyclerView

public class BindRecyclerView extends RecyclerView {

    public BindRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public void setOnRecyclerItemClickListener(OnRecyclerItemClickListener onRecyclerItemClickListener) {

        BindAdapter bindAdapter = getBindAdapter();
        if (bindAdapter != null && onRecyclerItemClickListener != null) {
            bindAdapter.setItemOneClickListener(onRecyclerItemClickListener);
        }

    }

    private BindAdapter getBindAdapter() {
        if (getAdapter() instanceof BindAdapter) {
            return (BindAdapter) getAdapter();
        }

        return null;
    }
}

六、OnRecyclerItemClickListener

public interface OnRecyclerItemClickListener {

    void onRecyclerItemClick(Object item);
}

七、RecyclerViewBinding

这里是重点部分,先贴代码

public class RecyclerViewBinding {

    @BindingAdapter(value = {"layoutId", "list","itemClickListener"
    }, requireAll = false)
    public static <T> void setAdapter(BindRecyclerView view,
                                      @LayoutRes final int layoutId,
                                      ObservableList<T> list,
                                      OnRecyclerItemClickListener itemClickListener){
        if (list == null) {
            return;
        }

        if (view.getLayoutManager() == null) {
            // default LinearLayoutManager 垂直布局
            LinearLayoutManager layoutManager = new LinearLayoutManager(
                    view.getContext(), LinearLayoutManager.VERTICAL, false);
            view.setLayoutManager(layoutManager);

            DividerItemDecoration decoration = new DividerItemDecoration(
                    view.getContext(), LinearLayoutManager.VERTICAL);
            //noinspection ConstantConditions 添加分割线
            //decoration.setDrawable(ContextCompat.getDrawable(view.getContext(), R.drawable.divider_gray));

            view.addItemDecoration(decoration);
        }
        //noinspection unchecked 绑定adpter
        BindAdapter<T> bindAdapter = (BindAdapter<T>) view.getAdapter();
        if (bindAdapter == null || !Objects.equals(bindAdapter.getObservableList(), list)) {

            //创建适配器
            bindAdapter = new BindAdapter<T>(view, list) {
                @Override
                public int onCreateViewHolderLayoutId() {
                    return layoutId;
                }
            };
        }

        //设置适配器
        view.setAdapter(bindAdapter);

        //item点击事件
        if (itemClickListener != null) {
            view.setOnRecyclerItemClickListener(itemClickListener);
        }
    }

}

添加@BindingAdapter注解,实现自定义属性layoutId(item布局文件)、list(数据集合)、itemClickListener(条目点击)。

以上就完成了自定义具有databinding功能的RecyclerView。其实最主要的还是自定义属性的使用,@BindingAdapter至关重要,如果需要自定义双向绑定就需要配合@InverseBindingAdapter使用了。

代码地址:

MVVM+RecycleView代码

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现这个功能需要以下步骤: 1. 在ViewModel中定义一个方法,用于请求另一个接口的数据,并将数据添加到列表中。 2. 在RecyclerView的Adapter中,为列表项中的按钮添加点击事件,并在事件处理中调用ViewModel中定义的方法。 3. 在ViewModel中,通过另一个接口获取数据,并将数据添加到列表中。 4. 在获取到数据后,通过LiveData将数据返回给RecyclerView的Adapter,让Adapter更新列表显示。 下面是代码示例: 在ViewModel中定义方法: ```java public class MyViewModel extends ViewModel { private MutableLiveData<List<MyData>> dataList = new MutableLiveData<>(); public LiveData<List<MyData>> getData() { return dataList; } public void loadData() { // 请求另一个接口的数据 List<MyData> newDataList = api.getData(); // 将数据添加到列表中 List<MyData> oldDataList = dataList.getValue(); oldDataList.addAll(newDataList); dataList.setValue(oldDataList); } } ``` 在RecyclerView的Adapter中,为列表项中的按钮添加点击事件: ```java public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private List<MyData> dataList; private MyViewModel viewModel; public MyAdapter(List<MyData> dataList, MyViewModel viewModel) { this.dataList = dataList; this.viewModel = viewModel; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // 创建ViewHolder } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { // 绑定数据 holder.button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewModel.loadData(); } }); } @Override public int getItemCount() { return dataList.size(); } static class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; Button button; public MyViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.text_view); button = itemView.findViewById(R.id.button); } } } ``` 在Activity或Fragment中,将ViewModel与RecyclerView的Adapter关联起来: ```java public class MyFragment extends Fragment { private MyAdapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class); viewModel.loadData(); viewModel.getData().observe(this, new Observer<List<MyData>>() { @Override public void onChanged(List<MyData> dataList) { adapter.notifyDataSetChanged(); } }); adapter = new MyAdapter(viewModel.getData().getValue(), viewModel); } @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); recyclerView.setAdapter(adapter); } } ``` 当用户点击列表项中的按钮时,就会调用ViewModel中的loadData方法,通过另一个接口获取数据,并将数据添加到列表中。获取到数据后,通过LiveData将数据返回给RecyclerView的Adapter,让Adapter更新列表显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值