Android Material Design —— RecyclerView

概述

RecyclerView是一个用于在有限的窗口中展示大量数据集的控件。那么,类似的控件当然就有常用的ListViewGridView,那么有了ListViewGridView为什么还需要RecyclerView这样的控件呢?下面就开始一步步了解RecyclerView的不同之处。

依赖

RecyclerViewSupport Library 的一部分,所以使用前需要在 app/build.gradle 中添加依赖

dependencies {
    compile 'com.android.support:recyclerview-v7:25.0.1'
}

结构

要想使用RecyclerView,需要做以下操作:

  • RecyclerView.Adapter – 处理数据集合并负责绑定视图

  • ViewHolder – 持有所有的用于绑定数据或者需要操作的View

  • LayoutManager – 负责摆放视图等相关操作

  • ItemDecoration – 负责绘制Item附近的分割线

  • ItemAnimator – 为Item的一般操作添加动画效果,如,增删条目等

想要在ListView中实现条目的增删动画是一件非常困难的事情,但是RecyclerView为我们提供了很好的便利。而且RecyclerView增强了ViewHolder设计模式,这在当前所使用的ListView中是不曾有的。

与传统ListView比较

RecyclerView与老前辈ListView的不同点,主要在于以下几个特性:

  • Adapter中的ViewHolder模式 – 对于ListView来说,通过创建ViewHolder来提升性能并不是必须的,因为ListView并没有严格的ViewHolder设计模式。但是在使用RecyclerView的时候,Adapter必须实现至少一个ViewHolder,必须遵循ViewHolder设计模式。

  • 定制Item条目 – ListView只能实现垂直线性排列的列表视图,与之不同的是,RecyclerView可以通过设置RecyclerView.LayoutManager来定制不同风格的视图,比如水平滚动列表或者不规则的瀑布流列表。

  • Item动画 – 在ListView中没有提供任何方法或者接口,方便开发者实现Item的增删动画。相反地,可以通过设置RecyclerViewRecyclerView.ItemAnimator来为条目增加动画效果。

  • 设置数据源 – 在LisView中针对不同数据封装了各种类型的Adapter,比如用来处理数组的ArrayAdapter和用来展示Database结果的CursorAdapter。相反地,在RecyclerView中必须自定义实现RecyclerView.Adapter并为其提供数据集合。

  • 设置条目分割线 – 在ListView中可以通过设置android:divider属性来为两个Item间设置分割线。如果想为RecyclerView添加此效果,则必须使用RecyclerView.ItemDecoration,这种实现方式不仅更灵活,而且样式也更加丰富。

  • 设置点击事件 – 在ListView中存在AdapterView.OnItemClickListener接口,用来绑定条目的点击事件。但是,很遗憾的是在RecyclerView中,并没有提供这样的接口,不过,提供了另外一个接口RcyclerView.OnItemTouchListener,用来响应条目的触摸事件。

实战

首先我实现了RecyclerView.Adapter

import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

public class ListAdapter extends RecyclerView.Adapter{
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}

但接下来,我不知道该怎么做了,可能这是习惯了使用ListView吧。而后查阅资料,然后实现了RecyclerView.ViewHolder

import android.support.v7.widget.RecyclerView;
import android.view.View;

public class ListViewHolder extends RecyclerView.ViewHolder{
    public ListViewHolder(View itemView) {
        super(itemView);
    }
}

而这个时候,首先想到的是,这和RecyclerView.Adapter的实现类有什么关联呢?首先看了下源码

public static abstract class Adapter<VH extends ViewHolder> {

发现,RecyclerView.Adapter传递了一个继承RecyclerView.ViewHolder的泛型,那么ListAdapter就应该改成

import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

public class ListAdapter extends RecyclerView.Adapter<ListViewHolder> {
    @Override
    public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(ListViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}

那么将数据集和布局样式都加入进去,就应该是

import android.content.Context;
import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import org.eking.hainancsp.R;
import org.eking.hainancsp.databinding.ItemTaskBinding;
import org.eking.hainancsp.model.TaskListModel;

import java.util.List;
import java.util.Map;

import static org.eking.hainancsp.adapter.TaskListAdapter.TASK_LIST_KEY_DATE;
import static org.eking.hainancsp.adapter.TaskListAdapter.TASK_LIST_KEY_STATUS;
import static org.eking.hainancsp.adapter.TaskListAdapter.TASK_LIST_KEY_TITLE;

public class ListAdapter extends RecyclerView.Adapter<ListViewHolder> {
    private Context context;
    private List<Map> arrData;

