RecyclerView一个可以代替ListView和GridView的控件,那么RecyclerView到底比他们好在哪里?

RecyclerView架构提供了一种插拔式的体验,所以实现了代码的高度解耦,使用起来也异常的灵活。


我们可以通过设置它的LayoutManager控制其显示的方式,通过ItemDecoration控制Item间的间隔,通过ItemAnimator控制Item的增删动画

RecyclerView.LayoutManager提供了三个实现类其中LinearLayoutManager 现行管理器,支持横向、纵向,GridLayoutManager 网格布局管理器,StaggeredGridLayoutManager 瀑布就式布局管理器


那么先从LinearLayoutManager看起

先在gradle中引用compile 'com.android.support:recyclerview-v7:23.4.0'

Activity布局文件如下:

<?xmlversion="1.0"encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.lg.recyclerviewdemo.LinearActivity"><android.support.v7.widget.RecyclerViewandroid:id="@+id/linear_recycler"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout>

item布局文件:

<?xmlversion="1.0"encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"><ImageViewandroid:layout_width="70dp"android:layout_height="70dp"android:src="@drawable/android"/><TextViewandroid:id="@+id/recycler_item_tv"android:layout_width="wrap_content"android:layout_height="100dp"android:layout_marginLeft="20dp"android:gravity="center"android:textColor="@color/colorPrimary"android:textSize="20sp"android:textStyle="bold"/></LinearLayout></RelativeLayout>

在Acitvity中初始化数据:

mDatas=newArrayList<String>();for(inti=1;i<=65;i++){mDatas.add("item"+i);}

核心代码:

recyclerAdapter=newRecyclerAdapter();//设置布局管理器linear_recycler.setLayoutManager(newLinearLayoutManager(this));//设置adapterlinear_recycler.setAdapter(recyclerAdapter);//添加分割线linear_recycler.addItemDecoration(newDividerLinearItemDecoration(this,DividerLinearItemDecoration.VERTICAL_LIST));

接下来自制adapter:

