本篇文章摘自微信公众号 guolin_blog (郭霖)独家发布

毫无疑问,RecyclerView 是现在 Android 世界中最重要的系统组件之一,它的出现就是为了高效代替 ListView 和 GridView。当时它的出现解决了我一个大的需求,这个需求就是在电视盒子界面上横向加载应用列表,由于 ListView 没有横向加载的功能,而网络上开源的那些 HorizontalListView 又不满足需求,所以我们只能自定义 ViewGroup 来实现需求,但是回收机制不是很完善,所以性能并不好,所以当 RecyclerView 横空出世时,我第一时间拥抱了它,并推荐 Android 开发小组成员们去了解它。

但后来,我发现 RecyclerView 除了比 ListView 好用外,某些地方它却更复杂了,它将更多的权力交给了开发者自己,比如布局,比如 ITEM 的分割线,比如点击监听等等。但总归它是好东西,所以我们得多花些时间来学习,平常开发我们一般按照 RecyclerView 的基本用法便可以实现绝大多数需求,但是某些场景下却远远不够,比如我们不想局限于 LinearLayoutManager 想自己定义 LayoutManager,我们需要定义时光轴的效果,我们想实现美妙的添加删除动画等等,这些情况下解决问题的话需要我们对 RecyclerView 本身有足够的了解。

今天,这篇文章不讲 RecyclerView 基本的知识和用法,讲它一个有趣的知识点 ItemDecoration。

ItemDecoration
Decoration 的英文意思是装饰物的意思,引申到这里来,肯定也是与 RecyclerView 的界面装饰有关。我们常见的就是分割线了。
我们在使用 ListView 的时候只要在 xml 文件中,使用 android:divider 就可以,但是很遗憾 RecyclerView 却没有相应的控制。

我们新建一个工程,然后在一个页面里面添加一个 RecyclerView。创建相关的 Adapter,加载布局文件,这里布局文件很简单,就是一个 TextView,再之后在 Activity 初始化它。

  1. public class DividerActivity extends AppCompatActivity {
  2. RecyclerView mRecyclerView;
  3. List<String> data;
  4. TestAdapter mAdapter;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_divider);
  9. mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
  10. initDatas();
  11. mAdapter = new TestAdapter(data);
  12. mRecyclerView.setAdapter(mAdapter);
  13. LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
  14. layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
  15. mRecyclerView.setLayoutManager(layoutmanager);
  16. }
  17.  
  18. private void initDatas() {
  19. data = new ArrayList<>();
  20. for (int i = ; i < ;i++) {
  21. data.add(i+" test ");
  22. }
  23. }
  24. }

可以看到所有的选项都混在一起,为了美观应该需要 1 px 的分割线,之前我一般在 Item 的布局文件中设置它的 topMargin 或者是 bottomMargin,所以我们可以在相关的 Adapter 中这样修改。

  1. public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  2. View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item,parent,false);
  3. RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
  4. layoutParams.topMargin = ;
  5. view.setLayoutParams(layoutParams);
  6. TestHolder holder = new TestHolder(view);
  7. return holder;
  8. }

效果如下:

现在我们同样可以通过给 RecyclerView 添加 ItemDecoration 来实现它。

首先,我们需要自定义一个 ItemDecoration,按照目前的需求,我们只需要实现它的一个方法就可以了。

  1. public class TestDividerItemDecoration extends RecyclerView.ItemDecoration {
  2.  
  3. @Override
  4. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  5. super.getItemOffsets(outRect, view, parent, state);
  6.  
  7. // //如果不是第一个,则设置top的值。
  8. if (parent.getChildAdapterPosition(view) != ){
  9. //这里直接硬编码为1px
  10. outRect.top = ;
  11. }
  12. }
  13. }

然后在 Activity 中添加它到 RecyclerView 就可以了。

  1. mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
  2. initDatas();
  3. mAdapter = new TestAdapter(data);
  4. mRecyclerView.setAdapter(mAdapter);
  5. LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
  6. layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
  7. mRecyclerView.setLayoutManager(layoutmanager);
  8. mRecyclerView.addItemDecoration(new TestDividerItemDecoration());

