RecyclerView使用

转载 http://blog.csdn.net/yalinfendou/article/details/46636409yalinfendou的博客


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

DEMO实现功能:

  • RecyclerView的点击事件: Item及item中的子View添加点击事件
  • RecyclerView Item之间添加分隔线:垂直与水平方向
  • RecyclerView 单个与多个Item的添加与删除
  • RecyclerView Item添加与删除动画效果
  • RecyclerView滚动状态监听
  • LayoutManager的使用

DEMO效果图:




RecyclerView的相关的LayoutManager ItemDecoration 和 ItemAnimator

  • LayoutManager这个是为RecyclerView设置布局管理器的,决定RecyclerView的显示风格,它有两个直接子类:LinearLayoutManager 和 StaggeredGridLayoutManager,还有一个间接子类GridLayoutManager,GridLayoutManager继承LinearLayoutManager 。线性布局管理器 LinearLayoutManager 的布局像ListView显示多列,可以直接设置其布局方向(垂直或水平),网格布局管理器 GridLayoutManager像Gridview那样显示多行多列,而StaggeredGridLayoutManager则可以实现流式布局
  • ItemDecoration:在Item之间设置分隔线和偏移,提供了三个方法:getItemOffsets,onDraw,onDrawOver。ItemDecoration的绘制是有一定的顺序的,onDraw的绘制在Item视图绘制之前,onDrawOver 的绘制在Item视图绘制之后,并将其绘制的显示在视图之上,getItemOffsets为Item设置偏移量。如果你只是想实现简单的Item之间的分割线的话,可以直接在item的XML文件中直接定义就可以了。
  • ItemAnimator用来设置item的添加或者删除的动画风格,默认的动画是DefaultItemAnimator,也可以自己设置,继承RecyclerView.ItemAnimator,然后重写animateAdd,animateMove等方法就可以了。     
      来看看一个简单的例子: 
      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);  
}  

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并未 填满整个屏幕 ),所以稍稍做了改动。
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)); 
<?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提供的的几个常用方法:
  •       notifyItemChanged(int position) //通知位置position的Item的数据改变
    •       notifyItemInserted(int)//通知位置position的Item的数据插入
    •       notifyItemRemoved(int)//通知位置position的Item的数据移除
  •       notifyItemRangeChanged(int positionStart, int itemCount) //通知从位置positionStart开始,有itemCount个Item的数据发生改变
  •       notifyItemRangeInserted(int positionStart, int itemCount) //通知从位置positionStart开始,有itemCount个Item的数据插入
  •       notifyItemRangeRemoved(int positionStart, int itemCount)//通知从位置positionStart开始,有itemCount个Item的数据移除
      主要是使用Adapter提供的notifyItemInserted(position)和notifyItemRemoved(position)方法,告知数据改变,如果删除或者添加Item都是从Position为0的位置开始,加上notifyDataSetChanged()刷新一下UI。
      注意:使用notifyDataSetChanged()不会触发Item的动画效果。

 * 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提供的默认的动画效果  
    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()+"\n"  
                        + "可见Item第一个Position:"  
                        + layoutManager.findFirstVisibleItemPosition()+"\n"  
                        + "可见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);  

  • StaggeredGridLayoutManager

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

  LinearLayoutManager,GridLayoutManager和StaggeredGridLayoutManager使用方法都类似,直接作为参数传给setLayoutManager就可以了。

      关于RecyclerView更深入的用法在后面的博文中介绍。

GITHUb下载地址https://github.com/myjoybar/Android-RecyclerView


CSDN下载地址http://download.csdn.net/detail/yalinfendou/8837997



  • StaggeredGridLayoutManager

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

你可能感兴趣的:(RecyclerView)