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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值