RecyclerView知识点串讲

RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。

概述

RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。
据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。

那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。

  • 你想要控制其显示的方式,请通过布局管理器LayoutManager
  • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
  • 你想要控制Item增删的动画,请通过ItemAnimator
  • 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)

基本使用

RecyclerView 与 ListView、GridView 类似,都是可以显示同一种类型 View 的集合的控件。
首先看看最简单的用法,四步走:

  1. 接入 build.gradle 文件中加入

    compile 'com.android.support:recyclerview-v7:24.0.0'
  2. 创建对象

    RecyclerView recyclerview = (RecyclerView) 
    findViewById(R.id.recyclerview);
  3. 设置显示规则

    recyclerview.setLayoutManager(new LinearLayoutManager(
    this, LinearLayoutManager.VERTICAL, false));

    RecyclerView 将所有的显示规则交给一个叫 LayoutManager 的类去完成了。
    LayoutManager 是一个抽象类,系统已经为我们提供了三个默认的实现类,分别是:

    1. LinearLayoutManager
    2. GridLayoutManager
    3. StaggeredGridLayoutManager
      从名字我们就能看出来了,分别是,线性显示、网格显示、瀑布流显示。当然你也可以通过继承这些类来扩展实现自己的 LayougManager。
  4. 设置适配器

    recyclerview.setAdapter(adapter);

    适配器,同 ListView 一样,用来设置每个item显示内容的。
    通常,我们写 ListView 适配器,都是首先继承 BaseAdapter,实现四个抽象方法,创建一个静态 ViewHolder , getView() 方法中判断 convertView 是否为空,创建还是获取 viewholder 对象。
    而 RecyclerView 也是类似的步骤,首先继承RecyclerView.Adapter类,实现三个抽象方法,创建一个静态的 ViewHolder。不过 RecyclerView 的 ViewHolder 创建稍微有些限制,类名就是上面继承的时候泛型中声明的类名(好像反了,应该是上面泛型中的类名应该是这个holder的类名);并且 ViewHolder 必须继承自RecyclerView.ViewHolder类。

