ItemDecoration详解以及用ItemDecoration实现按字母排序列表.docx
- 文档编号:6208573
- 上传时间:2023-01-04
- 格式:DOCX
- 页数:27
- 大小:131.93KB
ItemDecoration详解以及用ItemDecoration实现按字母排序列表.docx
《ItemDecoration详解以及用ItemDecoration实现按字母排序列表.docx》由会员分享,可在线阅读,更多相关《ItemDecoration详解以及用ItemDecoration实现按字母排序列表.docx(27页珍藏版)》请在冰豆网上搜索。
ItemDecoration详解以及用ItemDecoration实现按字母排序列表
ItemDecoration详解以及用ItemDecoration实现按字母排序列表
可以看出要实现上面效果,有三个步骤:
1.汉字转化为拼音,并且根据首字母排序
2.用ItemDecoration实现字母行的显示
3.自定义实现右侧的按字母导航栏
当然重点讲讲ItemDecoration的实现。
都知道RecyclerView本身都没有分割线,需要分割线都是在item中画一条线或者使用ItemDecoration来实现分割线。
在RecyclerView中我们可以给每一个item都添加ItemDecoration,所以可以自定义ItemDecoration来实现各种我们所需要的效果。
ItemDecoration
ItemDecoration是RecyclerView内部的一个抽象类,要实现这个抽象类自然需要实现内部的抽象方法,除了deprecated的方法只有下面三个方法:
1.voidgetItemOffsets(RectoutRect,Viewview,RecyclerViewparent,Statestate)
这个方法是用来指定每一个的item对应decoration的大小区域,主要实现方式就是设置outRect的left、top、right、bottom,如果一个item不需要decoration把outRect的上下左右设置为0即可。
盗用网上一张图看看outRect具体什么意思
2.voidonDraw(Canvasc,RecyclerViewparent,Statestate)
onDraw方法看名字大家都应该很熟悉,这个方法自然是用来画具体的ItemDecoration的,绘制的内容是显示在itemView的下层。
下层什么意思,待会来看看。
3.voidonDrawOver(Canvasc,RecyclerViewparent,Statestate)
也是一个绘制的方法,不过是绘制在itemView的上层
以上三个方法的调用顺序也就是按照上面的排列的顺序来调用的。
首先来看看用ItemDecoration实现的分割线DividerItemDecoration
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassDividerItemDecorationextendsRecyclerView.ItemDecoration{
privatestaticfinalint[]ATTRS=newint[]{android.R.attr.listDivider};
publicstaticfinalintHORIZONTAL_LIST=LinearLayoutManager.HORIZONTAL;
publicstaticfinalintVERTICAL_LIST=LinearLayoutManager.VERTICAL;
privateDrawablemDivider;
privateintmOrientation;
publicDividerItemDecoration(Contextcontext,intorientation){
//获取系统的divider
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;
}
@Override
publicvoidonDraw(Canvasc,RecyclerViewparent,RecyclerView.Statestate){
super.onDraw(c,parent,state);
if(mOrientation==VERTICAL_LIST){
drawVertical(c,parent);
}else{
drawHorizontal(c,parent);
}
}
//画竖直分割线
publicvoiddrawVertical(Canvasc,RecyclerViewparent){
//左边缘距离RecyclerView左边的距离
finalintleft=parent.getPaddingLeft();
//右边缘距离RecyclerView右边边的距离
finalintright=parent.getWidth()-parent.getPaddingRight();
finalintchildCount=parent.getChildCount();
for(inti=0;i finalViewchild=parent.getChildAt(i); finalRecyclerView.LayoutParamsparams=(RecyclerView.LayoutParams)child .getLayoutParams(); finalinttop=child.getBottom()+params.bottomMargin; finalintbottom; //去掉最后一条的分割线 if(i==childCount-1){//bottom和top相等,即高度为0不显示 bottom=top; }else{ bottom=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 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); } } @Override publicvoidgetItemOffsets(RectoutRect,Viewview,RecyclerViewparent,RecyclerView.Statestate){ super.getItemOffsets(outRect,view,parent,state); if(mOrientation==VERTICAL_LIST){ outRect.set(0,0,0,mDivider.getIntrinsicHeight()); }else{ outRect.set(0,0,mDivider.getIntrinsicWidth(),0); } } } 主要实现了getItemOffsets和onDraw方法,因为这两个方法已经能满足了,自然不需要全部方法实现。 在getItemOffsets方法中判断是竖直方向还是水平方向的分割线,竖直方向只需要在outRect的bottom加上分割线的宽度即可,当然水平分割线在右边加上就OK。 在onDraw方法中具体的画出分割线,不知道大家有没有想过这里的分割线高度和outRect中设置的高度有什么关系,那么下面修改一下代码实验一下。 在上面onDraw画竖直分割线的方法中,把分割线高度加上80px,即: [java]viewplaincopy在CODE上查看代码片派生到我的代码片 mDivider.setBounds(left,top,right,bottom+80); 效果没变化? 意思是onDraw里面画的区域大小不会超过outRect设置的大小吗? 记得之前说过,onDraw方法绘制的内容是在itemView的下层的,会不会是被itemView遮挡而没有显示出来呢,那么下面我们把itemView的背景色改为透明,看看效果 [html]viewplaincopy在CODE上查看代码片派生到我的代码片 android: background="@android: color/transparent" 这次看到了不同了吧,正是我们所猜想的那样。 也就证明了onDraw方法显示的内容实在itemView的下层,同时它绘制的内容并不是不会超过outRect指定的区域,而outRect指定的区域也是实际分配给ItemDecoration的区域,在这个区域绘制才不会影响itemView,所以onDraw绘制的内容我们应该要保持和outRect给定的区域是相同的。 显示字母的ItemDecoration 现在来看看显示字母的ItemDecoration是怎么实现的。 看上面的效果可以发现,最上面始终显示了一个ItemDecoration,上面说过onDrawOver方法绘制的内容是显示在最上层,所以用这个方法来绘制最上面再适合不过了。 其他itemView显示字母的ItemDecoration也并不是采用onDraw方法绘制,而是用xml实现的,因为采用xml方式来实现可以更方便的来定制ItemDecoration的内容,也可以实现其中的点击事件。 在看ItemDecoration之前,先看看所用到的一个接口和RecyclerView的Adapter [java]viewplaincopy在CODE上查看代码片派生到我的代码片 publicinterfaceStickyHeaderAdapter StringgetHeaderId(intposition); TonCreateHeaderViewHolder(ViewGroupparent); voidonBindHeaderViewHolder(Tviewholder,intposition); } 这个接口里面有三个方法,第一个方法是获取headerId,因为在显示是不可能每一个item都要显示decoration,只有每种首字母第一个才显示,所用这里需要一个id来判断是否需要设置ItemDecoration。 后面两个方法是仿照RecyclerView.Adapter的写的,因为我们采用ItemDecoration布局用xml实现,如果需要显示的ItemDecoration很多的话,每次都需要去用LayoutInflater去加载布局,显然不够优雅,所用用holder机制来实现复用。 下面来看看我们的Adapter [java]viewplaincopy在CODE上查看代码片派生到我的代码片 /** *Createdbylzy. *Date: 16/11/24 */ publicclassMedicineAdapterextendsRecyclerView.Adapter StickyHeaderAdapter privateContextmContext; privateList privateLayoutInflatermInflater; privateinti; publicMedicineAdapter(ContextmContext,List this.mContext=mContext; this.mDatas=mDatas; mInflater=LayoutInflater.from(mContext); } @Override publicViewHolderonCreateViewHolder(ViewGroupparent,intviewType){ returnnewViewHolder(mInflater.inflate(R.layout.item_medicine,parent,false)); } @Override publicvoidonBindViewHolder(finalViewHolderholder,finalintposition){ finalMedicineBeanMedicineBean=mDatas.get(position); holder.tvName.setText(MedicineBean.getName()); } @Override publicintgetItemCount(){ returnmDatas! =null? mDatas.size(): 0; } @Override publicStringgetHeaderId(intposition){ returnmDatas.get(position).getLetter(); } @Override//生成header的布局 publicHeaderHolderonCreateHeaderViewHolder(ViewGroupparent){ returnnewHeaderHolder(mInflater.inflate(R.layout.item_decoration,parent,false)); } @Override//绑定header的数据 publicvoidonBindHeaderViewHolder(HeaderHolderviewholder,intposition){ viewholder.header.setText(mDatas.get(position).getLetter()); } /** *根据分类的首字母获取其第一次出现该首字母的位置 */ publicintgetPositionForSection(Stringsection){ for(inti=0;i StringsortStr=mDatas.get(i).getLetter(); if(sortStr.equals(section)){ returni; } } return-1; } publicstaticclassViewHolderextendsRecyclerView.ViewHolder{ TextViewtvName; publicViewHolder(ViewitemView){ super(itemView); tvName=(TextView)itemView.findViewById(R.id.name); } } publicstaticclassHeaderHolderextendsRecyclerView.ViewHolder{ publicTextViewheader; publicHeaderHolder(ViewitemView){ super(itemView); header=(TextView)itemView; } } } 可以看到这个Adapter实现了之前上面的接口,接口的实现方式和普通的Adapter的实现都类似在onCreateHeaderViewHolder中加载xml文件,在onBindHeaderViewHolder中加载数据,HeaderHolder也是一样的,headerId是采用的一个letter的,也就是每一条的首字母。 下面看看ItemDecoration是怎么实现的。 getItemOffsets方法 首先看看第一个getItemOffsets方法的实现 [java]viewplaincopy在CODE上查看代码片派生到我的代码片 @Override publicvoidgetItemOffsets(RectoutRect,Viewview,RecyclerViewparent,RecyclerView.Statestate){ super.getItemOffsets(outRect,view,parent,state); //得到该item所在的位置 intposition=parent.getChildAdapterPosition(view); intheaderHeight=0; //在使用adapterPosition时最好的加上这个判断 if(position! =RecyclerView.NO_POSITION&&hasHeader(position)){ //获取到ItemDecoration所需要的高度 Viewheader=getHeader(parent,position).itemView; headerHeight=header.getHeight(); } outRect.set(0,headerHeight,0,0); } 很简单,就是判断如果这个item需要ItemDecoration就获取到header的高度,设置给outRect 判断是否需要header 判断是否需要header的方法,之前不是在Adapter里面写了getHeaderId的方法吗,这里就用到了,根据前两个headerId是否相同来判断是否需要设置ItemDecoration [java]viewplaincopy在CODE上查看代码片派生到我的代码片 privatebooleanhasHeader(intposition){ if(position==0){//第一个位置必然有 returntrue; } //判断和上一个的id不同则有header intprevious=position-1; return! mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous)); } 获取Header的方法 [java]viewplaincopy在CODE上查看代码片派生到我的代码片 privateRecyclerView.ViewHoldergetHeader(RecyclerViewparent,intposition){ //创建HeaderViewHolder MedicineAdapter.HeaderHolderholder=mAdapter.onCreateHeaderViewHolder(parent); finalViewheader=holder.itemView; //绑定数据 mAdapter.onBindHeaderViewHolder(holder,position); //测量View并且layout intwidthSpec=View.MeasureSpec.makeMeasureSpec(parent.getWidth(),View.MeasureSpec.EXACTLY); intheightSpec=View.MeasureSpec.makeMeasureSpec(parent.getHeight(),View.MeasureSpec.UNSPECIFIED); //根据父View的MeasureSpec和子view自身的LayoutParams以及padding来获取子View的MeasureSpec intchildWidth=ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft()+parent.getPaddingRight(),header.getLayoutParams().width); intchildHeight=ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop()+parent.getPaddingBottom(),header.getLayoutParams().height); //进行测量 header.measure(childWidth,c
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ItemDecoration 详解 以及 实现 字母 排序 列表