AndroidRecyclerView的基本使用


VIA:
yalinfendou的博客


Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewGroup,并实现了ScrollingView 和 NestedScrollingChild接口,RecyclerView相比ListView,是一次彻底的改变,RecyclerView 比ListView更加强大灵活。

DEMO实现功能:

 


简单的例子: XML中添加RecyclerView:


<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical" />

如果想水平排列显示,把layoutManager.setOrientation(LinearLayoutManager.VERTICAL)替换成layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL)即可。


// 如果布局大小一致有利于优化
        recyclerView.setHasFixedSize(true);
        // 创建一个线性布局管理器
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        // 设置布局管理器
        recyclerView.setLayoutManager(layoutManager);
        // 创建数据集
        List<User> listData = new ArrayList<User>();
        for (int i = 0; i < 20; ++i) {
                User uBean = new User();
                uBean.setUsername("我是Item" + i);
                listData.add(uBean);
        }

        // 创建Adapter,并指定数据集
        MyAdapter adapter = new MyAdapter(context, listData);
        // 设置Adapter
        recyclerView.setAdapter(adapter);
MyAdapter:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MViewHolder> {

        private Context context;
        private List<User> listData;

        public MyAdapter(Context context, List<User> mList) {
                super();
                this.context = context;
                this.listData = mList;
        }

        @Override
        public int getItemCount() {
                // TODO Auto-generated method stub
                return listData.size();
        }

        @Override
        public MViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {

                View view = View.inflate(viewGroup.getContext(),
                                R.layout.item_user_friend_nod, null);
                // 创建一个ViewHolder
                MViewHolder holder = new MViewHolder(view);
                return holder;
        }

        @Override
        public void onBindViewHolder(MViewHolder mViewHolder, int arg1) {

                mViewHolder.mTextView.setText(listData.get(arg1).getUsername());
                mViewHolder.image.setBackgroundResource(R.drawable.head);

        }

        public class MViewHolder extends RecyclerView.ViewHolder {
                public TextView mTextView;
                public ImageView image;

                public MViewHolder(View view) {
                        super(view);
                        this.mTextView = (TextView) view.findViewById(R.id.tv_friend_name);
                        this.image = (ImageView) itemView.findViewById(R.id.img_friend_avatar);
                                        
                }
        }

}
MViewHolder是一个内部类,在其构造函数中,获取控件。 MyAdapter继承了RecyclerView.Adapter<ViewHolder>,并重写了getItemCount(),onCreateViewHolder和onBindViewHolder三个方法。

为RecyclerView的Item及item中的子View添加点击事件

RecyclerView并没有像ListView那样提供OnItemClickListener和OnLongClickListener的回调,为了给RecyclerView添加Onclick监听,需要自己去实现其Onclick监听方法再对外公开。
首先,定义一个接口,并在里面声明3个监听回调函数,分别是Item普通点击监听,Item长按监听和Item内部View点击监听。
/**
 * item点击回调接口
 * 
 * @author wen_er
 * 
 */
public interface ItemClickListener {

        /**
         * Item 普通点击
         */

        public void onItemClick(View view, int postion);

        /**
         * Item 长按
         */

        public void onItemLongClick(View view, int postion);

        /**
         * Item 内部View点击
         */

