这篇文章主要介绍了 Android如何实现时间线效果,下面文章围绕 Android如何实现时间线效果的相关资料展开详细内容,具有一定的参考价值 ,需要的朋友可以参考一下
1、背景
这天下班前,老板找到小庄:“有个页面要优化,小需求,你跟进一下。”
小庄:“好的老板!”他看了看时间,忐忑地翻出原型,看到了这样一个页面:
需要优化页面的原型图:
2、分析
2.1功能分析
页面的大致功能:
- 该页面是个展示了某种流程的列表,每个列表项有不同的状态(已完成、进行中、未开始)
- 在列表的一侧有个类似时间线的
view,根据每个项的状态不同,展示不同颜色的圆点和竖线
2.2细节分析
对于其中一个项的时间线view,有哪些细节呢?
2.3方案设想
小庄的脑海里迅速地闪过了几个想法:
第一个想法是根据数据的状态,在adapter中设置颜色和是否线显示。
- 但是这么简单的圆和线还要找设计师要图么?这样岂不是显得他很菜。那要用Drawable?然而将来要改颜色什么的,也是麻烦,而且要写好几个文件。所以这个想法很快就被pass了
第二个想法就是使用自定义view,在每一个item中画出圆和线,然后用自定义属性设置颜色。
- 他马上写了个
demo尝试了一下,结果是他自定义view学艺不精,遇到了难以解决的问题[注],所以只能哭着放弃了
也许是命中注定他将推开一扇大门:旁友,也许你听说过RecyclerView.ItemDecoration吗?
注:2000 years later,我发现我根本复现不出来那问题,也许这就是缘分吧
RecyclerView.ItemDecoration简介
这是一款功能强大的神器,用来给列表添加分隔线只是它最常见又最普通的能力。这里简单介绍一下,不是本文的主要内容。因为它能实现的效果太多太厉害了,我学不过来(ಥ_ಥ)
实现自定义的一个ItemDecoration,需要继承它并按需重写以下两个方法:
onDraw:用于具体的绘制内容
- 方法有个参数是
parent:RecyclerView,即列表本身,所以我们可以从这里获取每个子项的内容 - 要注意的是这个方法里的绘制维度是整个列表,所以我们需要遍历列表,为每一个子项进行计算位置和绘制
getItemOffsets:用于控制item的四周的偏移量,onDraw中绘制的内容会在这些留白上画出来
- 然而这个方法的绘制维度又是针对每一个itemView,所以设置的是每个item的上下左右边距
3、编码
小庄现在已经有了基本的思路和知识储备,他打开IDE准备动手编码了。不过软件开发是迭代的过程,即使是这样的一个小需求,他也打算先从实现一个简单的版本开始。
3.1第一版
第一个版本,小庄打算只实现画出圆和线的形状,没有状态也没有颜色,主要为了验证自己的想法是否可行,
具体的实现需要做以下几个内容:
准备定义两个重要属性,它们将会参与计算位置和绘制内容
radius:用于确定圆的半径offset:用于表示圆点到item顶部的距离
并且在getItemOffsets中留出绘制整个时间线的空间,即item的左边距
最重要的工作内容是我们计算并绘制了圆和线(具体的计算可以看代码)
class FirstVerTimeline : RecyclerView.ItemDecoration() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
var radius = 8f
var offset = 15
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
val count = parent.childCount
for (i in 0 until count) {
// 获取当前的itemView
val 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())
3.2第二版
小庄打算在第二版里实现状态的不同颜色。为了实现这个需求,他陷入了深深的沉思:
- 数据类中肯定不可能耦合颜色这种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.childCount
for (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) {//-->这里有更新,改用数据源的大小判断是否为最后一个item
c.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 -> color1
2 -> color2
...
}
}
rv_timeline2.addItemDecoration(secondVerTimeline)
然后就可以运行看一下效果了:
4、结语
后来小庄又根据UI一顿修修改改,很快就完成了这个需求~但是小庄是一个有追求的程序员,他开始思考起了这个代码的扩展性和通用性如何。不想不要紧,一想发现根本没有鸭!如果产品想要把圆点变成图片怎么办?或者产品想要更随风飞翔自由是方向呢?
于是他想找个时间完善改进一下这个ItemDecoration,最好能应对产品的所有需求!具体升级内容请看下集~
到此这篇关于 Android如何实现时间线效果的文章就介绍到这了,更多相关 Android实现时间线效果内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
本文标题为:Android如何实现时间线效果
- iOS 对当前webView进行截屏的方法 2023-03-01
- Android MaterialButton使用实例详解(告别shape、selector) 2023-06-16
- 详解flutter engine 那些没被释放的东西 2022-12-04
- Android实现监听音量的变化 2023-03-30
- 作为iOS开发,这道面试题你能答出来,说明你基础很OK! 2023-09-14
- Flutter实现底部和顶部导航栏 2022-08-31
- 最好用的ios数据恢复软件:PhoneRescue for Mac 2023-09-14
- SurfaceView播放视频发送弹幕并实现滚动歌词 2023-01-02
- Android studio实现动态背景页面 2023-05-23
- Android实现轮询的三种方式 2023-02-17
