概述

RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 
据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。
那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
  • 你想要控制其显示的方式,请通过布局管理器LayoutManager
  • 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
  • 你想要控制Item增删的动画,请通过ItemAnimator
  • 你想要控制点击、长按事件,请自己写!

RecyclerView基本的使用代码:
    mRecyclerView = findView(R.id.id_recyclerview);
    mRecyclerView.setLayoutManager(layout);//设置布局管理器
    mRecyclerView.setAdapter(adapter)//设置adapter
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());//设置Item增加、移除动画
    mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));//添加分割线  

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

分割线 ItemDecoration

RecyclerView并没有支持divider这样的属性,你可以给Item的布局去设置margin、background等方式来间接添加分割线,但这种方式不够优雅,我们的分割线可以在代码中通过以下方法添加。
    mRecyclerView.addItemDecoration() 
该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类。 
public static abstract class ItemDecoration {
    public void onDraw(Canvas c, RecyclerView parent, State state) {
        onDraw(c, parent);
    }
    public void onDrawOver(Canvas c, RecyclerView parent, State state) {
        onDrawOver(c, parent);
    }
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
        getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
    }
    @Deprecated
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        outRect.set(0, 0, 0, 0);
    }
}

若我们调用mRecyclerView.addItemDecoration()方法添加分割线,则RecyclerView在绘制的时候,会去绘制Decoration,即调用该类的onDraw和onDrawOver方法:
  • onDraw 方法在drawChildren之前
  • onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。
  • getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。

代码 Activity





public class MainActivity extends ActionBarActivity implements MyOnItemClickLitener {
    private RecyclerView mRecyclerView;
    private List<String> mDatas;//数据
    private List<Integer> mHeights;//高度
    private MyRecyclerViewAdapter mRecyclerViewAdapter;//适配器
    private MyStaggeredAdapter mStaggeredAdapter;
    private ItemDecoration decoration1;//分割线
    private ItemDecoration decoration2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mRecyclerView = new android.support.v7.widget.RecyclerView(this);
        setContentView(mRecyclerView);
        initData();
        initAdapter();
        initRecylerView();
    }
    protected void initData() {
        mDatas = new ArrayList<String>();
        for (int i = 'A'; i < 'z'; i++) {
            mDatas.add("" + (char) i);
        }
        mHeights = new ArrayList<Integer>();
        for (int i = 0; i < mDatas.size(); i++) {
            mHeights.add((int) (100 + Math.random() * 500));
        }
    }
    private void initAdapter() {
        mRecyclerViewAdapter = new MyRecyclerViewAdapter(this, mDatas);
        mStaggeredAdapter = new MyStaggeredAdapter(this, mDatas, mHeights);
        mRecyclerViewAdapter.setOnItemClickLitener(this);
        mStaggeredAdapter.setOnItemClickLitener(this);
    }
    private void initRecylerView() {
        decoration1 = new DividerItemDecoration(this, 0);
        decoration2 = new DividerItemDecoration(this, 1);
        mRecyclerView.setPadding(10, 10, 10, 10);
        mRecyclerView.setAdapter(mRecyclerViewAdapter);//设置adapter
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));//设置布局管理器
        mRecyclerView.addItemDecoration(decoration1);//添加一个分割线
        mRecyclerView.addItemDecoration(decoration2);//还可以再添加一个分割线
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());//设置Item增加、移除动画。github上有很多动画效果,如RecyclerViewItemAnimators
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        mRecyclerView.removeItemDecoration(decoration1);//移除分割线,即使没有显示也可以移除
        mRecyclerView.removeItemDecoration(decoration2);
        switch (item.getItemId()) {
        case R.id.add:
            mRecyclerViewAdapter.addData(new Random().nextInt(5));
            break;
        case R.id.delete:
            mRecyclerViewAdapter.removeData(new Random().nextInt(5));
            break;
        //通过RecyclerView去实现ListView、GridView、瀑布流的效果基本上没有什么区别,仅仅通过设置不同的LayoutManager即可实现
        case R.id.listview:
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
            break;
        case R.id.gridview:
            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
            break;
        case R.id.staggeredHorizontalGridView://Staggered:错列的,叉排的。
            mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));//5行
            break;
        case R.id.staggeredVerticalGridview:
            mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));//3列
            break;
        case R.id.staggeredAdapter:
            mRecyclerView.setAdapter(mStaggeredAdapter);
            break;
        case R.id.recyclerViewAdapter:
            mRecyclerView.setAdapter(mRecyclerViewAdapter);
            break;
        }
        return true;
    }
    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(MainActivity.this, position + " 被点击了", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onItemLongClick(View view, int position) {
        Toast.makeText(MainActivity.this, position + "被长按了", Toast.LENGTH_SHORT).show();
    }
}