        public void onItemSubViewClick(View view, int postion);
}
然后再稍稍改造一下MyAdapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MViewHolder> {

        private Context context;
        private List<User> listData;
        private ItemClickListener mItemClickListener;

        public MyAdapter(Context context, List<User> mList) {
                super();
                this.context = context;
                this.listData = mList;
        }

        public void setItemClickListener(ItemClickListener mItemClickListener) {

                this.mItemClickListener = mItemClickListener;
        }

        @Override
        public int getItemCount() {
                // TODO Auto-generated method stub
                return listData.size();
        }

        @Override
        public MViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {

                View view = View.inflate(viewGroup.getContext(),
                                R.layout.item_user_friend_nod, null);
                // 创建一个ViewHolder
                MViewHolder holder = new MViewHolder(view);
                return holder;
        }

        @Override
        public void onBindViewHolder(final MViewHolder mViewHolder,
                        final int postion) {

                mViewHolder.mTextView.setText(listData.get(postion).getUsername());
                mViewHolder.image.setBackgroundResource(R.drawable.head);
                // 为image添加监听回调
                mViewHolder.image.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                if (null != mItemClickListener) {
                                        mItemClickListener.onItemSubViewClick(mViewHolder.image,
                                                        postion);
                                }

                        }

                });

        }

        public class MViewHolder extends RecyclerView.ViewHolder {
                public TextView mTextView;
                public ImageView image;

                public MViewHolder(final View view) {
                        super(view);
                        this.mTextView = (TextView) view.findViewById(R.id.tv_friend_name);
                        this.image = (ImageView) itemView.findViewById(R.id.img_friend_avatar);
                        //为item添加普通点击回调         
                        view.setOnClickListener(new OnClickListener() {

                                @Override
                                public void onClick(View v) {

                                        if (null != mItemClickListener) {
                                                mItemClickListener.onItemClick(view, getPosition());
                                        }

                                }
                        });

                        //为item添加长按回调   
                        view.setOnLongClickListener(new OnLongClickListener() {

                                @Override
                                public boolean onLongClick(View v) {
                                        if (null != mItemClickListener) {
                                                mItemClickListener.onItemLongClick(view, getPosition());
                                        }
                                        return true;
                                }
                        });

                }
        }

}
对比以上的代码,只是在onBindViewHolder中为Item的子View添加监听回调,在MViewHolder的构造方法中为Item添加点击和长按监听回调。 最后,在MainActivity中具体实例化我们的监听事件就OK啦!

//为Item具体实例点击3种事件
                adapter.setItemClickListener(new ItemClickListener() {
                        
                        @Override
                        public void onItemSubViewClick(View view, int postion) {
                                T.showShort(context, "亲,你点击了Image"+postion);
                                
                        }
                        
                        @Override
                        public void onItemLongClick(View view, int postion) {
                                T.showShort(context, "亲,你长按了Item"+postion);
                                
                        }
                        
                        @Override
                        public void onItemClick(View view, int postion) {
                                T.showShort(context, "亲,你点击了Item"+postion);
                                
                        }
                });

为Item之间添加分隔线

如果想要给RecyclerView的Item之间添加分隔线,可以使用addItemDecoration,但如果想图方便,就直接在Item对应的XML中定义就可以了,比如说,像这样(下面的例子只适合Item垂直布局)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/selector_item_action" >

        <TextView
            android:id="@+id/tv_friend_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp"
            android:layout_toRightOf="@+id/img_friend_avatar"
            android:text="test"
            android:textSize="18sp" />

        <ImageView
            android:id="@+id/img_friend_avatar"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentLeft="true"
            android:layout_marginBottom="8dip"
            android:layout_marginLeft="8dip"
            android:layout_marginTop="8dip"
            android:background="@drawable/ic_launcher" />
    </RelativeLayout>

    <!-- 可以添加以下代码为Item之间设置分隔线  -->
   <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_alignParentBottom="true"
        android:background="@drawable/divider_horizontal_line" />  
   

</RelativeLayout>
addItemDecoration的参数ItemDecoration需要我们重写它的onDraw,onDrawOver和getItemOffsets方法,因为item可能是水平排列,也可能是垂直排列,所以我们传入一个参数oritation值,作为item排列方向的标记,参考了Git上的代码,原来的代码当水平布局时,分隔线的高度会填满整个屏幕(Item并未填满整个屏幕),所以稍稍做了改动。

