经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android itemDecoration接口实现吸顶悬浮标题
来源:jb51  时间:2022/11/19 17:13:07  对本文有异议

方案

1.设置一个悬浮的视图挂在recycleView顶部,随着item的移动位置,悬浮标题自动跟随移动或者是保持原地不动。

2.使用recyclerView的ItemDecoration,给指定的item设置不同的itemDecoration,并且跟随item的移动而移动或者保持不变。

本文采用第二种方式实现,效果图:

了解ItemDecoration

这是个接口,一共有六个方法:

  1. public static abstract class ItemDecoration {
  2. /**
  3. * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
  4. * Any content drawn by this method will be drawn before the item views are drawn,
  5. * and will thus appear underneath the views.
  6. *
  7. * @param c Canvas to draw into
  8. * @param parent RecyclerView this ItemDecoration is drawing into
  9. * @param state The current state of RecyclerView
  10. */
  11. public void onDraw(Canvas c, RecyclerView parent, State state) {
  12. onDraw(c, parent);
  13. }
  14. /**
  15. * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
  16. * Any content drawn by this method will be drawn after the item views are drawn
  17. * and will thus appear over the views.
  18. *
  19. * @param c Canvas to draw into
  20. * @param parent RecyclerView this ItemDecoration is drawing into
  21. * @param state The current state of RecyclerView.
  22. */
  23. public void onDrawOver(Canvas c, RecyclerView parent, State state) {
  24. onDrawOver(c, parent);
  25. }
  26. /**
  27. * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
  28. * the number of pixels that the item view should be inset by, similar to padding or margin.
  29. * The default implementation sets the bounds of outRect to 0 and returns.
  30. *
  31. * <p>
  32. * If this ItemDecoration does not affect the positioning of item views, it should set
  33. * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
  34. * before returning.
  35. *
  36. * <p>
  37. * If you need to access Adapter for additional data, you can call
  38. * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
  39. * View.
  40. *
  41. * @param outRect Rect to receive the output.
  42. * @param view The child view to decorate
  43. * @param parent RecyclerView this ItemDecoration is decorating
  44. * @param state The current state of RecyclerView.
  45. */
  46. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
  47. getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
  48. parent);
  49. }
  50. /**
  51. * @deprecated
  52. * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
  53. */
  54. @Deprecated
  55. public void onDrawOver(Canvas c, RecyclerView parent) {
  56. }
  57. /**
  58. * @deprecated
  59. * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
  60. */
  61. @Deprecated
  62. public void onDraw(Canvas c, RecyclerView parent) {
  63. }
  64. /**
  65. * @deprecated
  66. * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
  67. */
  68. @Deprecated
  69. public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
  70. outRect.set(0, 0, 0, 0);
  71. }
  72. }

其中有三个方法是@deprecated的,那么我们只需要看以下三个方法:

  1. public void onDraw(Canvas c, RecyclerView parent, State state) {
  2. onDraw(c, parent);
  3. }
  4. public void onDrawOver(Canvas c, RecyclerView parent, State state) {
  5. onDrawOver(c, parent);
  6. }
  7. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
  8. getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
  9. parent);
  10. }

第一个方法的意思是绘制分割线本身;

第二个方法是在item项目绘制完成之后进行的绘制操作(这个会覆盖在item上面);

第三个方法是设置分割线的左间距,上间距,右间距,下间距,保存在outRect中。

如图所示:

其中最底层黄色部分大小是getItemOffsets方法返回的itemDecoration的矩阵设置边距宽度,onDraw方法根据设置的间距宽度来进行绘制黄色区域,其中棕红色部分是onDrawOver方法覆盖绘制在item上层的部分。

利用ItemDecoration来绘制悬浮标题栏

我们给每个需要title的item设置rect.top = titleHeight(标题栏高度);其他的间距可以先不考虑,不重要;

重写onDraw方法,绘制我们的itemDecoration标题栏;

我们需要重写onDrawOver方法,在滑动的时候去判断,

1)如果顶部标题区域是在该标题的items范围之内的滑动的话,那么我们需要覆盖绘制一个处于recyclerView.getpaddingTop位置的title,这样的话这个范围内滑动就有一个悬停的标题栏;

2)如果顶部的标题栏区域恰好下面紧跟着下一个标题栏,那么继续向上滑动的时候,需要下面的标题栏把上面的标题栏顶出界面之外。那么绘制的顶部标题栏的起始位置就是所处的item.bottom - titleHeight的位置。

使用以上三个步骤就可以做出一个流畅并且定制化很高的悬浮标题栏功能了。

