背景

这天下班前,老板找到小庄:有个页面要优化,小需求,你跟进一下。
小庄:好的老板! 他看了看时间,忐忑地翻出原型,看到了这样一个页面:

思索片刻后,小庄熟练地打开了某搜索引擎,没有找到合适的轮子,小庄知道软件开发的第一步必须是先进行需求分析和设计,而不是撸起袖子一把梭。于是他决定先分析下功能并整理思路。

预警:本文非常啰嗦,而且没有干货(害怕.jpg)

分析

功能分析

页面的大致功能:

  • 该页面是个展示了某种流程的列表,每个列表项有不同的状态(已完成、进行中、未开始)
  • 在列表的一侧有个类似时间线的view,根据每个项的状态不同,展示不同颜色的圆点和竖线

细节分析

某一个项的时间线view,其中有哪些细节呢?

  • 首先发现,这个时间线view是由两个大部分组成的,分别是:圆、线
  • 然后我们自然可以注意到,在一个项的时间线中,又出现了两种颜色:圆上面的线(以下简称为上线)是绿色,圆本身圆下面的线(以下简称为下线)又是红色
    • 也就是说,这个view不仅要知道自身的颜色,还得知道上一个item是什么颜色的
    • 也就是说,这个view的绘制应该分成三个部分,分别是:上线、圆、下线
  • 这是一个普通的中间的item。然而对于第一个item和最后一个item来说,它们是分别没有上线和下线的

开始编码

小庄现在已经有了基本的思路和知识储备,他打开IDE准备动手编码了。不过软件开发是迭代的过程,即使是这样的一个小需求,他也打算先从实现一个简单的版本开始。

第一版

第一个版本,小庄打算只实现画出圆和线的形状,没有状态也没有颜色,主要为了验证自己的想法是否可行,具体的实现需要做以下几个内容:

  • 准备定义两个重要属性,它们将会参与计算位置和绘制内容

    • radius:用于确定圆的半径
    • offset:用于表示圆点到item顶部的距离
  • 并且在getItemOffsets中留出绘制整个时间线的空间,即item的左边距
  • 最重要的工作内容是我们计算并绘制了圆和线(具体的计算可以看代码)
class FirstVerTimeline : RecyclerView.ItemDecoration() {private val paint = Paint(Paint.ANTI_ALIAS_FLAG)var radius = 8fvar offset = 15override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {super.onDraw(c, parent, state)val count = parent.childCountfor (i in 0 until count) {// 获取当前的itemViewval itemView = parent.getChildAt(i)// 整个轴线的x坐标都是相同的val xPosition = radius// 画上线。第一个item不画if (i != 0) {c.drawLine(xPosition, itemView.top.toFloat(),xPosition, itemView.top.toFloat() + offset, paint)}// 画下线。最后一个item不画if (i != count - 1) {c.drawLine(xPosition, itemView.top  + radius * 2 + offset, xPosition, itemView.bottom.toFloat(),paint)}// 画圆c.drawCircle(xPosition, itemView.top + offset + radius, radius, paint)}}override fun getItemOffsets(outRect: Rect, view: View, : RecyclerView, state: RecyclerView.State) {super.getItemOffsets(outRect, view, parent, state)// 设置item在左边的偏移量outRect.left = radius.toInt() * 2}
}

现在我们可以来定义一个虚拟的数据源Record,把这个ItemDecoration应用到一个RecyclerView上康康效果:

rv_timeline1.adapter = RecordAdapter(ArrayList<Record>())// 省略构造假数据
rv_timeline1.addItemDecoration(FirstVerTimeline())
复制代码

已经初具规模了!只是时间线和文字之间挤了一点,我们只需要加上一些合适的padding,换一下测试数据,看起来就会像真的一样了!

为了从图1到达图2,我们需要做:

  • 定义paddingLeftpaddingRight属性,用来表示轴线的左右padding

    • 修改getItemOffsetsoutRect.left = paddingLeft + paddingRight + radius.toInt() * 2,留出偏移量的位置
    • 修改xPosition的初始值为radius + paddingLeft,改变轴线的x坐标

到这里第一个版本就算完成啦,第二个版本会有什么新功能呢

第二版

小庄打算在第二版里实现状态的不同颜色。为了实现这个需求,他陷入了深深的沉思:

