ItemDecoration详解以及用ItemDecoration实现按字母排序列表_第1页
ItemDecoration详解以及用ItemDecoration实现按字母排序列表_第2页
ItemDecoration详解以及用ItemDecoration实现按字母排序列表_第3页
ItemDecoration详解以及用ItemDecoration实现按字母排序列表_第4页
ItemDecoration详解以及用ItemDecoration实现按字母排序列表_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、ItemDecoration详解以及用ItemDecoration实现按字母排序列表可以看出要实现上面效果,有三个步骤:1.汉字转化为拼音,并且根据首字母排序2.用ItemDecoration实现字母行的显示3.自定义实现右侧的按字母导航栏当然重点讲讲ItemDecoration的实现。都知道RecyclerView本身都没有分割线,需要分割线都是在item中画一条线或者使用ItemDecoration来实现分割线。在RecyclerView中我们可以给每一个item都添加ItemDecoration,所以可以自定义ItemDecoration来实现各种我们所需要的效果。ItemDecorat

2、ionItemDecoration是RecyclerView内部的一个抽象类,要实现这个抽象类自然需要实现内部的抽象方法,除了deprecated的方法只有下面三个方法:1.void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)这个方法是用来指定每一个的item对应decoration的大小区域,主要实现方式就是设置outRect的left、top、right、bottom,如果一个item不需要decoration把outRect的上下左右设置为0即可。盗用网上一张图看看outRect具体什

3、么意思2.void onDraw(Canvas c, RecyclerView parent, State state)onDraw方法看名字大家都应该很熟悉,这个方法自然是用来画具体的ItemDecoration的,绘制的内容是显示在itemView的下层。下层什么意思,待会来看看。3.void onDrawOver(Canvas c, RecyclerView parent, State state)也是一个绘制的方法,不过是绘制在itemView的上层以上三个方法的调用顺序也就是按照上面的排列的顺序来调用的。首先来看看用ItemDecoration实现的分割线DividerItemDec

4、orationjava view plain copy 在CODE上查看代码片派生到我的代码片public class DividerItemDecoration extends RecyclerView.ItemDecoration private static final int ATTRS = new intandroid.R.attr.listDivider; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST =

5、LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) /获取系统的divider final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); public vo

6、id setOrientation(int orientation) if (orientation != HORIZONTAL_LIST & orientation != VERTICAL_LIST) throw new IllegalArgumentException(invalid orientation); mOrientation = orientation; Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) super.onDraw(c, parent, stat

7、e); if (mOrientation = VERTICAL_LIST) drawVertical(c, parent); else drawHorizontal(c, parent); /画竖直分割线 public void drawVertical(Canvas c, RecyclerView parent) /左边缘距离RecyclerView左边的距离 final int left = parent.getPaddingLeft(); /右边缘距离RecyclerView右边边的距离 final int right = parent.getWidth() - parent.getPa

8、ddingRight(); 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 bo

9、ttom; /去掉最后一条的分割线 if (i = childCount - 1) /bottom和top相等,即高度为0 不显示 bottom = top; else bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); /画水平分割线 public void drawHorizontal(Canvas c, RecyclerView parent) final int top = parent.getPaddingTop();

10、 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