代码

  1. class MyDecoration(context: Context): RecyclerView.ItemDecoration() {
  2. var mPaint:Paint? = null
  3. var mPaint2:Paint? = null
  4. var mTextPaint:Paint? = null
  5. var mTitleHeight:Int? = null
  6. var mTitleHeight2:Int? = null
  7. var mTitleTextSize:Float? = null
  8. init {
  9. mTitleHeight =
  10. TypedValue.applyDimension(
  11. TypedValue.COMPLEX_UNIT_DIP,
  12. 30f,
  13. context.getResources().getDisplayMetrics()
  14. ).toInt()
  15. mTitleHeight2 =
  16. TypedValue.applyDimension(
  17. TypedValue.COMPLEX_UNIT_DIP,
  18. 3f,
  19. context.getResources().getDisplayMetrics()
  20. ).toInt()
  21. mTitleTextSize =
  22. TypedValue.applyDimension(
  23. TypedValue.COMPLEX_UNIT_SP,
  24. 16f,
  25. context.getResources().getDisplayMetrics()
  26. )
  27. mTextPaint = Paint()
  28. mTextPaint?.let {
  29. it.setTextSize(mTitleTextSize!!)
  30. it.setAntiAlias(true)
  31. it.setColor(Color.WHITE)
  32. }
  33. mPaint = Paint()
  34. mPaint?.let {
  35. it.setAntiAlias(true)
  36. it.setColor(Color.RED)
  37. }
  38. mPaint2 = Paint()
  39. mPaint2?.let {
  40. it.setAntiAlias(true)
  41. it.setColor(Color.BLUE)
  42. }
  43. }
  44. /**
  45. * recyclerView绘制onDraw -> item.onDraw -> onDrawOver
  46. */
  47. override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
  48. for (index in 0 until parent.childCount) {
  49. val childView = parent.getChildAt(index)
  50. childView?.let {
  51. val rect = Rect()
  52. val position = parent.getChildAdapterPosition(it)
  53. if (isTitleItem(position)) {
  54. rect.top = childView.top - mTitleHeight!!
  55. rect.bottom = childView.top
  56. } else {
  57. rect.top = childView.top - mTitleHeight2!!
  58. rect.bottom = childView.top
  59. }
  60. rect.left = parent.paddingLeft
  61. rect.right = parent.width - parent.paddingRight
  62. if (isTitleItem(position)) {
  63. mPaint?.let { it1 -> c.drawRect(rect, it1) }
  64. mTextPaint?.let { it3 ->
  65. c.drawText(
  66. getTitleStr(position),
  67. 0f,
  68. rect.top.toFloat() + (mTitleHeight?.div(2.00f)?:0f),
  69. it3)}
  70. } else {
  71. mPaint2?.let { it1 -> c.drawRect(rect, it1) }
  72. }
  73. }
  74. }
  75. }
  76. /**
  77. * recyclerView绘制onDraw -> item.onDraw -> onDrawOver
  78. * 绘制覆盖在item上面的分割线效果
  79. */
  80. override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
  81. val childView = parent.getChildAt(0)
  82. var nextView:View? = null;
  83. if (1 < parent.childCount) {
  84. nextView = parent.getChildAt(1)
  85. }
  86. childView?.let {
  87. val rect = Rect()
  88. val position = parent.getChildAdapterPosition(it)
  89. mTitleHeight?.let { height ->
  90. if (nextView != null
  91. && it.bottom - height < parent.paddingTop
  92. && isTitleItem(parent.getChildAdapterPosition(nextView))
  93. && !isSameTitle(parent.getChildAdapterPosition(nextView),position)) {
  94. rect.top = it.bottom - height
  95. rect.bottom = it.bottom
  96. } else {
  97. rect.top = parent.paddingTop
  98. rect.bottom = rect.top + height
  99. }
  100. }
  101. rect.left = parent.paddingLeft
  102. rect.right = parent.width - parent.paddingRight
  103. mPaint?.let { it1 -> c.drawRect(rect, it1) }
  104. mTextPaint?.let { it3 ->
  105. c.drawText(
  106. getTitleStr(position),
  107. 0f,
  108. rect.top + (mTitleHeight?.div(2.00f)?:0f),
  109. it3)}
  110. }
  111. }
  112. /**
  113. * 用于设置item周围的偏移量的,类似于设置padding喝margin效果,
  114. * 空出的效果用于绘制分割线
  115. */
  116. override fun getItemOffsets(
  117. outRect: Rect,
  118. view: View,
  119. parent: RecyclerView,
  120. state: RecyclerView.State
  121. ) {
  122. val position:Int = parent.getChildAdapterPosition(view)
  123. if (position % 4 == 0) {
  124. outRect.top = mTitleHeight!!
  125. } else{
  126. outRect.top = mTitleHeight2!!
  127. }
  128. }
  129. fun isTitleItem(position: Int):Boolean {
  130. return position % 4 == 0
  131. }
  132. fun getTitleStr(position: Int):String {
  133. return "标题:${position / 4}"
  134. }
  135. fun isSameTitle(position1: Int,position2: Int):Boolean {
  136. return (position1 / 4) == (position2 / 4)
  137. }
  138. }

到此这篇关于Android itemDecoration接口实现吸顶悬浮标题的文章就介绍到这了,更多相关Android吸顶悬浮标题内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号