<pre name="code" class="java">public class ItemDecorationDivider extends ItemDecoration {

        private Drawable mDivider;
        private int mOritation;

        public ItemDecorationDivider(Context context, int resId, int oritation) {

                mDivider = context.getResources().getDrawable(resId);
                this.mOritation = oritation;
                Log.i("ItemDecorationDivider", "mOritation=" + mOritation);

        }

        @Override
        public void onDrawOver(Canvas c, RecyclerView parent) {

                if (mOritation == LinearLayoutManager.VERTICAL) {
                        final int left = parent.getPaddingLeft();
                        final int right = parent.getWidth() - parent.getPaddingRight();

                        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;
                                final int bottom = top + mDivider.getIntrinsicHeight();
                                mDivider.setBounds(left, top, right, bottom);
                                mDivider.draw(c);
                        }
                } else if (mOritation == LinearLayoutManager.HORIZONTAL) {

                        final int top = parent.getPaddingTop();
                        // final int bottom = parent.getHeight() -
                        // parent.getPaddingBottom();

                        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;
                                final int right = left + mDivider.getIntrinsicHeight();

                                final int bottom = child.getBottom();
                                mDivider.setBounds(left, top, right, bottom);
                                mDivider.draw(c);
                        }
                }

        }

        @Override
        public void getItemOffsets(Rect outRect, int position,
                        RecyclerView parent) {
                if (mOritation == LinearLayoutManager.VERTICAL) {
                        outRect.set(0, 0, 0, mDivider.getIntrinsicWidth());
                        // outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
                } else if (mOritation == LinearLayoutManager.HORIZONTAL) {
                        // outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
                        outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
                }

        }
}
最主要的方法在onDrawOver中, mDrawable是Item之间分隔的Drawable资源, mDrawable.setBounds(left, top, right, bottom)设置分隔线的绘制范围,再绘制出来 , getItemOffsets为Item设置偏移量,告知RecyclerView需要绘制Item之间分隔线,然后把实现的ItemDivider作为参数传给recyclerView.addItemDecoration。
recyclerView.addItemDecoration(new ItemDecorationDivider(context,
R.drawable.item_divider, LinearLayoutManager.VERTICAL));
item_divider:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <solid android:color="#CCCCCC" />
    <size android:height="1dp" />
</shape>

RecyclerView Item的添加与删除


Adapter提供的的几个常用方法:
</pre><pre name="code" class="java">   /**
         * TODO<添加数据,指定其位置>
         */

        public void addData(User info, int position) {
                listData.add(position, info);
                notifyItemInserted(position);
        //      notifyDataSetChanged(); //不会触发Item的动画效果,告知数据改变,刷新UI
                
        }

        /**
         * TODO<添加数据到最后面添加>
         */

        public void addData(User info) {
                // listData.add(position, info);
                // notifyItemInserted(position);
                listData.add(info);
                notifyDataSetChanged();
        }

        /**
         * TODO<删除数据,指定其位置>
         */
        public void daleteData(int position) {
                listData.remove(position);
                notifyItemRemoved(position);
                
        }

        /**
         * TODO<某一位置开始,有itemCount个Item的数据删除>
         */
        public void itemRangeRemoved(int positionStart, int itemCount) {
                for (int i = positionStart; i < itemCount; i++) {
                        listData.remove(positionStart);
                }
                notifyItemRangeRemoved(positionStart, itemCount);
        //      notifyDataSetChanged(); //不会触发Item的动画效果,告知数据改变,刷新UI
        }

        /**
         * TODO<某一位置开始,有itemCount个Item的数据插入>
         */
        public void itemRangeInserted(User info, int positionStart, int itemCount) {
                for (int i = positionStart; i < itemCount; i++) {
                        listData.add(i, info);
                }
                notifyItemRangeInserted(positionStart, itemCount);
                // notifyDataSetChanged();
        }
直接使用:
case R.id.btn3:
                        User uBean = new User();
                        uBean.setUsername("我是增加的Item");
                        adapter.addData(uBean, 0);// 添加到第一个
                        break;
                case R.id.btn4:
                        adapter.daleteData(0); // 删除第一个
                        break;
                case R.id.btn5:
                        User uBean1 = new User();
                        uBean1.setUsername("我是连续添加的Item");
                        adapter.itemRangeInserted(uBean1, 0, 5);
                        break;
                case R.id.btn6:
                        adapter.itemRangeRemoved(0, 5);
                        break;

