我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android如何实现时间线效果

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android如何实现时间线效果

1、背景

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

需要优化页面的原型图:

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

2、分析

2.1功能分析

页面的大致功能:

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

2.2细节分析

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

首先发现,这个时间线view是由两个大部分组成的,分别是:圆、线

然后我们自然可以注意到,在一个项的时间线中,又出现了两种颜色:圆上面的线(以下简称为上线)是绿色,圆本身和圆下面的线(以下简称为下线)又是红色

  • 也就是说,这个view不仅要知道自身的颜色,还得知道上一个item是什么颜色的
  • 也就是说,这个view的绘制应该分成三个部分,分别是:上线、圆、下线

这是一个普通的中间的item。然而对于第一个item和最后一个item来说,它们是分别没有上线和下线的

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())

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

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

定义paddingLeft和paddingRight属性,用来表示轴线的左右padding

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

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

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实现时间线效果内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android如何实现时间线效果

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何在Android中利用itemdecoration实现一个时间线效果

今天就跟大家聊聊有关如何在Android中利用itemdecoration实现一个时间线效果,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。代码如下:// 时间线装饰器public c
2023-06-06

Android实现波浪线效果(xml bitmap)

我们要实现的效果如下:在这之前先带大家了解一下xml bitmap,何为XML Bitmap? XML Bitmap 是一个用XML定义的文件放在资源目录,定义的对象是图片,为bitmap定义别名,这个文件可以给bitmap定义一些额外的属
2022-06-06

css如何实现中间文字两边横线效果

小编给大家分享一下css如何实现中间文字两边横线效果 ,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1. vertical-align属性实现效果:vertica
2023-06-08

Android控件之使用ListView实现时间轴效果

实现思路: 该View是通过ListView实现的,通过实体两个字段内容content和时间time来展示每个ListItem 时间轴是使用上面一条线(20dp)和中间一个圆(15dp)和下面一条线(40dp)组装成的 在ListView
2022-06-06

Android如何实现View滑动效果

这篇文章给大家分享的是有关Android如何实现View滑动效果的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、View的滑动简介View的滑动是Android实现自定义控件的基础,同时在开发中我们也难免会遇到
2023-06-14

Android如何实现滤镜效果ColorMatrix

这篇文章将为大家详细讲解有关Android如何实现滤镜效果ColorMatrix,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Android是什么Android是一种基于Linux内核的自由及开放源代码的
2023-06-14

Android如何实现水波纹效果

这篇文章主要为大家展示了“Android如何实现水波纹效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Android如何实现水波纹效果”这篇文章吧。效果图attrs.xml自定义属性
2023-06-29

Android如何实现梯形TextView效果

小编给大家分享一下Android如何实现梯形TextView效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果图:自定义代码实现逻辑:public class
2023-06-15

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录