固定宽高的Adapter

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
    private Context context;
    private List<String> mDatas;
    private MyOnItemClickLitener mOnItemClickLitener;
    public void setOnItemClickLitener(MyOnItemClickLitener mOnItemClickLitener) {
        this.mOnItemClickLitener = mOnItemClickLitener;
    }
    public MyRecyclerViewAdapter(Context context, List<String> datas) {
        this.context = context;
        mDatas = datas;
    }
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false));
    }
    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        holder.tv.setText(mDatas.get(position));
        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.tv.getLayoutParams();
        lp.setMargins(5, 5, 5, 5);//设置边距
        holder.tv.setLayoutParams(lp);
        // 如果设置了回调,则设置点击事件
        if (mOnItemClickLitener != null) {
            holder.itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemClick(holder.itemView, pos);
                }
            });
            holder.itemView.setOnLongClickListener(new OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                    removeData(pos);
                    return false;
                }
            });
        }
    }
    @Override
    public int getItemCount() {
        return mDatas.size();
    }
    /**添加并更新数据,同时具有动画效果*/
    public void addData(int position) {
        mDatas.add(position, "Insert One");
        notifyItemInserted(position);//更新数据集,注意不是用adapter.notifyDataSetChanged(),否则没有动画效果
    }
    /**移除并更新数据,同时具有动画效果*/
    public void removeData(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tv;
        public MyViewHolder(View view) {
            super(view);
            tv = (TextView) view.findViewById(R.id.id_num);
        }
    }
}

随机宽高的Adapter

/**和MyAdapter唯一的区别就是:在代码中动态设置了TextView的高度(宽度)*/
public class MyStaggeredAdapter extends RecyclerView.Adapter<MyStaggeredAdapter.MyViewHolder> {
    private Context context;
    private List<String> mDatas;
    private List<Integer> mHeights;//高度
    private MyOnItemClickLitener mOnItemClickLitener;
    public void setOnItemClickLitener(MyOnItemClickLitener mOnItemClickLitener) {
        this.mOnItemClickLitener = mOnItemClickLitener;
    }
    public MyStaggeredAdapter(Context context, List<String> datas, List<Integer> heights) {
        this.context = context;
        this.mDatas = datas;
        this.mHeights = heights;
    }
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false));
    }
    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.tv.getLayoutParams();
        lp.setMargins(5, 5, 5, 5);
        //横向时,item的宽度需要设置;纵向时,item的高度需要设置
        lp.height = mHeights.get(position);//******************************************************************************************唯一的区别在这里!
        lp.width = mHeights.get(position);//*******************************************************************************************唯一的区别在这里!
        holder.tv.setLayoutParams(lp);
        holder.tv.setText(mDatas.get(position));
        // 如果设置了回调,则设置点击事件
        if (mOnItemClickLitener != null) {
            holder.itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemClick(holder.itemView, pos);
                }
            });
            holder.itemView.setOnLongClickListener(new OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                    removeData(pos);
                    return false;
                }
            });
        }
    }
    @Override
    public int getItemCount() {
        return mDatas.size();
    }
    public void addData(int position) {
        mDatas.add(position, "Insert One");
        mHeights.add((int) (100 + Math.random() * 300));
        notifyItemInserted(position);
    }
    public void removeData(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tv;
        public MyViewHolder(View view) {
            super(view);
            tv = (TextView) view.findViewById(R.id.id_num);
        }
    }
}