相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?
那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。

  • MainActivity.java

    public class MainActivity extends Activity  {
    
    private RecyclerView recyclerView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        //获取RecyclerView控件
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        //设置显示规则
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        //设置适配器
        final MyAdapter myAdapter = new MyAdapter();
        myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickLitener() {
            @Override
            public void onItemClick_Add(View view, List<String> datas, int position) {
                datas.add(position-1, "hello");
                myAdapter.notifyItemInserted(position);
            }
    
            @Override
            public void onItemClick_Delete(View view, List<String> datas, int position) {
                datas.remove(position);
                myAdapter.notifyItemRemoved(position);
            }
        });
        recyclerView.setAdapter(myAdapter);
        //设置分割线
        MyItemDecoration divider = new MyItemDecoration(new ColorDrawable(0xffff0000), OrientationHelper.VERTICAL);
        divider.setMargin(50,50,50,50);
        divider.setHeight(20);
        recyclerView.addItemDecoration(divider);
    }
    }
  • MyAdapter.java

    public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    /**
     * 定义RecyclerView的列表类型
     */
    private static final int TYPE_HEADER = 1;
    private static final int TYPE_ITEM = 2;
    private static final int TYPE_FOOTER = 3;
    
    /**
     * OnItemClick点击事件
     */
    public interface OnItemClickLitener {
        void onItemClick_Add(View view, List<String> datas, int position);
    
        void onItemClick_Delete(View view, List<String> datas, int position);
    }
    
    private OnItemClickLitener mOnItemClickLitener;
    
    public void setOnItemClickListener(OnItemClickLitener onItemClickLitener) {
        mOnItemClickLitener = onItemClickLitener;
    }
    
    private List<String> datas;
    
    public MyAdapter() {
        datas = new ArrayList(2);
        datas.add("01");
        datas.add("02");
        datas.add("03");
        datas.add("04");
        datas.add("05");
    }
    
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        switch (viewType) {
            case TYPE_HEADER:
                return new HeadViewHolder(inflater.inflate(R.layout.layout_recycler_head, parent, false));
            case TYPE_ITEM:
                return new ItemViewHolder(inflater.inflate(R.layout.layout_recycler_item, parent, false));
            case TYPE_FOOTER:
                return new FooterViewHolder(inflater.inflate(R.layout.layout_recycler_footer, null));
        }
        return null;
    }
    
    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof HeadViewHolder) {
            ((HeadViewHolder) holder).mTextView.setText("我是一个Header");
        } else if (holder instanceof FooterViewHolder) {
            ((FooterViewHolder) holder).mTextView.setText("我是一个Footer");
        } else {
            ((ItemViewHolder)holder).txtInfo.setText(datas.get(position-1));
            ((ItemViewHolder) holder).btnAdd.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemClick_Add(v, datas, pos);
                }
            });
            ((ItemViewHolder) holder).btnDelete.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemClick_Delete(v, datas, pos);
                }
            });
        }
    }
    
    @Override
    public int getItemCount() {
        return datas.size() + 2;
    }
    
    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return TYPE_HEADER;
        } else if (position == datas.size() + 1) {
            return TYPE_FOOTER;
        }
        return TYPE_ITEM;
    }
    
    //自定义的ViewHolder,持有每个Item的的所有界面元素
    public static class ItemViewHolder extends RecyclerView.ViewHolder {
        public TextView txtInfo;
        public Button btnAdd;
        public Button btnDelete;
    
        public ItemViewHolder(View view) {
            super(view);
            txtInfo = (TextView) view.findViewById(R.id.txtInfo);
            btnAdd = (Button) view.findViewById(R.id.btnAdd);
            btnDelete = (Button) view.findViewById(R.id.btnDelete);
        }
    }
    
    public static class HeadViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
    
        public HeadViewHolder(View view) {
            super(view);
            mTextView = (TextView) view.findViewById(R.id.txtHeader);
        }
    }
    
    public static class FooterViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
    
        public FooterViewHolder(View view) {
            super(view);
            mTextView = (TextView) view.findViewById(R.id.txtFooter);
        }
    }
    }
  • MyItemDecoration.java

    public class MyItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDivider;
    
    private int leftMargin, rightMargin, topMargin, bottomMargin;
    
    private int width, height;
    
    private int mOrientation;
    
    public MyItemDecoration(Drawable divider, int orientation) {
        setDivider(divider);
        setOrientation(orientation);
    }
    
    private void setDivider(Drawable divider) {
        this.mDivider = divider;
        if (mDivider == null) {
            mDivider = new ColorDrawable(0xffff0000);
        }
        width = mDivider.getIntrinsicWidth();
        height = mDivider.getIntrinsicHeight();
    }
    
    private void setOrientation(int orientation) {
        if (orientation != LinearLayoutManager.HORIZONTAL && orientation != LinearLayoutManager.VERTICAL) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }
    
    public void setMargin(int left, int top, int right, int bottom) {
        this.leftMargin = left;
        this.topMargin = top;
        this.rightMargin = right;
        this.bottomMargin = bottom;
    }
    
    public void setHeight(int height) {
        this.height = height;
    }
    
    public void setWidth(int width) {
        this.width = width;
    }
    
    public int getHeight() {
        return height;
    }
    
    public int getWidth() {
        return width;
    }
    
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            drawHorizontal(c, parent);
        } else {
            drawVertical(c, parent);
        }
    }
    
    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop() + topMargin;
        final int bottom = parent.getHeight() - parent.getPaddingBottom() - bottomMargin;
    
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin + leftMargin;
            final int right = left + width;
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft() + leftMargin;
        final int right = parent.getWidth() - parent.getPaddingRight() - rightMargin;
    
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin + topMargin;
            final int bottom = top + height;
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            outRect.set(0, 0, leftMargin + width + rightMargin, 0);
        } else {
            outRect.set(0, 0, 0, topMargin + height + bottomMargin);
        }
    }
    }
  • layout_recycler_head.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
              android:background="#FFFF00"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/txtHeader"
        android:text="Header"
        android:layout_width="match_parent"
        android:layout_height="175dp"
        android:textColor="#ff000000"
        android:gravity="center"
        android:textSize="20sp" />

