配色: 字号:
用ItemDecoration实现按字母排序列表
2016-12-19 | 阅:  转:  |  分享 
  
用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.Adapterimplements

StickyHeaderAdapter{

privateContextmContext;

privateListmDatas;

privateLayoutInflatermInflater;

privateinti;



publicMedicineAdapter(ContextmContext,ListmDatas){

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,childHeight);

//根据测量后的宽高放置位置

header.layout(0,0,header.getMeasuredWidth(),header.getMeasuredHeight());

returnholder;

}

在这里面调用了之前Adapter实现接口里面的方法,创建了ViewHolder,绑定了数据。都知道自定义view需要实现onMeasure、onLayout、onDraw方法,所以在这里对它进行了测量和放置,而draw是在onDrawOver里面实现的。对这里熟悉的可以去看看相关的知识点。



onDrawOver方法

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

@Override

publicvoidonDrawOver(Canvasc,RecyclerViewparent,RecyclerView.Statestate){

finalintcount=parent.getChildCount();



for(intlayoutPos=0;layoutPos
finalViewchild=parent.getChildAt(layoutPos);



finalintadapterPos=parent.getChildAdapterPosition(child);

//只有在最上面一个item或者有header的item才绘制ItemDecoration

if(adapterPos!=RecyclerView.NO_POSITION&&(layoutPos==0||hasHeader(adapterPos))){

Viewheader=getHeader(parent,adapterPos).itemView;

c.save();

finalintleft=child.getLeft();

finalinttop=getHeaderTop(parent,child,header,adapterPos,layoutPos);

c.translate(left,top);

header.setTranslationX(left);

header.setTranslationY(top);

header.draw(c);

c.restore();

}

}

}

这里就是一个循环,在需要Header的地方进行绘制,当然需要把画布移动到要绘制的位置,主要是确定它距离顶部的大小。

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

privateintgetHeaderTop(RecyclerViewparent,Viewchild,Viewheader,intadapterPos,intlayoutPos){

intheaderHeight=header.getHeight();

inttop=((int)child.getY())-headerHeight;

if(layoutPos==0){//处理最上面两个ItemDecoration切换时

finalintcount=parent.getChildCount();

finalStringcurrentId=mAdapter.getHeaderId(adapterPos);

for(inti=1;i
intadapterPosHere=parent.getChildAdapterPosition(parent.getChildAt(i));

if(adapterPosHere!=RecyclerView.NO_POSITION){

StringnextId=mAdapter.getHeaderId(adapterPosHere);

if(!nextId.equals(currentId)){//找到下一个不同类的view

finalViewnext=parent.getChildAt(i);

//这里计算offset画个图会很清楚

finalintoffset=((int)next.getY())-(headerHeight+getHeader(parent,adapterPosHere).itemView.getHeight());

if(offset<0){//如果大于0的话,此时并没有切换

returnoffset;

}else{

break;

}

}

}

}

//top不能小于0,否则最上面的ItemDecoration不会一直存在

top=Math.max(0,top);

}

returntop;

}

这里的逻辑是这样的:

1.当此view不是最上面的显示的时候,header距离顶部直接就是此view距离顶部距离减去header的高度即可

2.当此view是最上面的view的时候,首先用for循环找到它下一个和它headerId不同的第一个view,利用找到的这个view和它本身来计算出它的header距离顶部的距离,当这个距离大于0时,代表此view的header还全部显示出来,这时直接用上面的方式获取这个距离,当这个距离小于0时就是我们所希望的。





OK完成,下面贴出ItemDecoration的所以代码

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

/

Createdbylzyon2016/11/23.

/

publicclassStickyItemDecorationextendsRecyclerView.ItemDecoration{

privatestaticfinalStringTAG="lzy";

privateMedicineAdaptermAdapter;



publicStickyItemDecoration(MedicineAdaptermAdapter){

super();

this.mAdapter=mAdapter;

}



//最后调用绘制顶部固定的header

@Override

publicvoidonDrawOver(Canvasc,RecyclerViewparent,RecyclerView.Statestate){

finalintcount=parent.getChildCount();



for(intlayoutPos=0;layoutPos
finalViewchild=parent.getChildAt(layoutPos);



finalintadapterPos=parent.getChildAdapterPosition(child);

//只有在最上面一个item或者有header的item才绘制ItemDecoration

if(adapterPos!=RecyclerView.NO_POSITION&&(layoutPos==0||hasHeader(adapterPos))){

Viewheader=getHeader(parent,adapterPos).itemView;

c.save();

finalintleft=child.getLeft();

finalinttop=getHeaderTop(parent,child,header,adapterPos,layoutPos);

c.translate(left,top);

header.setTranslationX(left);

header.setTranslationY(top);

header.draw(c);

c.restore();

}

}

}



@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);

}