点击事件回调接口

/**系统没有提供ClickListener和LongClickListener,我们自己通过接口回调处理 */
public interface MyOnItemClickLitener {
    void onItemClick(View view, int position);
    void onItemLongClick(View view, int position);
}

分割线示例

/**zhy写的分割线,Item如果为最后一列则右边无间隔线,如果为最后一行则底部无分割线*/
public class MyGridItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDivider;
    public MyGridItemDecoration(Context context) {
        //通过读取系统主题中的 Android.R.attr.listDivider属性,将其作为Item间的分割线, <item name="android:listDivider">@drawable/divider_bg</item>  
        TypedArray typedArray = context.obtainStyledAttributes(new int[] { android.R.attr.listDivider });
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();//回收TypedArray,以便后面重用。This TypedArray should be recycled after use with recycle()
    }
    @Override
    /**判断如果是最后一行,则不需要绘制底部;如果是最后一列,则不需要绘制右边,整个判断也考虑到了StaggeredGridLayoutManager的横向和纵向*/
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        int spanCount = getSpanCount(parent);
        int itemCount = parent.getAdapter().getItemCount();
        //使用outRect设置绘制的范围。一般如果仅仅是希望有空隙,还是去设置item的margin方便
        if (isLastRaw(parent, itemPosition, spanCount, itemCount)) {// 如果是最后一行,则不需要绘制底部
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else if (isLastColum(parent, itemPosition, spanCount, itemCount)) {// 如果是最后一列,则不需要绘制右边
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
        }
    }
    @Override
    public void onDraw(Canvas c, RecyclerView parent, State state) {
        drawHorizontal(c, parent);
        drawVertical(c, parent);
    }
    //******************************************************************************************
    public void drawHorizontal(Canvas c, RecyclerView parent) {
        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.getLeft() - params.leftMargin;
            final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    public void drawVertical(Canvas c, RecyclerView parent) {
        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.getTop() - params.topMargin;
            final int bottom = child.getBottom() + params.bottomMargin;
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    //******************************************************************************************
    /**获取RecyclerView有多少列*/
    private int getSpanCount(RecyclerView parent) {
        int spanCount = -1; // 列数
        LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        else if (layoutManager instanceof StaggeredGridLayoutManager) spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
        return spanCount;
    }
    private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) {
        LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            childCount = childCount - childCount % spanCount;
            if (pos >= childCount) // 如果是最后一行,则不需要绘制底部
            return true;
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
            // StaggeredGridLayoutManager 且纵向滚动
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount) return true;// 如果是最后一行,则不需要绘制底部
            } else { // StaggeredGridLayoutManager 且横向滚动
                if ((pos + 1) % spanCount == 0) return true;// 如果是最后一行,则不需要绘制底部
            }
        }
        return false;
    }
    private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {
        LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            if ((pos + 1) % spanCount == 0) {// 如果是最后一列,则不需要绘制右边
                return true;
            }
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                if ((pos + 1) % spanCount == 0) {// 如果是最后一列,则不需要绘制右边
                    return true;
                }
            } else {
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount) // 如果是最后一列,则不需要绘制右边
                return true;
            }
        }
        return false;
    }
}

附件列表

