Android RecyclerView详解及实现瀑布流式布局
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
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。