11、= child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) super.getItemOffsets(outRect, v

12、iew, 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加上分割线的宽度即可,当然水平分割线在右边加上

13、就OK。在onDraw方法中具体的画出分割线,不知道大家有没有想过这里的分割线高度和outRect中设置的高度有什么关系,那么下面修改一下代码实验一下。在上面onDraw画竖直分割线的方法中,把分割线高度加上80px,即:java view plain copy 在CODE上查看代码片派生到我的代码片mDivider.setBounds(left, top, right, bottom+80); 效果没变化?意思是onDraw里面画的区域大小不会超过outRect设置的大小吗?记得之前说过,onDraw方法绘制的内容是在itemView的下层的,会不会是被itemView遮挡而没有显示出来呢,

14、那么下面我们把itemView的背景色改为透明,看看效果html view plain copy 在CODE上查看代码片派生到我的代码片android:background=android:color/transparent 这次看到了不同了吧,正是我们所猜想的那样。也就证明了onDraw方法显示的内容实在itemView的下层,同时它绘制的内容并不是不会超过outRect指定的区域,而outRect指定的区域也是实际分配给ItemDecoration的区域,在这个区域绘制才不会影响itemView,所以onDraw绘制的内容我们应该要保持和outRect给定的区域是相同的。显示字母的Item

15、Decoration现在来看看显示字母的ItemDecoration是怎么实现的。看上面的效果可以发现,最上面始终显示了一个ItemDecoration,上面说过onDrawOver方法绘制的内容是显示在最上层,所以用这个方法来绘制最上面再适合不过了。其他itemView显示字母的ItemDecoration也并不是采用onDraw方法绘制,而是用xml实现的,因为采用xml方式来实现可以更方便的来定制ItemDecoration的内容,也可以实现其中的点击事件。在看ItemDecoration之前,先看看所用到的一个接口和RecyclerView的Adapterjava view plain

16、 copy 在CODE上查看代码片派生到我的代码片public interface StickyHeaderAdapter String getHeaderId(int position); T onCreateHeaderViewHolder(ViewGroup parent); void onBindHeaderViewHolder(T viewholder, int position); 这个接口里面有三个方法,第一个方法是获取headerId,因为在显示是不可能每一个item都要显示decoration,只有每种首字母第一个才显示,所用这里需要一个id来判断是否需要设置ItemDeco

17、ration。后面两个方法是仿照RecyclerView.Adapter的写的,因为我们采用ItemDecoration布局用xml实现,如果需要显示的ItemDecoration很多的话,每次都需要去用LayoutInflater去加载布局,显然不够优雅,所用用holder机制来实现复用。下面来看看我们的Adapterjava view plain copy 在CODE上查看代码片派生到我的代码片/* * Created by lzy . * Date: 16/11/24 */ public class MedicineAdapter extends RecyclerView.Adapter

18、 implements StickyHeaderAdapter private Context mContext; private List mDatas; private LayoutInflater mInflater; private int i; public MedicineAdapter(Context mContext, List mDatas) this.mContext = mContext; this.mDatas = mDatas; mInflater = LayoutInflater.from(mContext); Override public ViewHolder

19、onCreateViewHolder(ViewGroup parent, int viewType) return new ViewHolder(mInflater.inflate(R.layout.item_medicine, parent, false); Override public void onBindViewHolder(final ViewHolder holder, final int position) final MedicineBean MedicineBean = mDatas.get(position); holder.tvName.setText(Medicine

20、Bean.getName(); Override public int getItemCount() return mDatas != null ? mDatas.size() : 0; Override public String getHeaderId(int position) return mDatas.get(position).getLetter(); Override/生成header的布局 public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) return new HeaderHolder(mInflate

21、r.inflate(R.layout.item_decoration, parent, false); Override/绑定header的数据 public void onBindHeaderViewHolder(HeaderHolder viewholder, int position) viewholder.header.setText(mDatas.get(position).getLetter(); /* * 根据分类的首字母获取其第一次出现该首字母的位置 */ public int getPositionForSection(String section) for (int i =

22、 0; i mDatas.size(); i+) String sortStr = mDatas.get(i).getLetter(); if (sortStr.equals(section) return i; return -1; public static class ViewHolder extends RecyclerView.ViewHolder TextView tvName; public ViewHolder(View itemView) super(itemView); tvName = (TextView) itemView.findViewById(R.)

23、; public static class HeaderHolder extends RecyclerView.ViewHolder public TextView header; public HeaderHolder(View itemView) super(itemView); header = (TextView) itemView; 可以看到这个Adapter实现了之前上面的接口,接口的实现方式和普通的Adapter的实现都类似在onCreateHeaderViewHolder中加载xml文件,在onBindHeaderViewHolder中加载数据,HeaderHolder也是一样

24、的,headerId是采用的一个letter的,也就是每一条的首字母。下面看看ItemDecoration是怎么实现的。getItemOffsets方法首先看看第一个getItemOffsets方法的实现java view plain copy 在CODE上查看代码片派生到我的代码片Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) super.getItemOffsets(outRect, view, parent, state)

25、; /得到该item所在的位置 int position = parent.getChildAdapterPosition(view); int headerHeight = 0; /在使用adapterPosition时最好的加上这个判断 if (position != RecyclerView.NO_POSITION & hasHeader(position) /获取到ItemDecoration所需要的高度 View header = getHeader(parent, position).itemView; headerHeight = header.getHeight(); outR

26、ect.set(0, headerHeight, 0, 0); 很简单,就是判断如果这个item需要ItemDecoration就获取到header的高度,设置给outRect判断是否需要header判断是否需要header的方法,之前不是在Adapter里面写了getHeaderId的方法吗,这里就用到了,根据前两个headerId是否相同来判断是否需要设置ItemDecorationjava view plain copy 在CODE上查看代码片派生到我的代码片private boolean hasHeader(int position) if (position = 0) /第一个位置必

27、然有 return true; /判断和上一个的id不同则有header int previous = position - 1; return !mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous); 获取Header的方法java view plain copy 在CODE上查看代码片派生到我的代码片private RecyclerView.ViewHolder getHeader(RecyclerView parent, int position) /创建HeaderViewHolder Medicine

28、Adapter.HeaderHolder holder = mAdapter.onCreateHeaderViewHolder(parent); final View header = holder.itemView; /绑定数据 mAdapter.onBindHeaderViewHolder(holder, position); /测量View并且layout int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); int heightSpec = View.

29、MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); /根据父View的MeasureSpec和子view自身的LayoutParams以及padding来获取子View的MeasureSpec int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width); int chi

30、ldHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height); /进行测量 header.measure(childWidth, childHeight); /根据测量后的宽高放置位置 header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight(); return holder; 在这里面调用了之前A