/

判断是否有header



@paramposition

@return

/

privatebooleanhasHeader(intposition){

if(position==0){//第一个位置必然有

returntrue;

}

//判断和上一个的id不同则有header

intprevious=position-1;

return!mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous));

}



/

获得自定义的Header



@paramparent

@paramposition

@return

/

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,childHeight);

//根据测量后的宽高放置位置

header.layout(0,0,header.getMeasuredWidth(),header.getMeasuredHeight());

returnholder;

}



/

计算距离顶部的高度



@paramparent

@paramchild

@paramheader

@paramadapterPos

@paramlayoutPos

@return

/

privateintgetHeaderTop(RecyclerViewparent,Viewchild,Viewheader,intadapterPos,intlayoutPos){

intheaderHeight=header.getHeight();

inttop=((int)child.getY())-headerHeight;

if(layoutPos==0){//处理最上面两个ItemDecoration切换时

finalintcount=parent.getChildCount();

finalStringcurrentId=mAdapter.getHeaderId(adapterPos);

for(inti=1;i
intadapterPosHere=parent.getChildAdapterPosition(parent.getChildAt(i));

if(adapterPosHere!=RecyclerView.NO_POSITION){

StringnextId=mAdapter.getHeaderId(adapterPosHere);

//找到下一个不同类的view

if(!nextId.equals(currentId)){

finalViewnext=parent.getChildAt(i);

//这里计算offset画个图会很清楚

finalintoffset=((int)next.getY())-(headerHeight+getHeader(parent,adapterPosHere).itemView.getHeight());

if(offset<0){//如果大于0的话,此时并没有切换

returnoffset;

}else{

break;

}

}

}

}

//top不能小于0,否则最上面的ItemDecoration不会一直存在

top=Math.max(0,top);

}

returntop;

}

}



汉字转拼音

通过一个三方的类库pinyin4j来实现,这里就是调用方法PinyinHelper.toHanyuPinyinStringArray获取一个汉字的拼音,然后得到第一个英文字母并转化为大写

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

privateListfilledData(String[]data){

ListmSortList=newArrayList();



for(inti=0;i
MedicineBeanmedicineBean=newMedicineBean();

medicineBean.setName(data[i]);

//汉字转换成拼音

String[]pinyin=PinyinHelper.toHanyuPinyinStringArray(data[i].toCharArray()[0]);

StringsortString=pinyin[0].substring(0,1).toUpperCase();

//正则表达式,判断首字母是否是英文字母

if(sortString.matches("[A-Z]")){

medicineBean.setLetter(sortString.toUpperCase());

}else{

medicineBean.setLetter("#");

}



mSortList.add(medicineBean);

}

returnmSortList;



}