RecyclerView 详解的更多相关文章

  1. android L新控件RecyclerView详解与DeMo[转]

    http://blog.csdn.net/codebob/article/details/37813801 在谷歌的官网我们可以看到它是这样介绍的: RecyclerView  is a more a ...

  2. android listview 替代品recyclerview详解

    安卓v7支持包下的ListView替代品————RecyclerView   RecyclerView这个控件也出来很久了,相信大家也学习的差不多了,如果还没学习的,或许我可以带领大家体验一把这个艺术 ...

  3. RecyclerView详解

    RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,但是直接把viewholder的实现封装起来,用户只要实 ...

  4. 【Android 界面效果47】RecyclerView详解

    RecylerView作为 support-library发布出来,这对开发者来说绝对是个好消息.因为可以在更低的Android版本上使用这个新视图.下面我们看如何获取 RecylerView.首先打 ...

  5. Android RecyclerView使用详解(三)

    在上一篇(RecyclerView使用详解(二))文章中介绍了RecyclerView的多Item布局实现,接下来要来讲讲RecyclerView的Cursor实现,相较于之前的实现,Cursor有更 ...

  6. Android RecyclerView使用详解(二)

    在上一篇(RecyclerView使用详解(一))文章中简单的介绍了RecyclerView的基本用法,接下来要来讲讲RecyclerView的更多用法,要实现不同的功能效果,大部分都还是在于Recy ...

  7. Android 高级UI设计笔记07:RecyclerView 的详解

    1. 使用RecyclerView       在 Android 应用程序中列表是一个非常重要的控件,适用场合非常多,如新闻列表.应用列表.消息列表等等,但是从Android 一出生到现在并没有非常 ...

  8. RecyclerView 缓存机制详解

    一 前言 RecyclerView据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView.GridView.RecyclerView可以用来代替传 ...

  9. ANDROID L——Material Design详解(UI控件)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lolli ...

随机推荐

  1. 批处理备份和恢复mysql数据库

    备份 set "Ymd=%date:~,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%" md "D: ...

  2. Helloworld和程序员人生

    转:Helloworld和程序员人生 高中时期 10 PRINT "HELLO WORLD" 20 END 大学新生 program Hello(input, output) be ...

  3. 移动端web页面使用position:fixed问题总结

    近期完成了一个新的项目(搜狐直播),其中又涉及到了 fixed(固定位置定位)的问题,在之前的文章<移动Web产品前端开发口诀——“快”>中已经阐述过我对 iScroll 的态度,所以在这 ...

  4. 《Javascript模式》之对象创建模式读书笔记

    引言: 在javascript中创建对象是很容易的,可以使用对象字面量或者构造函数或者object.creat.在接下来的介绍中,我们将越过这些方法去寻求一些其他的对象创建模式. 我们知道js是一种简 ...

  5. JQUERY1.9学习笔记 之基本过滤器(六) 页眉选择器

    页眉选择器jQuery( ":header" ) 描述:选择页眉的所有标签,如 h1,h2, h3 等. <!DOCTYPE html><html lang=&q ...

  6. js数组(列表)的基本操作

    本文主要介绍JS对数组(列表)的基本操作.习惯了用数据库的操作顺序来说明:增.删.改.查:合并,裁剪,排序,格式化. 一.数组元素的添加(增加) 增加数组元素有三种方法:unshift()  push ...

  7. Html中input标签的使用

    1.取消按钮按下时的虚线框 在input里添加属性值 hideFocus 或者 HideFocus=true 2.只读文本框内容 在input里添加属性值 readonly 3.防止退后清空的TEXT ...

  8. 卓越网的kindle paperwhite

    卓越网的kindle paperwhite, 899元的价钱,好吸引啊,我是不是也应该买一个呢,从卓越网中看见kindle paperwhite的1代开始,一直想买,等到现在的2代也出了,也继续在考虑 ...

  9. Altium Designer如何批量修改名称,数值,封装

    方法一: altium里的封装管理库 1,Tools -> Footprint Manager -> ...2,在Component List里选择要改的器件3,在View and Edi ...

  10. Android Activity整体管理和关闭工具类封装

    如何彻底退出程序,在任意位置退出程序,如何管理当前的运行栈,知道activity的堆栈结构等,本文封装了一个Activity管理类,可以方便随时退出程序. /** * 应用程序Activity管理类: ...