首页 > 其他分享 >RecyclerView实现吸顶效果

RecyclerView实现吸顶效果

时间:2022-10-27 02:44:19浏览次数:65  
标签:fontMetrics canvas targetRect parent val 吸顶 top 效果 RecyclerView

本文主要讲述得方式是通过自定义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

相关文章

  • Handsontable合并表头,实现rowspan效果
    背景使用handsontable只能进行跨列的合并,如果跨行的话就有api提供,我们需要对表头进行跨行合并实现这里学习这边博文提供的一个思路,就是假设需要对表格第一行的跨行合并......
  • 呼吸效果
    点击查看代码<!DOCTYPEhtml><htmllang="en"><head> <metacharset="UTF-8"> <metahttp-equiv="X-UA-Compatible"content="IE=edge"> <metaname="viewport"con......
  • Gitea 1.18 功能前瞻(其三):增强文本预览效果、继续扩展软件包注册中心、增强工单实用功
    今天是10月26日星期三,Gitea周期性地发布了1.18的第一个RC0版本,在此阶段会收集一些功能和使用上的问题,随后还会发布RC1,新功能的完整性和健壮性会逐步趋近正式版......
  • Android实现天气下雨效果
    效果图   代码是这个人写的,致敬大佬BaseType类importandroid.content.Context;importandroid.graphics.Canvas;importandroid.graphics.Color;importand......
  • vue+elementUI-Table,实现自定义表格列效果
    都是日常的工作小结,编写不易,转载请注明出处~考虑到会看我博客的都是和我一样的小白,尽量写细一些,有问题可在评论区留言一实现原理1.1一般我们在写elementUI表格代码时,都......
  • 好看的CSS效果
    好看的效果1.复选框:思路是使用复选框并进行隐藏,然后自己写一个div,并且用:before来放进div复选框这,不少地方采用绝对定位,在:checked时更改对应颜色。<divclass=......
  • APP自动化效果测试工具
    1.背景项目测试过程中经常需要在手机端体验语音产品的识别效果和稳定性,识别效果与手机硬件强相关无法抛开硬件影响。因此开发了一套基于uiautomator2+pythonUI自动化工具,......
  • 美颜滤镜SDK有什么效果?美颜SDK可以自主开发吗?
    当下,短视频和直播平台在互联网娱乐APP类中保持着极高的人气,特别是在年轻群体中,甚至已经成了人手必备。如今的年轻人喜欢追求新鲜事物,审美能力也有所提高,对自己外表比较严格,......
  • Solidworks 2020总是显示3D效果,不能消失平面效果
    打开SolidWorks2020装配体后,通过快捷方式Space正视于前视基准面或者其他基准面后,总是显示3D效果,不能显示2D效果,就像轴测图一样,如下:解决办法:在设计树上的“前视基准面”......
  • DiffUtil工具类使用-让recyclerview使用更高效
    DiffUtil工具类使用-让recyclerview使用更高效问题背景安卓开发过程中,recyclerview是很常见的滑动列表视图组件,数据刷新的时候,我们经常就是直接调用了mAdapter.notifyData......