publicclassRecyclerAdapterextendsRecyclerView.Adapter<LinearHolder>{privateViewview;@OverridepublicLinearHolderonCreateViewHolder(ViewGroupparent,intviewType){//利用反射将item的布局加载出来view=LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item,null);//new一个我们的ViewHolder,findViewById操作都在LinearHolder的构造方法中进行了returnnewLinearHolder(view);}@OverridepublicvoidonBindViewHolder(LinearHolderholder,intposition){holder.recycler_item.setText(MainActivity.mDatas.get(position));}@OverridepublicintgetItemCount(){returnMainActivity.mDatas.size();}}classLinearHolderextendsRecyclerView.ViewHolder{TextViewrecycler_item;publicLinearHolder(ViewitemView){super(itemView);recycler_item=(TextView)itemView.findViewById(R.id.recycler_item_tv);}

再绘画它的分割线:

publicclassDividerLinearItemDecorationextendsRecyclerView.ItemDecoration{privatestaticfinalint[]ATTRS=newint[]{android.R.attr.listDivider};publicstaticfinalintHORIZONTAL_LIST=LinearLayoutManager.HORIZONTAL;publicstaticfinalintVERTICAL_LIST=LinearLayoutManager.VERTICAL;privateDrawablemDivider;privateintmOrientation;publicDividerLinearItemDecoration(Contextcontext,intorientation){finalTypedArraya=context.obtainStyledAttributes(ATTRS);mDivider=a.getDrawable(0);a.recycle();setOrientation(orientation);}publicvoidsetOrientation(intorientation){if(orientation!=HORIZONTAL_LIST&&orientation!=VERTICAL_LIST){thrownewIllegalArgumentException("invalidorientation");}mOrientation=orientation;}@OverridepublicvoidonDraw(Canvasc,RecyclerViewparent){if(mOrientation==VERTICAL_LIST){drawVertical(c,parent);}else{drawHorizontal(c,parent);}}publicvoiddrawVertical(Canvasc,RecyclerViewparent){finalintleft=parent.getPaddingLeft();finalintright=parent.getWidth()-parent.getPaddingRight();finalintchildCount=parent.getChildCount();for(inti=0;i<childCount;i++){finalViewchild=parent.getChildAt(i);RecyclerViewv=newRecyclerView(parent.getContext());finalRecyclerView.LayoutParamsparams=(RecyclerView.LayoutParams)child.getLayoutParams();finalinttop=child.getBottom()+params.bottomMargin;finalintbottom=top+mDivider.getIntrinsicHeight();mDivider.setBounds(left,top,right,bottom);mDivider.draw(c);}}publicvoiddrawHorizontal(Canvasc,RecyclerViewparent){finalinttop=parent.getPaddingTop();finalintbottom=parent.getHeight()-parent.getPaddingBottom();finalintchildCount=parent.getChildCount();for(inti=0;i<childCount;i++){finalViewchild=parent.getChildAt(i);finalRecyclerView.LayoutParamsparams=(RecyclerView.LayoutParams)child.getLayoutParams();finalintleft=child.getRight()+params.rightMargin;finalintright=left+mDivider.getIntrinsicHeight();mDivider.setBounds(left,top,right,bottom);mDivider.draw(c);}}@OverridepublicvoidgetItemOffsets(RectoutRect,intitemPosition,RecyclerViewparent){if(mOrientation==VERTICAL_LIST){outRect.set(0,0,0,mDivider.getIntrinsicHeight());}else{outRect.set(0,0,mDivider.getIntrinsicWidth(),0);}}}

好了,我们来看看效果:

嘛,貌似和ListView没什么区别,还这么麻烦

别急,我们试试GridLayoutManager

很简单,我们只需要改变LayoutManager和ItemDecoration就行了:

grid_recycler.setLayoutManager(newGridLayoutManager(this,2));grid_recycler.addItemDecoration(newDividerGridItemDecoration(this));

DividerGridItemDecoration代码:

publicclassDividerGridItemDecorationextendsRecyclerView.ItemDecoration{privatestaticfinalint[]ATTRS=newint[]{android.R.attr.listDivider};privateDrawablemDivider;publicDividerGridItemDecoration(Contextcontext){finalTypedArraya=context.obtainStyledAttributes(ATTRS);mDivider=a.getDrawable(0);a.recycle();}@OverridepublicvoidonDraw(Canvasc,RecyclerViewparent,RecyclerView.Statestate){drawHorizontal(c,parent);drawVertical(c,parent);}privateintgetSpanCount(RecyclerViewparent){//列数intspanCount=-1;RecyclerView.LayoutManagerlayoutManager=parent.getLayoutManager();if(layoutManagerinstanceofGridLayoutManager){spanCount=((GridLayoutManager)layoutManager).getSpanCount();}elseif(layoutManagerinstanceofStaggeredGridLayoutManager){spanCount=((StaggeredGridLayoutManager)layoutManager).getSpanCount();}returnspanCount;}publicvoiddrawHorizontal(Canvasc,RecyclerViewparent){intchildCount=parent.getChildCount();for(inti=0;i<childCount;i++){finalViewchild=parent.getChildAt(i);finalRecyclerView.LayoutParamsparams=(RecyclerView.LayoutParams)child.getLayoutParams();finalintleft=child.getLeft()-params.leftMargin;finalintright=child.getRight()+params.rightMargin+mDivider.getIntrinsicWidth();finalinttop=child.getBottom()+params.bottomMargin;finalintbottom=top+mDivider.getIntrinsicHeight();mDivider.setBounds(left,top,right,bottom);mDivider.draw(c);}}publicvoiddrawVertical(Canvasc,RecyclerViewparent){finalintchildCount=parent.getChildCount();for(inti=0;i<childCount;i++){finalViewchild=parent.getChildAt(i);finalRecyclerView.LayoutParamsparams=(RecyclerView.LayoutParams)child.getLayoutParams();finalinttop=child.getTop()-params.topMargin;finalintbottom=child.getBottom()+params.bottomMargin;finalintleft=child.getRight()+params.rightMargin;finalintright=left+mDivider.getIntrinsicWidth();mDivider.setBounds(left,top,right,bottom);mDivider.draw(c);}}privatebooleanisLastColum(RecyclerViewparent,intpos,intspanCount,intchildCount){RecyclerView.LayoutManagerlayoutManager=parent.getLayoutManager();if(layoutManagerinstanceofGridLayoutManager){if((pos+1)%spanCount==0)//如果是最后一列,则不需要绘制右边{returntrue;}}elseif(layoutManagerinstanceofStaggeredGridLayoutManager){intorientation=((StaggeredGridLayoutManager)layoutManager).getOrientation();if(orientation==StaggeredGridLayoutManager.VERTICAL){if((pos+1)%spanCount==0)//如果是最后一列,则不需要绘制右边{returntrue;}}else{childCount=childCount-childCount%spanCount;if(pos>=childCount)//如果是最后一列,则不需要绘制右边returntrue;}}returnfalse;}privatebooleanisLastRaw(RecyclerViewparent,intpos,intspanCount,intchildCount){RecyclerView.LayoutManagerlayoutManager=parent.getLayoutManager();if(layoutManagerinstanceofGridLayoutManager){childCount=childCount-childCount%spanCount;if(pos>=childCount)//如果是最后一行,则不需要绘制底部returntrue;}elseif(layoutManagerinstanceofStaggeredGridLayoutManager){intorientation=((StaggeredGridLayoutManager)layoutManager).getOrientation();//StaggeredGridLayoutManager且纵向滚动if(orientation==StaggeredGridLayoutManager.VERTICAL){childCount=childCount-childCount%spanCount;//如果是最后一行,则不需要绘制底部if(pos>=childCount)returntrue;}else//StaggeredGridLayoutManager且横向滚动{//如果是最后一行,则不需要绘制底部if((pos+1)%spanCount==0){returntrue;}}}returnfalse;}@OverridepublicvoidgetItemOffsets(RectoutRect,intitemPosition,RecyclerViewparent){intspanCount=getSpanCount(parent);intchildCount=parent.getAdapter().getItemCount();if(isLastRaw(parent,itemPosition,spanCount,childCount))//如果是最后一行,则不需要绘制底部{outRect.set(0,0,mDivider.getIntrinsicWidth(),0);}elseif(isLastColum(parent,itemPosition,spanCount,childCount))//如果是最后一列,则不需要绘制右边{outRect.set(0,0,0,mDivider.getIntrinsicHeight());}else{outRect.set(0,0,mDivider.getIntrinsicWidth(),mDivider.getIntrinsicHeight());}}}

看下效果吧:

渍,有点意思,不过也没那么神乎其神啊

别忘了,我们还有个StaggeredGridLayoutManager没用

展示了那么多纵向的,我们来个横向的,同样改变LayoutManager

stag_grid_recycler.setLayoutManager(newStaggeredGridLayoutManager(3,StaggeredGridLayoutManager.HORIZONTAL));

看下效果:

一个RecyclerView就能实现这么多功能,确实强大啊

不过,你以为这样就完了?下来要放大招了

看标题,瀑布流有木有,你用ListView不会那么简单就实现吧,但是用RecyclerView分分钟

稍微改变item布局,让图片放在字的上面

我们在onBindViewHolder给item设置随机高度:

LayoutParamslayoutParams=holder.sg_item.getLayoutParams();layoutParams.height=heights.get(position);

看下大招效果:

我就问你6不6,6的话还不快关注我(嘎嘎)

好吧,可能吓到你了,什么?点击事件?

好吧,很不幸告诉你,要自己写,对,就是要自己写。

前面已经说过了,RecyclerView实现了高度解耦,非常的灵活(你要干什么,自己去写)。那就写吧!

先写个接口:

publicinterfaceOnItemClickLitener{/*点击事件*/voidonItemClick(Viewview,intposition);/*长按事件*/voidonItemLongClick(Viewview,intposition);}

在adapter中加入代码:

privateOnItemClickLitenermOnItemClickLitener;publicvoidsetOnItemClickLitener(OnItemClickLitenermOnItemClickLitener){this.mOnItemClickLitener=mOnItemClickLitener;}

onBindViewHolder方法中加入:

holder.sg_item.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){intpos=holder.getLayoutPosition();mOnItemClickLitener.onItemClick(holder.itemView,pos);}});holder.sg_item.setOnLongClickListener(newView.OnLongClickListener(){@OverridepublicbooleanonLongClick(Viewv){intpos=holder.getLayoutPosition();mOnItemClickLitener.onItemLongClick(holder.itemView,pos);returnfalse;}});

然后在Activity中调用:

staggeredGridAdapter.setOnItemClickLitener(newOnItemClickLitener(){@OverridepublicvoidonItemClick(Viewview,intposition){staggeredGridAdapter.notifyItemRemoved(position);}@OverridepublicvoidonItemLongClick(Viewview,finalintposition){android.support.v7.app.AlertDialog.Builderbuilder=newAlertDialog.Builder(StaggeredGridVActivity.this);builder.setTitle("Delete?").setNegativeButton("no",null).setPositiveButton("yes",newDialogInterface.OnClickListener(){@OverridepublicvoidonClick(DialogInterfacedialog,intwhich){staggeredGridAdapter.notifyItemRemoved(position);Toast.makeText(StaggeredGridVActivity.this,MainActivity.mDatas.get(position),Toast.LENGTH_SHORT).show();}}).show();}});

效果图:

怎么样?厉害吧。不过你以为这样就完了?

如果我想要将item托拉拽再加上侧滑删除呢?

首先,如果要实现托拉拽功能,那item长按事件还是不要写代码的,避免事件冲突

然后在Activity中加入代码:

ItemTouchHelperitemTouchHelper=newItemTouchHelper(callback);itemTouchHelper.attachToRecyclerView(stag_v_recycler);

callback代码:

ItemTouchHelper.Callbackcallback=newItemTouchHelper.Callback(){//这个方法是用来设置我们拖动的方向以及侧滑的方向的@OverridepublicintgetMovementFlags(RecyclerViewrecyclerView,RecyclerView.ViewHolderviewHolder){//设置拖拽方向为上下左右finalintdragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN|ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;//设置侧滑方向为从左到右和从右到左都可以finalintswipeFlags=ItemTouchHelper.START|ItemTouchHelper.END;//将方向参数设置进去returnmakeMovementFlags(dragFlags,swipeFlags);}/***@paramrecyclerView*@paramviewHolder拖动的ViewHolder*@paramtarget目标位置的ViewHolder*@return*/@OverridepublicbooleanonMove(RecyclerViewrecyclerView,RecyclerView.ViewHolderviewHolder,RecyclerView.ViewHoldertarget){intfromPosition=viewHolder.getAdapterPosition();//得到拖动ViewHolder的positioninttoPosition=target.getAdapterPosition();//得到目标ViewHolder的positionif(fromPosition<toPosition){//分别把中间所有的item的位置重新交换for(inti=fromPosition;i<toPosition;i++){Collections.swap(MainActivity.mDatas,i,i+1);}}else{for(inti=fromPosition;i>toPosition;i--){Collections.swap(MainActivity.mDatas,i,i-1);}}staggeredGridAdapter.notifyItemMoved(fromPosition,toPosition);//返回true表示执行拖动returntrue;}@OverridepublicvoidonSwiped(RecyclerView.ViewHolderviewHolder,intdirection){intposition=viewHolder.getAdapterPosition();staggeredGridAdapter.notifyItemRemoved(position);}@OverridepublicvoidonChildDraw(Canvasc,RecyclerViewrecyclerView,RecyclerView.ViewHolderviewHolder,floatdX,floatdY,intactionState,booleanisCurrentlyActive){super.onChildDraw(c,recyclerView,viewHolder,dX,dY,actionState,isCurrentlyActive);if(actionState==ItemTouchHelper.ACTION_STATE_SWIPE){//滑动时改变Item的透明度finalfloatalpha=1-Math.abs(dX)/(float)viewHolder.itemView.getWidth();viewHolder.itemView.setAlpha(alpha);viewHolder.itemView.setTranslationX(dX);}}};

OK,我们来看看效果:

如果你喜欢我的文章,那就关注我的博客吧,我会不定期的发些技术贴

源码地址:http://down.51cto.com/data/2222200