为RecyclerView的Item添加动画


在在ListView中,给item添加动画的常用方式是,使用LayoutAnimationController为ViewGroup添加动画,在RecyclerView中,则使用RecyclerView提供的setItemAnimator()方法
// 使用RecyclerView提供的默认的动画效果
2.
recyclerView.setItemAnimator(new DefaultItemAnimator());
这就是我们在效果图看到的动画效果,如果想要其它的动画效果,参见GitHub:https://github.com/gabrielemariotti/RecyclerViewItemAnimators 目前提供了:ScaleInOutItemAnimator,SlideInOutBottomItemAnimator,SlideInOutLeftItemAnimator,SlideInOutRightItemAnimator,SlideInOutTopItemAnimator,SlideScaleInOutRightItemAnimator几种动画效果,使用方法相似。

 

RecyclerView滚动状态监听


RecyclerView提供了setOnScrollListener方法,以便监听屏幕滚动状态。
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
                        @Override
                        public void onScrollStateChanged(RecyclerView recyclerView,
                                        int scrollState) {
                                updateState(scrollState);
                        }

                        @Override
                        public void onScrolled(RecyclerView recyclerView, int i, int i2) {

                                String s = "可见Item数量:" + layoutManager.getChildCount()+"
"
                                                + "可见Item第一个Position:"
                                                + layoutManager.findFirstVisibleItemPosition()+"
"
                                                + "可见Item最后一个Position:"
                                                + layoutManager.findLastVisibleItemPosition();
                                tv.setText(s);
                        }
                });
private void updateState(int scrollState) {
                String stateName = "Undefined";
                switch (scrollState) {
                case SCROLL_STATE_IDLE:
                        stateName = "Idle";
                        break;

                case SCROLL_STATE_DRAGGING:
                        stateName = "Dragging";
                        break;

                case SCROLL_STATE_SETTLING:
                        stateName = "Flinging";
                        break;
                }

                tv_state.setText("滑动状态:" + stateName);
        }
当滚动RecyclerView的时候,效果如DEMO效果图所示。
最后看看LayoutManager,前面说过,LayoutManager是为RecyclerView设置布局管理器的,决定RecyclerView的显示风格。

LinearLayoutManager

前面的代码中,使用 LinearLayoutManager layoutManager = new LinearLayoutManager(this)和 layoutManager.setOrientation(LinearLayoutManager.VERTICAL)创建一个线性布局管理器和设置其布局方向 还可以使用直接以下使用构造方法传入布局方向: LinearLayoutManager(Context context, int orientation, boolean reverseLayout) 第二个参数为布局方向:LinearLayoutManager.HORIZONTAL或者LinearLayoutManager.VERTICAL 第三个参数:true或者false,决定布局是否反向

GridLayoutManager

这是类似GridView的网格布局,三个构造函数:
GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) //可以直接在XMl中设置RecyclerView 属性"layoutManager". GridLayoutManager(Context context, int spanCount) //spanCount为列数,默认方向vertical
GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout)//spanCount为列数,orientation为布局方向,reverseLayout决定布局是否反向。
gridLayoutManager = new GridLayoutManager(this, 3,
                                GridLayoutManager.VERTICAL, false);
                // 设置布局管理器
                recyclerView.setLayoutManager(gridLayoutManager);

StaggeredGridLayoutManager

流式布局,两个构造函数:
StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
StaggeredGridLayoutManager(int spanCount, int orientation) //spanCount为列数,orientation为布局方向

StaggeredGridLayoutManager = new StaggeredGridLayoutManager(2,
                                StaggeredGridLayoutManager.VERTICAL);
                // 设置布局管理器
                recyclerView.setLayoutManager(StaggeredGridLayoutManager);
LinearLayoutManager,GridLayoutManager和StaggeredGridLayoutManager使用方法都类似,直接作为参数传给setLayoutManager就可以了。
关于RecyclerView更深入的用法在后面的博文中介绍。


转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/46636409