右边的字母导航栏

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassSideBarextendsView{

//触摸事件

privateOnTouchingLetterChangedListeneronTouchingLetterChangedListener;

//26个字母

publicstaticString[]b={"A","B","C","D","E","F","G","H","I",

"J","K","L","M","N","O","P","Q","R","S","T","U","V",

"W","X","Y","Z","#"};

privateintchoose=-1;//选中

privatePaintpaint=newPaint();



privateTextViewmTextDialog;



publicvoidsetTextView(TextViewmTextDialog){

this.mTextDialog=mTextDialog;

}



publicSideBar(Contextcontext,AttributeSetattrs,intdefStyle){

super(context,attrs,defStyle);

}



publicSideBar(Contextcontext,AttributeSetattrs){

super(context,attrs);

}



publicSideBar(Contextcontext){

super(context);

}



@Override

protectedvoidonDraw(Canvascanvas){

super.onDraw(canvas);

//获取焦点改变背景颜色.

intheight=getHeight();//获取对应高度

intwidth=getWidth();//获取对应宽度

intsingleHeight=height/b.length;//获取每一个字母的高度



for(inti=0;i
paint.setColor(Color.rgb(33,65,98));

//paint.setColor(Color.WHITE);

paint.setTypeface(Typeface.DEFAULT_BOLD);

paint.setAntiAlias(true);

paint.setTextSize(20);

//选中的状态

if(i==choose){

paint.setColor(Color.parseColor("#3399ff"));

paint.setFakeBoldText(true);

}

//x坐标等于中间-字符串宽度的一半.

floatxPos=width/2-paint.measureText(b[i])/2;

floatyPos=singleHeighti+singleHeight;

canvas.drawText(b[i],xPos,yPos,paint);

paint.reset();//重置画笔

}



}



@Override

publicbooleandispatchTouchEvent(MotionEventevent){

finalintaction=event.getAction();

finalfloaty=event.getY();//点击y坐标

finalintoldChoose=choose;

finalOnTouchingLetterChangedListenerlistener=onTouchingLetterChangedListener;

finalintc=(int)(y/getHeight()b.length);//点击y坐标所占总高度的比例b数组的长度就等于点击b中的个数.



switch(action){

caseMotionEvent.ACTION_UP:

setBackgroundDrawable(newColorDrawable(0x00000000));

choose=-1;//

invalidate();

if(mTextDialog!=null){

mTextDialog.setVisibility(View.INVISIBLE);

}

break;

default:

setBackgroundColor(Color.parseColor("#808080"));

if(oldChoose!=c){

if(c>=0&&c
if(listener!=null){

listener.onTouchingLetterChanged(b[c]);

}

if(mTextDialog!=null){

mTextDialog.setText(b[c]);

mTextDialog.setVisibility(View.VISIBLE);

}

choose=c;

invalidate();

}

}



break;

}

returntrue;

}



/

向外公开的方法



@paramonTouchingLetterChangedListener

/

publicvoidsetOnTouchingLetterChangedListener(

OnTouchingLetterChangedListeneronTouchingLetterChangedListener){

this.onTouchingLetterChangedListener=onTouchingLetterChangedListener;

}



/

接口



@authorcoder

/

publicinterfaceOnTouchingLetterChangedListener{

voidonTouchingLetterChanged(Strings);

}



}

这是在网上找的一个类,相信大家都能看懂,就不多说了,最后贴出MainActivity

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassMainActivityextendsAppCompatActivity{

privateString[]mData={"阿魏八味丸","阿昔洛韦眼膏","艾司洛尔","安吖啶注射液","阿达帕林","参茸追风酒","草乌","石斛夜光丸",

"骨质增生片","乌鸡白凤丸","人参益母丸","补脾益肠丸","丹参片","小金丸","妇宁康","糖脉康","菲伯瑞","乙肝解毒片","脑血栓片"};

privatestaticfinalStringTAG="lzy";

privateRecyclerViewmRecyclerView;

privateSideBarsideBar;

privateStickyItemDecorationmDecoration;

privatePinyinComparatorpinyinComparator;

privateMedicineAdaptermedicineAdapter;



@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInswww.baiyuewang.nettanceState);

setContentView(R.layout.activity_main);

mRecyclerView=(RecyclerView)findViewById(R.id.rv);

mRecyclerView.setLayoutManager(newLinearLayoutManager(this));



//按照字母排序

pinyinComparator=newPinyinComparator();



Listdata=filledData(mData);

Collections.sort(data,pinyinComparator);



medicineAdapter=newMedicineAdapter(this,data);

mRecyclerView.setAdapter(medicineAdapter);

mDecoration=newStickyItemDecoration(medicineAdapter);

mRecyclerView.addItemDecoration(mDecoration);

//添加分割线

mRecyclerView.addItemDecoration(newDividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL_LIST));



sideBar=(SideBar)findViewById(R.id.sideBar);

sideBar.setTextView((TextView)findViewById(R.id.dialog));

//设置右侧触摸监听

sideBar.setOnTouchingLetterChangedListener(newSideBar.OnTouchingLetterChangedListener(){



@Override

publicvoidonTouchingLetterChanged(Strings){

//该字母首次出现的位置

intposition=medicineAdapter.getPositionForSection(s);

if(position!=-1){

mRecyclerView.scrollToPosition(position);

}



}

});



}



/

填充数据



@paramdata

@return

/

privateListfilledData(String[]data){

ListmSortList=newArrayList();



for(inti=0;i
MedicineBeanmedicineBean=newMedicineBean();

medicineBean.setName(data[i]);

//汉字转换成拼音

String[]pinyin=PinyinHelper.toHanyuPinyinStringArray(data[i].toCharArray()[0]);

StringsortString=pinyin[0].substring(0,1).toUpperCase();

//正则表达式,判断首字母是否是英文字母

if(sortString.matches("[A-Z]")){

medicineBean.setLetter(sortString.toUpperCase());

}else{

medicineBean.setLetter("#");

}



mSortList.add(medicineBean);

}

returnmSortList;



}

}

献花(0)
+1
(本文系thedust79首藏)