    public ListAdapter(Context context) {
        this.context = context;
    }

    public ListAdapter(Context context, List<Map> arrData) {
        this.context = context;
        this.arrData = arrData;
    }

    public void setData(List<Map> data) {
        this.arrData = data;
    }

    @Override
    public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ItemTaskBinding binding = DataBindingUtil.
                inflate(LayoutInflater.from(context), R.layout.item_task, parent, false);
        return new ListViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ListViewHolder holder, int position) {
        Map map = arrData.get(position);
        String date = map.get(TASK_LIST_KEY_DATE).toString();
        String status = map.get(TASK_LIST_KEY_STATUS).toString();
        String title = map.get(TASK_LIST_KEY_TITLE).toString();
        TaskListModel model = new TaskListModel(context, date, status, title);
        holder.getBinding().setModel(model);
    }

    @Override
    public int getItemCount() {
        return arrData == null ? 0 : arrData.size();
    }
}

然后就是ListViewHolder

import android.support.v7.widget.RecyclerView;
import android.view.View;

import org.eking.hainancsp.databinding.ItemTaskBinding;

class ListViewHolder extends RecyclerView.ViewHolder {
    private ItemTaskBinding binding;
    ListViewHolder(View itemView){
        super(itemView);
    }

    ListViewHolder(ItemTaskBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }

    ItemTaskBinding getBinding() {
        return binding;
    }
}

哦,对了,最后就是给RecyclerView设置ListAdapter

    @BindingAdapter(value = {"adapter", "data"}, requireAll = false)
    public static void setTaskListAdapter(RecyclerView recyclerView, ListAdapter adapter, List<Map> data) {
        if (data == null || adapter == null || data.size() == 0) return;
        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
        adapter.setData(data);
        recyclerView.setAdapter(adapter);
    }

接下来运行效果看下

这里写图片描述

这么看来,实现还是蛮简单的!接下来就该添加分割线了,需要实现RecyclerView.ItemDecoration

import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class ListItemDecoration extends RecyclerView.ItemDecoration {
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }
}

额,傻眼了,然后该怎么做?然后继续查阅了下资料,记录下重写的三个方法

  • public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) 装饰的绘制在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡

  • public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) 装饰的绘制在Item条目绘制之后调用,因此装饰将浮于Item之上

  • public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent, RecyclerView.State state) 与padding或margin类似,LayoutManager在测量阶段会调用该方法,计算出每一个Item的正确尺寸并设置偏移量(详细参考这篇文章https://blog.piasy.com/2016/03/26/Insight-Android-RecyclerView-ItemDecoration/

了解了这些,就可以简单地实现分割线了

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class ListItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable divider;
    private int dividerHeight;

    public ListItemDecoration(Context context) {
        TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.listDivider});
        divider = ta.getDrawable(0);
        ta.recycle();
        dividerHeight = 2;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        boolean signed = false;
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        for (int i = 0; i < parent.getChildCount(); i++) {
            View child = parent.getChildAt(i);
            if (!signed && child instanceof ViewGroup) {
                ViewGroup item = (ViewGroup) child;
                for (int j = 0; j < item.getChildCount(); j++) {
                    if (item.getChildAt(j) instanceof ImageView) {
                        ImageView imageView = (ImageView) item.getChildAt(j);
                        left += imageView.getRight();
                        signed = true;
                    }
                }
            }
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            int top = child.getBottom() + params.topMargin;
            int bottom = top + dividerHeight;
            divider.setBounds(left, top, right, bottom);
            divider.draw(c);
        }
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }
}

运行下效果,如下

这里写图片描述

至此,用RecyclerView实现ListView的效果成功了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值