效果如图:

getItemOffsets()
我们可以看到自定义的 TestDividerItemDeoration 只实现了一个方法 getItemOffsets()。方法里面有四个参数。

Rect outRect
View view
RecyclerView parent
RecyclerView.State state
这四个参数分别干什么的呢?我们不妨在 AndroidStudio 中按 Ctrl 键点击方法名,就可以到了它被调用的位置。

  1. Rect getItemDecorInsetsForChild(View child) {
  2. final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  3. if (!lp.mInsetsDirty) {
  4. return lp.mDecorInsets;
  5. }
  6.  
  7. if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
  8. // changed/invalid items should not be updated until they are rebound.
  9. return lp.mDecorInsets;
  10. }
  11. final Rect insets = lp.mDecorInsets;
  12. insets.set(, , , );
  13. final int decorCount = mItemDecorations.size();
  14. for (int i = ; i < decorCount; i++) {
  15. mTempRect.set(, , , );
  16. //在这里被调用
  17. mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
  18. insets.left += mTempRect.left;
  19. insets.top += mTempRect.top;
  20. insets.right += mTempRect.right;
  21. insets.bottom += mTempRect.bottom;
  22. }
  23. lp.mInsetsDirty = false;
  24. return insets;
  25. }

我们注意这一行代码 java mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState); 很容易得知,outRect 是一个全为 0 的 Rect。view 指 RecyclerView 中的 Item。parent 就是 RecyclerView 本身,state 就是一个状态。

我们可以看下面的这张图。

绿色区域代表 RecyclerView 中的一个 ItemView,而外面橙色区域也就是相应的 outRect,也就是 ItemView 与其它组件的偏移区域,等同于 margin 属性,通过复写 getItemOffsets() 方法,然后指定 outRect 中的 top、left、right、bottom 就可以控制各个方向的间隔了。注意的是这些属性都是偏移量,是指偏移 ItemView 各个方向的数值。在上面的例子中我设置了 outRect.top = 1; 所以每个 ItemView 之间有 1 px 的空隙,而这 1 px 空隙透露了下面背景色,所以看起来就像是分隔线,这实现了简单的分隔线效果,但这种方法分隔线的效果只能取决于背景色,如果我要定制分割线的颜色呢?这个时候就要讲到一个新的方法名 onDraw()。

onDraw()
在 Android 中的每一个 View 中 onDraw() 是很重要的一个方法,用来绘制组件的UI效果,所以在 ItemDecocration 中它自然也是用来绘制外观的。我们来看它的方法声明。
java public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);

可以看到它传递了一个 Canvas 参数对象,所以它拥有了绘制的能力。但是怎么绘制呢?

其实它是配合了前面的 getItemOffsets 方法一起使用的,getItemOffsets 撑开了 ItemView 的上下左右间隔区域,而 onDraw 方法通过计算每个 ItemView 的坐标位置与它的 outRect 值来确定它要绘制内容的区间。

假设,我们要设计一个高度为 2 px 的红色分割线,那么我们就需要在每个 ItemView top位置上方画一个 2 px 高度的矩形,然后填充颜色为红色。
需要注意的一点是 getItemOffsets 是针对每一个 ItemView,而 onDraw 方法却是针对 RecyclerView 本身,所以在 onDraw 方法中需要遍历屏幕上可见的 ItemView,分别获取它们的位置信息,然后分别的绘制对应的分割线。

我们看下面的这张示意图