</LinearLayout>
  • yout_recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >


    <TextView
        android:id="@+id/txtInfo"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:layout_weight="1"
        android:textColor="#ff000000"
        android:gravity="center"
        android:layout_gravity="center"
        android:text="Hello"
        android:textSize="20sp" />
    <Button
        android:id="@+id/btnAdd"
        android:text="新增"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"/>
    <Button
        android:id="@+id/btnDelete"
        android:text="删除"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"/>
</LinearLayout>
  • layout_recycler_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:background="#FFFF00"
              android:orientation="horizontal">

    <TextView
        android:id="@+id/txtFooter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="Footer"
        android:textColor="#ff000000"
        android:textSize="20sp"/>
</LinearLayout>

RecyclerView显示Item布局不一致

在自定义RecyclerAdapter的时候,在重写onCreateViewHolder方法是使用了

    @Override
    public H onCreateViewHolder(ViewGroup parent, int viewType) {
        View view=View.inflate(context,layoutId,null);
        return view;
    }

进行生成布局,结果发现生成的布局没有LayoutParams。以前自定义View的时候发现,LayoutParams是由于ViewGroup生成的,因为这里添加的ViewGroup为null。所以并不会生成LayoutParams。结果在RecyclerView的getViewForPosition方法中检查了有没有LayoutParams如果没有的话就调用LayoutManager的generateDefaultLayoutParams生成默认的LayoutParames。代码段如下:

final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            final LayoutParams rvLayoutParams;
            if (lp == null) {
                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else if (!checkLayoutParams(lp)) {
                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else {
                rvLayoutParams = (LayoutParams) lp;
            }

而在LinearLayoutManager中generateDefaultLayoutParams方法实现如下。

 /**
     * {@inheritDoc}
     */
    @Override
    public LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

最终会造成RecycleView的显示效果与布局文件不一致。后来使用了LayoutInflater来填充布局。

@Override
    public H onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = mInflater.inflate(layoutId, parent, false);
        return getInstanceOfH(view);
    }

查看LayoutInflater源码发现inflate最后的参数如果是false的话就不会将生成的View添加到parent。但是会根据parent产生相应的LayoutParams 。源码如下:

 * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.

因为在onCreateViewHolder中产生的View不能由我们手动添加到RecycleView中所以最后的参数只能是false;


吐槽

OnItemTouchListener 什么鬼?
用习惯了 ListView 的 OnItemClickListener ,RecyclerView 你的 OnItemClickListener 呢?
Tell me where do I find, something like ListView listener ?

好吧,翻遍了 API 列表,就找到了个 OnItemTouchListener ,这特么什么鬼,我干嘛要对每个 item 监听触摸屏事件。
万万没想到,最终我还是在 Google IO 里面的介绍找到了原因。原来是 Google 的工程师分不清究竟是改给 listview 的 item 添加点击事件,还是应该给每个 item 的 view 添加点击事件,索性就不给 OnItemClickListener 了,然后在 support demo 里面,你就会发现,RecyclerView 的 item 点击事件都是写在了 adapter 的 ViewHolder 里面。

RecyclerView是一个高度自由的控件,它把控件位置的摆放交给LayoutManager去管理,RecyclerView的数据交给Adapter类去管理,RecyclerView的分割线交给RecyclerView.ItemDecoration的实现类去管理(需用户自己实现,系统未给出默认实现类),

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值