31、dapter实现接口里面的方法,创建了ViewHolder,绑定了数据。都知道自定义view需要实现onMeasure、onLayout、onDraw方法,所以在这里对它进行了测量和放置,而draw是在onDrawOver里面实现的。对这里熟悉的可以去看看相关的知识点。onDrawOver方法java view plain copy 在CODE上查看代码片派生到我的代码片Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) final int count = parent

32、.getChildCount(); for (int layoutPos = 0; layoutPos count; layoutPos+) final View child = parent.getChildAt(layoutPos); final int adapterPos = parent.getChildAdapterPosition(child); /只有在最上面一个item或者有header的item才绘制ItemDecoration if (adapterPos != RecyclerView.NO_POSITION & (layoutPos = 0 | hasHeader(a

33、dapterPos) View header = getHeader(parent, adapterPos).itemView; c.save(); final int left = child.getLeft(); final int top = getHeaderTop(parent, child, header, adapterPos, layoutPos); c.translate(left, top); header.setTranslationX(left); header.setTranslationY(top); header.draw(c); c.restore(); 这里就

34、是一个循环,在需要Header的地方进行绘制,当然需要把画布移动到要绘制的位置,主要是确定它距离顶部的大小。java view plain copy 在CODE上查看代码片派生到我的代码片private int getHeaderTop(RecyclerView parent, View child, View header, int adapterPos, int layoutPos) int headerHeight = header.getHeight(); int top = (int) child.getY() - headerHeight; if (layoutPos = 0) /

35、处理最上面两个ItemDecoration切换时 final int count = parent.getChildCount(); final String currentId = mAdapter.getHeaderId(adapterPos); for (int i = 1; i count; i+) int adapterPosHere = parent.getChildAdapterPosition(parent.getChildAt(i); if (adapterPosHere != RecyclerView.NO_POSITION) String nextId = mAdapte

36、r.getHeaderId(adapterPosHere); if (!nextId.equals(currentId) /找到下一个不同类的view final View next = parent.getChildAt(i); /这里计算offset画个图会很清楚 final int offset = (int) next.getY() - (headerHeight + getHeader(parent, adapterPosHere).itemView.getHeight(); if (offset 0) /如果大于0的话,此时并没有切换 return offset; else bre

37、ak; /top不能小于0,否则最上面的ItemDecoration不会一直存在 top = Math.max(0, top); return top; 这里的逻辑是这样的:1.当此view不是最上面的显示的时候,header距离顶部直接就是此view距离顶部距离减去header的高度即可 2.当此view是最上面的view的时候,首先用for循环找到它下一个和它headerId不同的第一个view,利用找到的这个view和它本身来计算出它的header距离顶部的距离,当这个距离大于0时,代表此view的header还全部显示出来,这时直接用上面的方式获取这个距离,当这个距离小于0时就是我们所

38、希望的。 OK完成,下面贴出ItemDecoration的所以代码java view plain copy 在CODE上查看代码片派生到我的代码片/* * Created by lzy on 2016/11/23. */ public class StickyItemDecoration extends RecyclerView.ItemDecoration private static final String TAG = lzy; private MedicineAdapter mAdapter; public StickyItemDecoration(MedicineAdapter mA

39、dapter) super(); this.mAdapter = mAdapter; /最后调用 绘制顶部固定的header Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) final int count = parent.getChildCount(); for (int layoutPos = 0; layoutPos count; layoutPos+) final View child = parent.getChildAt(layoutPos); fina

40、l int adapterPos = parent.getChildAdapterPosition(child); /只有在最上面一个item或者有header的item才绘制ItemDecoration if (adapterPos != RecyclerView.NO_POSITION & (layoutPos = 0 | hasHeader(adapterPos) View header = getHeader(parent, adapterPos).itemView; c.save(); final int left = child.getLeft(); final int top =

41、 getHeaderTop(parent, child, header, adapterPos, layoutPos); c.translate(left, top); header.setTranslationX(left); header.setTranslationY(top); header.draw(c); c.restore(); Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) super.getItemOffset

42、s(outRect, view, parent, state); /得到该item所在的位置 int position = parent.getChildAdapterPosition(view); int headerHeight = 0; /在使用adapterPosition时最好的加上这个判断 if (position != RecyclerView.NO_POSITION & hasHeader(position) /获取到ItemDecoration所需要的高度 View header = getHeader(parent, position).itemView; headerHe

43、ight = header.getHeight(); outRect.set(0, headerHeight, 0, 0); /* * 判断是否有header * * param position * return */ private boolean hasHeader(int position) if (position = 0) /第一个位置必然有 return true; /判断和上一个的id不同则有header int previous = position - 1; return !mAdapter.getHeaderId(position).equals(mAdapter.get

44、HeaderId(previous); /* * 获得自定义的Header * * param parent * param position * return */ private RecyclerView.ViewHolder getHeader(RecyclerView parent, int position) /创建HeaderViewHolder MedicineAdapter.HeaderHolder holder = mAdapter.onCreateHeaderViewHolder(parent); final View header = holder.itemView; /绑定数据 mAdapter.onBindHeaderViewHolder(holder, position); /测量View并且layout int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论