为了便于观察我将第一条分割线的颜色透明化了,我们可以看到每条分割线绘制的区域其实就是 outRect.top 至 ItemView.top 之间的区域,所以我们就需要在当初 getOffsets 方法进行位置偏移时就记录下每个 itemView 向上的间隔距离,之后的逻辑就是遍历屏幕上的 View,然后描绘分割线。

  1. public class ColorDividerItemDecoration extends RecyclerView.ItemDecoration {
  2.  
  3. private float mDividerHeight;
  4.  
  5. private Paint mPaint;
  6.  
  7. public ColorDividerItemDecoration() {
  8. mPaint = new Paint();
  9. mPaint.setAntiAlias(true);
  10. mPaint.setColor(Color.RED);
  11. }
  12.  
  13. @Override
  14. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  15. super.getItemOffsets(outRect, view, parent, state);
  16.  
  17. // //第一个ItemView不需要在上面绘制分割线
  18. if (parent.getChildAdapterPosition(view) != ){
  19. //这里直接硬编码为1px
  20. outRect.top = ;
  21. mDividerHeight = ;
  22. }
  23. }
  24.  
  25. @Override
  26. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  27. super.onDraw(c, parent, state);
  28.  
  29. int childCount = parent.getChildCount();
  30.  
  31. for ( int i = ; i < childCount; i++ ) {
  32. View view = parent.getChildAt(i);
  33.  
  34. int index = parent.getChildAdapterPosition(view);
  35. //第一个ItemView不需要绘制
  36. if ( index == ) {
  37. continue;
  38. }
  39.  
  40. float dividerTop = view.getTop() - mDividerHeight;
  41. float dividerLeft = parent.getPaddingLeft();
  42. float dividerBottom = view.getTop();
  43. float dividerRight = parent.getWidth() - parent.getPaddingRight();
  44.  
  45. c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);
  46. }
  47. }
  48. }

然后我们在 Activity 将 ColorDividerItemDecoration 添加到相应的 RecyclerView 中就可以了。

  1. mRecyclerView.addItemDecoration(new ColorDividerItemDecoration());

效果如下图:

至此,红色的分割线就搞定了。

但一定要注意的是,onDraw 方法可不只能绘制简单的线条,它可是拥有 Canvas 的,所以画圆、画矩形、画弧形、绘制图片都不在话下。为了提高本篇代码的技术含量,下面我们通过 ItemDecoration 来实现一个时光轴的效果。

通过 ItemDecoration 实现时光轴的效果
编码的开始先做设计,或者说先思考。思考我们要做什么,或者说要怎么做。

我们可以看到左边白色的图案就大概是我们时光轴要绘制的图形。我们通过 getItemOffsets 方法来对 ItemView 进行 left 和 top 的间距设置。然后确定好轴线的起始坐标,中间轴结点的图形或者是图案。我们可以通过 ItemView 将相应的时光轴片断分解,如下图。

主要是一些参数的确定,例如 DividerHeight,注意这个 DividerHeight 不是指 ItemView 向上的间隔值,而是相应的 ItemDecoration 的高度。中心坐标 (centerX,centerY),还有上下两段轴线的起始坐标。有了这些参数后,我们就能轻松地编码了。

  1. public class TimelineItemDecoration extends RecyclerView.ItemDecoration {
  2.  
  3. private Paint mPaint;
  4. //ItemView左边的间距
  5. private float mOffsetLeft;
  6. //ItemView右边的间距
  7. private float mOffsetTop;
  8. //时间轴结点的半径
  9. private float mNodeRadius;
  10.  
  11. public TimelineItemDecoration(Context context) {
  12. mPaint = new Paint();
  13. mPaint.setAntiAlias(true);
  14. mPaint.setColor(Color.RED);
  15.  
  16. mOffsetLeft = context.getResources().getDimension(R.dimen.timeline_item_offset_left);
  17. mNodeRadius = context.getResources().getDimension(R.dimen.timeline_item_node_radius);
  18. }
  19.  
  20. @Override
  21. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  22. super.getItemOffsets(outRect, view, parent, state);
  23.  
  24. // //第一个ItemView不需要在上面绘制分割线
  25. if (parent.getChildAdapterPosition(view) != ){
  26. //这里直接硬编码为1px
  27. outRect.top = ;
  28. mOffsetTop = ;
  29. }
  30.  
  31. outRect.left = (int) mOffsetLeft;
  32. }
  33.  
  34. @Override
  35. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  36. super.onDraw(c, parent, state);
  37.  
  38. int childCount = parent.getChildCount();
  39.  
  40. for ( int i = ; i < childCount; i++ ) {
  41. View view = parent.getChildAt(i);
  42.  
  43. int index = parent.getChildAdapterPosition(view);
  44.  
  45. float dividerTop = view.getTop() - mOffsetTop;
  46. //第一个ItemView 没有向上方向的间隔
  47. if ( index == ) {
  48. dividerTop = view.getTop();
  49. }
  50.  
  51. float dividerLeft = parent.getPaddingLeft();
  52. float dividerBottom = view.getBottom();
  53. float dividerRight = parent.getWidth() - parent.getPaddingRight();
  54.  
  55. float centerX = dividerLeft + mOffsetLeft / ;
  56. float centerY = dividerTop + (dividerBottom - dividerTop) / ;
  57.  
  58. float upLineTopX = centerX;
  59. float upLineTopY = dividerTop;
  60. float upLineBottomX = centerX;
  61. float upLineBottomY = centerY - mNodeRadius;
  62.  
  63. //绘制上半部轴线
  64. c.drawLine(upLineTopX,upLineTopY,upLineBottomX,upLineBottomY,mPaint);
  65.  
  66. //绘制时间轴结点
  67. c.drawCircle(centerX,centerY,mNodeRadius,mPaint);
  68.  
  69. float downLineTopX = centerX;
  70. float downLineTopY = centerY + mNodeRadius;
  71. float downLineBottomX = centerX;
  72. float downLineBottomY = dividerBottom;
  73.  
  74. //绘制上半部轴线
  75. c.drawLine(downLineTopX,downLineTopY,downLineBottomX,downLineBottomY,mPaint);
  76. }
  77. }
  78. }

