时间轴、流程类时间轴绘制

效果图

  • 可控制是否绘制在中间
  • 控制绘制的线条是否为虚线
  • 控制第一条数据圆顶部线条和最后一条数据圆底部线条是否绘制

除了gif图片展示的属性,还可以控制圆的大小颜色、圆是否有上和左偏移、线条颜色等属性

除了通用的时间轴绘制,我们还可以通过改变绘制圆的样式,改为绘制相应的bitmap图像,来实现展示相关的流程

思路

关于ItemDecoration相关的内容已经写了不少,这个其实就是小菜一碟。我们需要做的工作有两点

  • ItemDecoration在getItemOffsets()方法内做相应的偏移
  • onDraw()方法内分别绘制圆、圆顶部线条、圆底部线条
    • 绘制线条,我们需要知道start和end的点坐标;
    • 绘制圆,我们需要知道圆心和半径;

通过下图,你将能清楚地获取到这些绘制需要的一些信息

具体实现

有了以上内容,我们开始绘制

步骤一:ItemView顶部偏移

kotlin 复制代码
 override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State,
    ) {
        if (parent.getChildAdapterPosition(view) != 0) {
            //第一个不做顶部偏移
            outRect.top = topItemSpace
        }
        outRect.left = leftItemSpace

    }

步骤二:绘制圆和线条

kotlin 复制代码
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        //获取到的是当前屏幕可见的个数
        val childCount = parent.childCount

        for (i in 0 until childCount) {
            val view = parent.getChildAt(i)
            //获取真实的在整体数据中的位置
            val index=parent.getChildAdapterPosition(view)

            //ItemView左侧+偏移的矩形框(绿色框部分)
            val spaceRectTop = if (index == 0) view.top else view.top - topItemSpace
            val spaceRectBottom = view.bottom
            val spaceRectLeft = view.left - leftItemSpace
            val spaceRectRight = view.left

            //ItemView左侧,不包含偏移的矩形框(红色框部分)
            val dataRectLeft = view.left - leftItemSpace
            val dataRectTop = view.top
            val dataRectRight = view.left
            val dataRectBottom = view.bottom

            //圆心坐标
            var centerX = if(isDrawAtMiddle) (dataRectLeft + dataRectRight)/ 2  else (dataRectLeft + dataRectRight)/ 2 + circleLeftPadding
            val centerY =
                if (isDrawAtMiddle) (dataRectTop + dataRectBottom) / 2 else dataRectTop + circleRadius + circleTopPadding

            //绘制第一条线
            if (index==0){
                if (isDrawFirstItemTopLine){
                    c.drawLine(
                        centerX.toFloat(),
                        spaceRectTop.toFloat(),
                        centerX.toFloat(),
                        (centerY - circleRadius).toFloat(),
                        mLinePaint
                    )
                }
            }else{
                c.drawLine(
                    centerX.toFloat(),
                    spaceRectTop.toFloat(),
                    centerX.toFloat(),
                    (centerY - circleRadius).toFloat(),
                    mLinePaint
                )
            }
            //绘制圆(居中显示)
            c.drawCircle(centerX.toFloat(), centerY.toFloat(), circleRadius.toFloat(), mCirclePaint)


            //绘制第二条线,注意这里要用itemCount,因为上面的childCount是当前页面可见的个数
            parent.adapter?.let {
                if (index!=it.itemCount-1){
                    c.drawLine(
                        centerX.toFloat(),
                        (centerY + circleRadius).toFloat(),
                        centerX.toFloat(),
                        spaceRectBottom.toFloat(),
                        mLinePaint
                    )
                }else{
                    if (isDrawLastItemBottomLine){
                        c.drawLine(
                            centerX.toFloat(),
                            (centerY + circleRadius).toFloat(),
                            centerX.toFloat(),
                            spaceRectBottom.toFloat(),
                            mLinePaint
                        )
                    }
                }
            }
        }
    }

注意:下标的获取

因为我们需要每个ItemView都绘制,所以需要使用循环。但因为val childCount = parent.childCount获取到的是当前页面可见的个数,并不是实际的个数,所以我们在判断是否是首条或者最后一条数据时,那个index要通过val index=parent.getChildAdapterPosition(view)的方式来获取到真实的下标位置。

流程类的绘制

和绘制通用的圆类似,不过是将Canvas.drawCircle()改为Canvas.drawBitmap()。至于不同的bitmap的加载,我们可以通过传入集合的数据类型来判断绘制哪种图片即可。

kotlin 复制代码
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
	...
	val srcRect = Rect(0, 0, progressBitmap.width, progressBitmap.height)
	val dstRect = Rect(
        centerX - circleRadius,
        centerY - circleRadius,
        centerX + circleRadius,
        centerY + circleRadius
    )
    c.drawBitmap(errorBitmap, srcRect, dstRect, mCirclePaint)
    ...
}

总结

其实主要还是ItemDecoration相关的内容,在onDraw()方法内绘制圆、绘制bitmap和绘制线条,根据上面的图,知道具体的坐标位置,绘制就很轻松了,也可以在此基础上继续扩展,使得我们的时间轴ItemDecoration更加的通用,方便运用到项目中。

如果本文对你有帮助,请别忘记三连,如果有不恰当的地方也请提出来,下篇文章见。

相关推荐
程序员陆业聪5 小时前
从 OpenClaw 到 Android:Harness Engineering 是怎么让 Agent 变得可用的
android
hnlgzb7 小时前
常见的Android Jetpack库会有哪些?这些库中又有哪些常用类的?
android·android jetpack
钛态10 小时前
Flutter 三方库 http_mock_adapter — 赋能鸿蒙应用开发的高效率网络接口 Mock 与自动化测试注入引擎(适配鸿蒙 HarmonyOS Next ohos)
android·网络协议·flutter·http·华为·中间件·harmonyos
王码码203510 小时前
Flutter for OpenHarmony:Flutter 三方库 algoliasearch 毫秒级云端搜索体验(云原生搜索引擎)
android·前端·git·flutter·搜索引擎·云原生·harmonyos
左手厨刀右手茼蒿10 小时前
Flutter for OpenHarmony: Flutter 三方库 shamsi_date 助力鸿蒙应用精准适配波斯历法(中东出海必备)
android·flutter·ui·华为·自动化·harmonyos
代码飞天10 小时前
wireshark的高级使用
android·java·wireshark
2501_9159184111 小时前
苹果App Store上架审核卡住原因分析与解决方案指南
android·ios·小程序·https·uni-app·iphone·webview
skiy11 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
小小小点12 小时前
Android四大常用布局详解与实战
android
MinQ13 小时前
binder和socket区别及原理
android