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

· 浏览次数 : 0

小编点评

效果图可控制是否绘制在中间 控制第一条数据圆顶部线条和最后一条数据圆底部线条是否绘制 除了gif图片展示的属性,还可以控制圆的大小颜色、圆是否有上和左偏移、线条颜色等属性 除了通用的时间轴绘制,我们还可以通过改变绘制圆的样式,改为绘制相应的bitmap图像,来实现展示相关的流程思路 1. 获取当前屏幕可见的个数 2. 循环遍历每个ItemView,根据真实数据中的位置获取其下标 3. 在onDraw()方法内绘制圆、圆顶部线条、圆底部线条 和线条 4. 利用Canvas.drawCircle()或Canvas drawBitmap()绘制对应的item和进度条 ItemDecoration 相关代码示例: ``` class MyItemDecoration : RecyclerView.Item Decoration() { private val itemPaint = Paint() private val linePaint = Paint() private var circlePaint: Paint? = null private var errorBitmap: Bitmap? = null private var circleSize : Int = 0 private var circleRadius: Int = 0 private var circleLeftPadding: Int = 0 private var circleTopPadding: Int = 0 private var isDrawAtMiddle = false private var isDrawFirstItemTopLine = false private var isDrawLastItemBottomLine = false init { itemPaint.color = Color.BLACK itemPaint.style = Paint.Style.FILL linePaint.color = Color.BLUE linePaint.style = Paint.Style.FILL errorBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.error) circleSize = 50 circleRadius = 20 circleLeftPadding = 10 circleTopPadding = 10 } override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { if(parent.getChildAdapterPosition(view)!=0){ //第一个不做顶部偏移 outRect.top = topItemSpace } outRect.left = leftItemSpace } override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { super.onDraw(c, parent, state) val childCount = parent.childCount for(i in 0 until childCount){ val view = parent.getChildAt(i) val index = parent.getChildAdapterPosition(view) val spaceRectTop = if(index == 0) view.top else view.top - topItemSpace val spaceRectBottom = view.bottom val spaceRectLeft = view.left - leftItemSpace val spaceRectRight = view.left val dataRectLeft = view.left - leftItemSpace val dataRectTop = view.top val dataRectRight = view.left val dataRectBottom = view.bottom val centerX: Float val centerY: Float if(isDrawAtMiddle){ centerX = (dataRectLeft + dataRectRight) / 2 centerY =(dataRectTop + dataRectBottom) / 2 }else{ centerX = (dataRectLeft + dataRectRight) / 2 + circleLeftPadding centerY = dataRectTop + circleRadius + circleTopPadding isDrawAtMiddle = true } // 绘制线条 c.drawLine(dataRectLeft, spaceRectTop, dataRectLeft, (centerY - circleRadius).toFloat(), linePaint) c.drawLine(dataRectLeft, spaceRectTop, dataRectLeft, (centerY + circleRadius).toFloat(), linePaint) // 绘制圆 c.drawCircle(centerX, centerY, circleRadius, circlePaint) // 绘制第二条线 c.drawLine(centerX, (centerY + circleRadius).toFloat(), centerX, spaceRectBottom.toFloat(), linePaint) // 判断是否是最后一个Item if(index == parent.itemCount - 1){ if(isDrawLastItemBottomLine) { c.drawLine(dataRectLeft, (centerY + circleRadius).toFloat(), dataRectLeft, spaceRectBottom.toFloat(), linePaint) } } else { c.drawLine(dataRectLeft, (centerY + circleRadius).toFloat(), dataRectLeft, spaceRectBottom.toFloat(), linePaint) } } } } ```

正文

效果图

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

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

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

流程类时间轴效果图

思路

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

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

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

image

具体实现

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

步骤一:ItemView顶部偏移

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

    }

步骤二:绘制圆和线条

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的加载,我们可以通过传入集合的数据类型来判断绘制哪种图片即可。

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更加的通用,方便运用到项目中。

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

与时间轴、流程类时间轴绘制相似的内容:

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

效果图 可控制是否绘制在中间 控制绘制的线条是否为虚线 控制第一条数据圆顶部线条和最后一条数据圆底部线条是否绘制 除了gif图片展示的属性,还可以控制圆的大小颜色、圆是否有上和左偏移、线条颜色等属性 除了通用的时间轴绘制,我们还可以通过改变绘制圆的样式,改为绘制相应的bitmap图像,来实现展示相关