然后效果如下图:

 
感觉不怎么美观,我们尝试将结点的实心圆改成空心圆。

  1. //绘制时间轴结点
  2. mPaint.setStyle(Paint.Style.STROKE);
  3. c.drawCircle(centerX,centerY,mNodeRadius,mPaint);
  4. mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

效果如下:

感觉美观了许多。

同时,我们可以将轴结点用图标代替圆或者圆圈。

上面的图标感觉更好看了。

需要注意的是 onDraw 方法,ItemDecoration 是在 ItemView 的下方绘制的,也就是 ItemView 可能会覆盖 ItemDecoration 的内容。我们可以验证一下,在时间轴与 ItemView 的边界画一个完整的圆,观察它的效果。

  1. mPaint.setStyle(Paint.Style.STROKE);
  2. c.drawCircle(view.getLeft(),centerY,mNodeRadius,mPaint);
  3.  
  4. mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

可以看到,在重合的地方,圆圈确实被 ItemView 内容覆盖了。

大家可能会想到,ItemDecoration 内容能不能覆盖在 ItemView 内容之上呢?

答案是肯定的,但不是在 onDraw() 方法实现,而是另外一个方法 onDrawOver()。

onDrawOver 和角标。
现实中的APP或者网站经常有一些排行榜比如下面:

或者这样。

这些角标都是绘制在 ItemView 之上的,现在有了 ItemDecoration 我们也可以轻松而优雅地实现它。

比如我们要实现一个图书销量排行榜。我们有大概的草图。

然后我们就可以编码了。
布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent" android:layout_height="wrap_content"
  4. android:background="@android:color/white">
  5.  
  6. <TextView
  7. android:id="@+id/tv_rank_oder"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_centerVertical="true"
  11. android:layout_alignParentLeft="true"
  12. android:layout_marginLeft="24dp"
  13. android:gravity="center"
  14. android:textColor="#6c6c6c"/>
  15.  
  16. <ImageView
  17. android:id="@+id/iv_cover"
  18. android:layout_width="80dp"
  19. android:layout_height="80dp"
  20. android:layout_centerVertical="true"
  21. android:layout_marginLeft="60dp"/>
  22.  
  23. <TextView
  24. android:id="@+id/tv_title"
  25. android:layout_width="wrap_content"
  26. android:layout_height="wrap_content"
  27. android:layout_toRightOf="@id/iv_cover"
  28. android:layout_marginLeft="12dp"
  29. android:layout_marginTop="12dp"
  30. android:textColor="@android:color/black"/>
  31. <TextView
  32. android:id="@+id/tv_price"
  33. android:layout_width="wrap_content"
  34. android:layout_height="wrap_content"
  35. android:layout_toRightOf="@id/iv_cover"
  36. android:layout_below="@id/tv_title"
  37. android:layout_marginLeft="12dp"
  38. android:layout_marginTop="12dp"
  39. android:textColor="@android:color/holo_red_dark"/>
  40. </RelativeLayout>

