本文主要讲述得方式是通过自定义RecyclerView.ItemDecoration来实现RecyclerView的吸顶效果
本文使用Kotlin代码
实现的方式:主要是通过重写绘制方法 onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State)
文中吸顶的内容是通过绘制单行Text文字+背景,RecyclerView的Item是一个含有TextView的LinearLayout
涉及文字居中绘制会使用paint.drawText(),baseLine的获取采用的方式:
// 方式一 float baseline = targetRect.top + (targetRect.bottom - targetRect.top)/2f - (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.top; // 方式二 float baseline = targetRect.top + (targetRect.bottom - targetRect.top)/2f + (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
自定义RecyclerView.ItemDecoration:
class ThisItemDecoration : RecyclerView.ItemDecoration() { private var mDrawable:Drawable = ColorDrawable(Color.BLACK) /** * 间隔高度 * */ private val dividerHeight = 30.dp2px() /**
* 顶高
* */ private val headerTopHeight = 66.dp2px() private val paintHeaderText = Paint().apply { isDither = true isAntiAlias = true color = Color.YELLOW textSize = 18.dp2px().toFloat() } private val paintHeader = Paint().apply { isDither = true isAntiAlias = true style = Paint.Style.FILL color = Color.RED }
// 标记顶的横线 private val paint3 = Paint().apply { isDither = true isAntiAlias = true style = Paint.Style.FILL strokeWidth = 1.dp2px().toFloat() color = Color.BLACK } override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { canvas.save() var left = 0 var right = parent.width if (parent.clipToPadding) { left = parent.paddingLeft right = parent.width - parent.paddingRight canvas.clipRect( left, parent.paddingTop, right, parent.height - parent.paddingBottom ) } if (parent.adapter !is ThisRecyclerViewAdapter) { return } val adapter = parent.adapter as ThisRecyclerViewAdapter val position = (parent.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() val isHeader = adapter.isHeader(position + 1) if(isHeader) { val itemView = parent.findViewHolderForAdapterPosition(position + 1)?.itemView if (null != itemView) { val bottom = min(headerTopHeight * 1.0f, itemView.top - headerTopHeight - parent.paddingTop.toFloat()) val top = bottom - headerTopHeight val targetRect = RectF(left.toFloat(), top + parent.paddingTop, right.toFloat(), bottom + parent.paddingTop) canvas.drawRect(targetRect, paintHeader) val groupType = adapter.getTypeByPositionString(position) val fontMetrics = paintHeaderText.fontMetrics val baseline = targetRect.top + (targetRect.bottom - targetRect.top) / 2f - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top canvas.drawText( groupType, left.toFloat() + 5.dp2px(), baseline, paintHeaderText) } return } val top = parent.paddingTop.toFloat() val bottom = top + headerTopHeight val targetRect = RectF(left.toFloat(), top, right.toFloat(), bottom) canvas.drawRect(targetRect, paintHeader) val groupType = adapter.getTypeByPositionString(position) val fontMetrics = paintHeaderText.fontMetrics val baseline = targetRect.top + (targetRect.bottom - targetRect.top) / 2f - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top canvas.drawText( groupType, left.toFloat() + 5.dp2px(), baseline, paintHeaderText) canvas.drawLine(0f, top + headerTopHeight / 2f, right.toFloat(), top + headerTopHeight / 2f, paint3) canvas.restore() } override fun onDraw( canvas: Canvas, parent: RecyclerView, state: RecyclerView.State ) { canvas.save() var left = 0 var right = parent.width if (parent.clipToPadding) { left = parent.paddingLeft right = parent.width - parent.paddingRight canvas.clipRect( left, parent.paddingTop, right, parent.height - parent.paddingBottom ) } val childCount = parent.childCount; for (i in 0 until childCount) { val child = parent.getChildAt(i) drawHeaderText(canvas, parent, child, left, right) val lp = child.layoutParams as RecyclerView.LayoutParams val top: Int = child.bottom + lp.bottomMargin val bottom: Int = top + dividerHeight mDrawable.setBounds(left, top, right, bottom); mDrawable.draw(canvas) } canvas.restore() } private fun drawHeaderText( canvas: Canvas, parent: RecyclerView, view: View, left: Int, right: Int ): Boolean { if (parent.adapter !is ThisRecyclerViewAdapter) { return false } val adapter = parent.adapter as ThisRecyclerViewAdapter val position = parent.getChildLayoutPosition(view) if (adapter.isHeader(position)) { val targetRect = RectF(left.toFloat(), (view.top - headerTopHeight).toFloat(), right.toFloat(), view.top.toFloat()) canvas.drawRect(targetRect, paintHeader) val groupType = adapter.getTypeByPositionString(position) val fontMetrics = paintHeaderText.fontMetrics val baseline = targetRect.top + (targetRect.bottom - targetRect.top) / 2f - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top canvas.drawText( groupType, left.toFloat() + 5.dp2px(), baseline, paintHeaderText) return true } return false } /** * 设置预留空间 */ override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State ) { if (parent.adapter is ThisRecyclerViewAdapter) { val adapter = parent.adapter as ThisRecyclerViewAdapter val position = parent.getChildLayoutPosition(view) if (adapter.isHeader(position)) { outRect.set(0, headerTopHeight, 0, dividerHeight) } else { if(adapter.isLastLine(position)) { outRect.set(0, 0, 0, 0) } else { outRect.set(0, 0, 0, dividerHeight) } } return } outRect.set(0, 0, 0, 0) } }
RecyclerView的Item的Bean:
class Carrier constructor(val type: CarrierType) { private var id by Delegates.notNull<Int>() var name by Delegates.notNull<String>() var detail by Delegates.notNull<String>() var segment: MutableList<String> = arrayListOf() init { when (type) { CarrierType.TYPE_TANK -> { id = 1 name = "坦克" detail = "攻击性载具,攻城略地必备" } CarrierType.TYPE_ARMORED_CAR -> { id = 2 name = "装甲车" detail = "运输士兵,能极大的保证运输效率和士兵安全" } } } } enum class CarrierType { // 坦克 TYPE_TANK, // 装甲车 TYPE_ARMORED_CAR, // 导弹车 }
Adapter中重要的方法:
private var list: MutableList<Carrier> = arrayListOf() fun isHeader(position: Int): Boolean { return if (itemCount <= 1) { false } else { if (position == 0 && position + 1 < itemCount) { list[position].name == list[position + 1].name } else { list[position].name != list[position - 1].name } } } fun isLastLine(position: Int): Boolean { return position == itemCount - 1 } fun getTypeByPositionString(position: Int): String { return when (list[position].type) { CarrierType.TYPE_TANK -> { "载具:坦克" } CarrierType.TYPE_ARMORED_CAR -> { "载具:装甲车辆" } else -> { "" } } }
MainActivity的实现:
class MainActivity : AppCompatActivity() { private lateinit var bd2: LayoutTest2Binding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) test2() } private fun test2() { bd2 = LayoutTest2Binding.inflate(layoutInflater) setContentView(bd2.root) val list = arrayListOf<Carrier>() for (i in 1..5) { for(j in 1..20) { if (i % 2 == 0) { list.add(Carrier(CarrierType.TYPE_TANK)) } else { list.add(Carrier(CarrierType.TYPE_ARMORED_CAR) ) } } } val rv = bd2.rv rv.layoutManager = LinearLayoutManager(this) rv.addItemDecoration(ThisItemDecoration()) rv.adapter = ThisRecyclerViewAdapter(list) } }
以上,暂时这样子
标签:fontMetrics,canvas,targetRect,parent,val,吸顶,top,效果,RecyclerView From: https://www.cnblogs.com/swalka/p/16830713.html