  • 数据类中肯定不可能耦合颜色这种UI实现,所以需要一个由状态获取颜色的办法
  • 由于画一个item还需要知道上一个item的颜色,干脆直接把整个数据源列表data传入ItemDecoration好了
  • 结合以上两点,我们可以定义一个函数类型的属性var color: (item: T) -> Int,实现这个属性就可以让使用者通过数据状态设置想要的颜色了

函数类型是kotlin(或者说函数式编程)的特性之一。如果是Java的话可以考虑用模板模式实现,即定义一个抽象方法让子类去重写

class SecondVerTimeline<T> : RecyclerView.ItemDecoration() {// 其他属性...var data: List<T> = ArrayList()  //-->这里有更新,定义了数据源var color: (item: T) -> Int = { _ -> Color.GRAY }  //-->这里有更新,通过这个属性设置颜色选择策略override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {super.onDraw(c, parent, state)val count = parent.childCountfor (i in 0 until count) {// ...val adapterPosition = parent.getChildAdapterPosition(itemView)  //-->这里有更新,获取当前项的真正位置val item = data[adapterPosition]  //-->这里有更新,获取当前项的数据源// 画上线。第一个item不画if (adapterPosition != 0) {paint.color = color(data[adapterPosition - 1])  //-->这里有更新,设置上线的颜色c.drawLine(...)}paint.color = color(item)  //-->这里有更新,设置圆和下线的颜色// 画下线。最后一个item不画if (adapterPosition != data.size - 1) {//-->这里有更新,改用数据源的大小判断是否为最后一个itemc.drawLine(...)}// 画圆...}}// getItemOffsets...
}

代码中可能需要注意的点:

  • 绘制上线前,需要通过data数据源获取到上一个item,并用color属性获得其状态对应的颜色
  • 绘制圆和下线前,同样需要改变到这一个item的颜色
  • parent.childCount获取到的子项数量指的是屏幕中可见的部分,必须要用parent.getChildAdapterPosition获取到该项在列表中的真正位置,才能确定下线要不要画。否则会出现【当前屏幕上可见的最后一项不是真正的最后一项,但它却没有下线,但向下滑动后它又有下线了】的尴尬场景
  • 注意到此时用于判断是否为最后一个item的方法,从count - 1变为了data.size - 1,用数据源的大小判断,比count更加准确(原因同上一条)

使用时也需要有一些变化:

  • data设置给ItemDecoration
  • 通过color属性设置颜色策略
val secondVerTimeline = SecondVerTimeline<Record>()
secondVerTimeline.data = records
secondVerTimeline.color = { item ->when (item.status) {1 -> color12 -> color2...}
}
rv_timeline2.addItemDecoration(secondVerTimeline)

然后就可以运行看一下效果了:

哇哦,鹅妹子嘤,这样就已经实现根据状态转变颜色的功能了!第二版的功能也圆满实现!

后话

后来小庄又根据UI一顿修修改改,很快就完成了这个需求~但是小庄是一个有追求的程序员,他开始思考起了这个代码的扩展性和通用性如何。不想不要紧,一想发现根本没有鸭!如果产品想要把圆点变成图片怎么办?或者产品想要更随风飞翔自由是方向呢?

于是他想找个时间完善改进一下这个ItemDecoration,最好能应对产品的所有需求!具体升级内容请看下集~

然而没有什么设计能做到一劳永逸,软件工作中唯一不变的就是变化,同时我们也不应该为了应对所谓的“未来可能发生的改动”而过度设计

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

文末

欢迎关注我的CSDN,分享Android干货,交流Android技术。
对文章有何见解,或者有何技术问题,都可以在评论区一起留言讨论,我会虔诚为你解答。
最后,如果你想知道更多Android的知识或需要其他资料我这里均免费分享,只需你多多支持我即可哦!

——可以直接点这里可以看到全部资料内容免费打包领取。

大佬教你Android如何实现时间线效果相关推荐

  1. html怎么实现蓝色垂直的直线,css实例教程 一款纯css实现的垂直时间线效果

    今天给大家分享一款纯css实现的垂直时间线效果.垂直时间线适合放在类似任务时间安排的网页上.该实现采用了蓝色作为主题色,界面效果还不错.一起看下效果图: 实现的代码. html代码: 复制代码代码如下 ...

  2. 微信小程序Timeline时间线效果实现

    微信小程序实现类似elementUI的Timeline时间线效果,自适应页面与文本 wxml代码: <view class="box"><view wx:for= ...