相应的 Adapter 代码:

  1. public class BookRankAdapter extends RecyclerView.Adapter<BookRankAdapter.TestHolder> {
  2.  
  3. List<String> data;
  4. int[] mIconResouces;
  5. public BookRankAdapter(List<String> data,int[] ids) {
  6. this.data = data;
  7. this.mIconResouces = ids;
  8. }
  9.  
  10. public void setData(List<String> data,int[] ids) {
  11. this.data = data;
  12. mIconResouces = ids;
  13. notifyDataSetChanged();
  14. }
  15.  
  16. @Override
  17. public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  18. View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_ranklist_item,parent,false);
  19. TestHolder holder = new TestHolder(view);
  20. return holder;
  21. }
  22.  
  23. @Override
  24. public void onBindViewHolder(TestHolder holder, int position) {
  25. if (data != null && data.size() > ) {
  26. String text = data.get(position);
  27.  
  28. String[] infos = text.split("-");
  29. holder.tvOrder.setText(position+"");
  30. holder.tvTitle.setText(infos[]);
  31. holder.tvPrice.setText(infos[]);
  32.  
  33. holder.ivCover.setImageResource(mIconResouces[position]);
  34. }
  35. }
  36.  
  37. @Override
  38. public int getItemCount() {
  39. return data == null ? : data.size();
  40. }
  41.  
  42. static class TestHolder extends RecyclerView.ViewHolder{
  43. public TextView tvOrder;
  44. public TextView tvTitle;
  45. public TextView tvPrice;
  46. public ImageView ivCover;
  47. public TestHolder(View itemView) {
  48. super(itemView);
  49.  
  50. tvOrder = (TextView) itemView.findViewById(R.id.tv_rank_oder);
  51. tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
  52. tvPrice = (TextView) itemView.findViewById(R.id.tv_price);
  53. ivCover = (ImageView) itemView.findViewById(R.id.iv_cover);
  54.  
  55. }
  56.  
  57. }
  58. }

自定义 FlagItemDecoration

  1. public class FlagItemDecoration extends RecyclerView.ItemDecoration {
  2. private Paint mPaint;
  3. private Bitmap mIcon;
  4. private float mFlagLeft;
  5.  
  6. public FlagItemDecoration(Context context) {
  7. mPaint = new Paint();
  8. mPaint.setAntiAlias(true);
  9. mPaint.setColor(Color.RED);
  10.  
  11. mIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.hotsale);
  12. mFlagLeft = context.getResources().getDimension(R.dimen.flag_left);
  13. }
  14.  
  15. @Override
  16. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  17. // super.getItemOffsets(outRect, view, parent, state);
  18.  
  19. //第一个ItemView不需要在上面绘制分割线
  20. if (parent.getChildAdapterPosition(view) == ){
  21. outRect.top = ;
  22. } else {
  23. outRect.top = ;
  24. }
  25.  
  26. }
  27.  
  28. @Override
  29. public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
  30. super.onDrawOver(c, parent, state);
  31.  
  32. int childCount = parent.getChildCount();
  33.  
  34. for ( int i = ; i < childCount; i++ ) {
  35. View view = parent.getChildAt(i);
  36. int index = parent.getChildAdapterPosition(view);
  37. float top = view.getTop();
  38. if ( index < ) {
  39. c.drawBitmap(mIcon,mFlagLeft,top,mPaint);
  40. }
  41.  
  42. }
  43. }
  44. }