基于drawio构建流程图编辑器

# 基于drawio构建流程图编辑器 `drawio`是一款非常强大的开源在线的流程图编辑器,支持绘制各种形式的图表,提供了`Web`端与客户端支持,同时也支持多种资源类型的导出。 ## 描述 在我们平时写论文、文档时,为了更好地阐述具体的步骤和流程,我们经常会有绘制流程图的需求,这时我们可能会想到

如何科学地利用MTTR优化软件交付流程?

谷歌提出的衡量 DevOps 质量的 DORA 指标让 MTTR(平均恢复时间) 名声大振。在本文中,你将了解到 MTTR 的作用、为什么它对行业研究很有用、你可能被它误导的原因以及如何避免 MTTR 产生的弊端。 ## MTTR 究竟是在测量什么? MTTR 指平均恢复时间,既是 Mean Tim

内存优化:Boxing

dotMemory 如今,许多开发人员都熟悉性能分析的工作流程:在分析器下运行应用程序,测量方法的执行时间,识别占用时间较多的方法,并致力于优化它们。然而,这种情况并没有涵盖到一个重要的性能指标:应用程序多次GC所分配的时间。当然,你可以评估GC所需的总时间,但是它从哪里来,如何减少呢? “普通”性

Vue3简单项目流程分享——工作室主页

Vue3简单项目流程分享——工作室主页 零、写在最前 以下是项目相关的一些链接: 源代码GitHub仓库(需要魔法上网):仓库 网页示例(需要魔法上网):网页示例 UI图(来源@设计师杨贺):MasterGo主页 补充:由于时间关系,该网页没有适配手机端,最佳展示效果为网页端1440p宽度。 如果你

APP中RN页面热更新流程-ReactNative源码分析

平时使用WebStorm或VSCode对RN工程中的文件修改后,在键盘上按一下快捷cmd+s进行文件保存,此时当前调试的RN页面就会自动进行刷新,这是RN开发相比于原生开发一个很大的优点:热更新。 那么,从按一下快捷cmd+s到RN页面展示出最新的JS页面,这个过程是怎样发生的呢?下面根据时间顺序来

Python3.10动态修改Windows系统(win10/win11)本地IP地址(静态IP)

一般情况下,局域网里的终端比如本地服务器设置静态IP的好处是可以有效减少网络连接时间,原因是过程中省略了每次联网后从DHCP服务器获取IP地址的流程,缺点是容易引发IP地址的冲突,当然,还有操作层面的繁琐,如果想要切换静态IP地址,就得去网络连接设置中手动操作,本次我们使用Python3.10动态地

应用内支付服务现网、沙盒环境下常见关键事件的对比与总结

在集成和调试订阅型商品时,我们会依赖沙盒环境来进行模拟实际场景。 订阅型商品的购买流程和一次性商品的购买流程类似,但订阅还有其他细节场景,比如续订成功或失败,续订周期时长等。沙盒环境下的订阅续订时间会比正常情况更快,引入“时光机”概念帮助您快速测试您应用的订阅场景。比如订阅周期为1周,商品在3分钟后

记一次由于操作失误致使数据库瘫痪的故障分析与解决方案

在这篇文章中,我将分享一次由于操作不当导致数据库瘫痪的经验。通过回顾故障发生的时间、系统简介、时间线、问题分析和经验总结等方面的内容。讨论操作时间不当、操作流程不当、缺乏执行计划和限流机制等问题,并提出一些建议,如确认数据库更新时间、优化更新操作、使用限流工具、设置超时时间和重试机制、调整数据库参数以及定期维护和优化数据库。通过分享这次经验,我希望能帮助他人避免类似的错误,并提高数据库操作的准确性和稳定性。

一篇带你了解如何使用纯前端类Excel表格构建现金流量表

现金流量表(Cash Flow Statement),是指反映企业在一定会计期间现金和现金等价物流入和流出的报表。现金流量表是企业财务报表的三个基本报告之一(另外两个是资产负债表和损益表)。 为了全面系统地揭示企业一定时期的财务状况、经营成果和现金流量,财务报表需按财政部会计准则的标准格式设计,因此