  3. 腾讯微博android sdk,腾讯微博java(android)sdk时间线api详细介绍

    本文主要介绍腾讯微博android sdk中时间线有关的20个接口,以及正常获取及翻页的使用的示例代码及部分特例 注意:以下所有的api示例代码都需要先新建QqTSdkService类对象qqTSdk ...

  4. RecyclerView列表控件漂亮时间线实现

    时间都去哪了:时间你走慢一点吧 很多软件中都有时间线的东西,比如天气,计划,旅游等时间线最多了:具体实现方式很多,在本篇文章中讲解一种自定义View封装的方式实现时间线效果,PS:这也是面试中也时常会 ...

  5. 介绍三种绘制时间线图的方法

    作者 |周萝卜 来源 |萝卜大杂烩 今天我们再来分享几种不同的制作方法,大家可以自行比较下各种方法的优劣. Matplotlib 制作 Matplotlib 作为 Python 家族最为重要的可视化工 ...

  6. 几种绘制时间线图的方法

    前面分享过一篇自动化制作<历史上的今天>时间线图片的文章,小伙伴们普遍反映还不错,尤其是制作时间线的方法,还是非常巧妙的.今天我们再来分享几种不同的制作方法,大家可以自行比较下各种方法的优 ...

  7. android 日程安排view,RecyclerView 列表控件中简单实现时间线

    时间 时间,时间,时间啊:走慢一点吧- 看见很多软件中都有时间线的东西,貌似天气啊,旅游啊什么的最多了:具体实现方式很多,在本篇文章中讲解一种自定义View封装的方式. 效果 先来看看效果. 分析 软 ...

  8. 微软桌面5.0新增时间线功能

    2019独角兽企业重金招聘Python工程师标准>>> 晨,微软召开发布会,正式更新Surface Pro 6 .Surface Laptop 2 以及 Surface Studio ...

  9. 手把手教你Android手机与BLE终端通信--连接,发送和接收数据

    假设你还没有看上一篇 手把手教你Android手机与BLE终端通信--搜索,你就先看看吧,由于这一篇要接着讲搜索到蓝牙后的连接.和连接后的发送和接收数据. 评论里有非常多人问假设一条信息特别长,怎么不 ...

最新文章

  1. php7 ext skel_基于PHP7的PHP扩展开发之一(hello word)
  2. string中的Copy-on-Write技术
  3. 手机数据抓包入门教程
  4. python框架django的数据库的正向生成和反向生成
  5. JavaScript中的面向对象(1):对象创建模式
  6. TQ210——交叉编译器的安装
  7. 201521123035《Java程序设计》第八周学习总结
  8. 全面取消校园全封闭管理!这个省发通知了!那北京呢?
  9. unity 2020 怎么写shader使其接受光照?_如何在Unity中造一个PBR Shader轮子
  10. RabbitMq的基本认识和配置(一)
  11. 《从零开始学Swift》学习笔记(Day 62)——Core Foundation框架之内存托管对象与非托管对象...
  12. python的常量_python常量 (最全常量解析)
  13. ajax传参中文乱码问题解决
  14. uboot引导kernel - 4 -gd bd详解
  15. STM32 之十 供电系统及内部参照电压(VREFINT)使用及改善ADC参考电压,内部参照电压的具体方法,只有在STM32F0x芯片的参考手册中才能找到,其他MCU的参考手册都是很简单的说明
  16. 巴西龟饲养日志----6月份记录
  17. canvas如何绘制虚线
  18. 【MFC进阶】05文件处理CFile
  19. FIBOS入坑指南——本地开发环境搭建
  20. c语言课程设计研究生初试录取报告,C语言程序设计课程设计 研究生初试录取

热门文章

  1. 怎么查硬盘序列号_担心硬盘体质?不妨先给硬盘做一次体检
  2. oracle impdp无法打开日志文件,Solaris 下 Oracle impdp 过程中出现的问题
  3. CUDA学习-函数前缀__global__
  4. 阿里云平台注册与使用Linux
  5. 对象必须实现 iconvertible_Java I/O 流之数据流_对象流
  6. 程序员学习视频教程汇总——(转载)
  7. (转)C#开发微信门户及应用(4)--关注用户列表及详细信息管理
  8. JAVA_OPTS 参数
  9. 复杂的结构化存取(一)
  10. 一步步部署SharePoint Workflow 2013