然后在 Activity 进行相应的数据处理,这里的数据都是为了测试用的,所以比较随意。

  1. public class BookRankActivity extends AppCompatActivity {
  2. RecyclerView mRecyclerView;
  3. List<String> data;
  4. BookRankAdapter mAdapter;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_bookrank);
  9. mRecyclerView = (RecyclerView) findViewById(R.id.bookrank_recyclerview);
  10.  
  11. initDatas();
  12. int resouces[] = new int[] {R.drawable.book_renmin,R.drawable.book_huochetou,
  13. R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming
  14. ,R.drawable.book_renmin,R.drawable.book_huochetou,
  15. R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming
  16. ,R.drawable.book_renmin,R.drawable.book_huochetou,
  17. R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming
  18. };
  19. mAdapter = new BookRankAdapter(data,resouces);
  20. mRecyclerView.setAdapter(mAdapter);
  21. LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
  22. layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
  23. mRecyclerView.setLayoutManager(layoutmanager);
  24. mRecyclerView.addItemDecoration(new FlagItemDecoration(this));
  25. }
  26.  
  27. private void initDatas() {
  28. data = new ArrayList<>();
  29. data.add("人民的名义- ¥ 33.5");
  30. data.add("火车头 - ¥ 27.5");
  31. data.add("解忧杂货店- ¥ 19.9");
  32. data.add("TensorFlow - ¥ 102.5");
  33. data.add("王阳明心学 - ¥ 60");
  34.  
  35. data.add("人民的名义1- ¥ 33.5");
  36. data.add("火车头1 - ¥ 27.5");
  37. data.add("解忧杂货店1- ¥ 19.9");
  38. data.add("TensorFlow1 - ¥ 102.5");
  39. data.add("王阳明心学1 - ¥ 60");
  40.  
  41. data.add("人民的名义2 - ¥ 33.5");
  42. data.add("火车头2 - ¥ 27.5");
  43. data.add("解忧杂货店2- ¥ 19.9");
  44. data.add("TensorFlow2 - ¥ 102.5");
  45. data.add("王阳明心学2 - ¥ 60");
  46. }
  47. }

最终效果如下图:

有人在想,通过 ItemView 中的布局文件不就可以完成这样的操作吗?是的,确实是可以的,将 Flag 角标定义在每一个 ItemView 布局文件中,然后在 Adapter 的 onBindViewHolder 方法中根据 postion 的值来决定是否加载角标。

但是这里是为了说明 ItemDecoration 中的 onDrawOver 方法,为了说明它确实能让 ItemDecoration 图像绘制在 ItemView 内容之上。事实上,ItemDecoration 的妙处还有好多好多。

总结
自定义一个 ItemDecoration 通常要根据需要,复写它的 3 个方法。
* getItemOffsets 撑开 ItemView 上、下、左、右四个方向的空间
* onDraw 在 ItemView 内容之下绘制图形
* onDrawOver 在 ItemView 内容之上绘制图形。

提醒
由于文章篇幅,ItemDecoration 最让我兴奋的内容我需要另写一篇文章,那就是通过 ItemDecoration 自定义 RecyclerView 中的头部或者是粘性头部。相信大家对头部这个概念比较了解,现在通过 ItemDecoration 就可以优雅地实现它,记住优雅两个字,条条大路通罗马,但是有人就优雅、有人就显得手忙脚乱。所以,我将文章标题定了一个词,叫小甜点,吃了让人舒心,ItemDecoration 用了也让人舒心。

好了,文章就到这里结束。

github地址

更有意思的内容请看这篇《RecyclerView探索之通过ItemDecoration实现StickyHeader效果》
---------------------
作者:frank909
来源:CSDN
原文:https://blog.csdn.net/briblue/article/details/70161917
版权声明:本文为博主原创文章,转载请附上博文链接!

小甜点,RecyclerView 之 ItemDecoration 讲解及高级特性实践的更多相关文章

  1. Dubbo高级特性实践-泛化调用

    引言 当后端Java服务用Dubbo协议作为RPC方案的基础,但部分消费方是前端Restful的PHP服务,不能直接调用,于是在中间架设了Router服务提供统一的基于HTTP的后端调用入口. 而Ro ...

  2. Dubbo 高级特性实践-泛化调用

    引言 当后端Java服务用Dubbo协议作为RPC方案的基础,但部分消费方是前端Restful的PHP服务,不能直接调用,于是在中间架设了Router服务提供统一的基于HTTP的后端调用入口. 而Ro ...

  3. android RecyclerView (二) ItemDecoration 详解

    RecyclerView 已经推出了一年多了,日常开发中也已经彻底从 ListView 迁移到了 RecyclerView,但前两天有人在一个安卓群里面问了个关于最顶上的 item view 加蒙层的 ...

  4. (转载)RecyclerView之ItemDecoration由浅入深

    RecyclerView之ItemDecoration由浅入深 作者 小武站台 关注 2016.09.19 18:20 字数 1155 阅读 10480评论 15喜欢 91赞赏 3 译文的GitHub ...

  5. 「小程序JAVA实战」小程序视图之细说wx:key列表高级特性(16)

    转自:https://idig8.com/2018/08/09/xiaochengxu-chuji-16/ wx:key的高级特性.这个很重要,因为在app上经常有上拉,下拉加载,我们如果不使用这个特 ...

  6. Python高级特性(切片,迭代,列表生成式,生成器,迭代器)

    掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n = 1 while n ...

  7. jvm高级特性(6)(线程的种类,调度,状态,安全程度,实现安全的方法,同步种类,锁优化,锁种类)

    JVM高级特性与实践(十三):线程实现 与 Java线程调度 JVM高级特性与实践(十四):线程安全 与 锁优化 一. 线程的实现 线程其实是比进程更轻量级的调度执行单位. 线程的引入,可以把一个检查 ...

  8. jvm高级特性(5)(1)(原子性,可见性,有序性,volatile,概述)

    JVM高级特性与实践(十二):高效并发时的内外存交互.三大特征(原子.可见.有序性) 与 volatile型变量特殊规则 简介: 阿姆达尔定律(Amdahl):该定律通过系统中并行化与串行化的比重来描 ...

  9. java高级特性增强

    第4天 java高级特性增强 今天内容安排: 1.掌握多线程 2.掌握并发包下的队列 3.了解JMS 4.掌握JVM技术 5.掌握反射和动态代理 java多线程增强 .1. java多线程基本知识 . ...

随机推荐

  1. Docker(二)搭建和使用Docker

    摘自 https://mp.weixin.qq.com/s/E9tqhe00EjfV8y1pqWkZfw 一.Docker的架构 Docker使用C/S结构,即客户端/服务器体系结构.Docker客户 ...

  2. LinkedStack的底层实现

    package zy813ture; import java.util.EmptyStackException; public class MyLinkedStack1 { private Node ...

  3. Spark思维导图之性能优化

  4. ES学习

    官方参考手册 https://www.elastic.co/guide/en/elasticsearch/reference/5.6/index.html https://www.elastic.co ...

  5. 安装Vmware并破解

    1. 先下载Vmware安装包 链接:http://pan.baidu.com/s/1hsjCKgk 密码:c1o6 2. 解压缩 3. 运行VMware-workstation-full-10.0. ...

  6. Django REST framework 第五章 Relationships & Hyperlinked APIs

    到目前为止,API内部的关系是使用主键来代表的.在这篇教程中,我们将提高API的凝聚力和可发现性,通过在相互关系上使用超链接. Creating an endpoint for the root of ...

  7. Flask里面的cookie的基本操作

    #cookie相关操作,依赖于make_response #调用cookie依赖request模块 from flask import Flask,make_response,request #建立对 ...

  8. DeepLearning.ai-Week2-Keras tutorial-the Happy House

    1 - Import Packages import numpy as np from keras import layers from keras.layers import Input, Dens ...

  9. python3.7中asyncio的具体实现

    讲讲我在使用python异步IO语法时踩过的坑 简单介绍异步IO的原理 以及利用最新语法糖实现异步IO的步骤, 然后给出实现异步的不同例子 网上找了很多python的asyncio示例.很多都是用 # ...

  10. 存在Settings数据在手机系统中的位置

    旧版本Android,将settings数据存在数据库中,{system, secure, global} 对应的是 /data/data/com.android.providers.settings ...