快速简单的定制一个时间轴布局(UnderLineLinearLayout)

2024-06-03 12:48

本文主要是介绍快速简单的定制一个时间轴布局(UnderLineLinearLayout),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

github:

https://github.com/razerdp/UnderLineLinearLayout

先上图:


 

很多情况下,我们都会遇到需要类似于时间轴的一个布局,网上也有很多,但是很多情况下我们其实并不需要那么多库,毕竟64k限制就在那,不管我们用还是不用,它依然在那。。。

而且更多时候,我们的时间轴也许并不需要那么多数据(比如转账流程?)

事实上这次的教程也是因为我司需要弄一个转账流程,这个转账流程包含了基本的三个状态:

1 - 转账开始

2 - 转账中

3 - 转账成功

而失败后重新转账可以看到前面的状态,也就是前面的三个状态的重复

一般而言,转账3~4次失败我们也就算了,同时因为布局比较简单,所以我这次就没打算用listview,而是简单的继承LinearLayout

 

当然,如果数据比较多,为了内存建议还是用listview

 

那么下面正文开始:

 首先规划一下我们需要的元素,从图中我们可以看到的元素有:

      线(颜色,宽),点(大小,颜色),图标

 为了方便调整,我们还需要引入两个值

      点距离父控件左边的偏移值(调整左右间距),每个节点距离childView的偏移值(调整上下间距)

 

于是我们的attrs.xml就出来了

[java] viewplain copy

print?

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <declare-styleable name="UnderLineLinearLayout">  
  4.         <!--时间轴偏移值-->  
  5.         <attr name="line_margin_side" format="dimension"/>  
  6.         <!--时间轴动态调整值-->  
  7.         <attr name="line_dynamic_dimen" format="dimension"/>  
  8.         <!--线宽-->  
  9.         <attr name="line_stroke_width" format="dimension"/>  
  10.         <!--线的颜色-->  
  11.         <attr name="line_color" format="color"/>  
  12.         <!--点的大小-->  
  13.         <attr name="point_size" format="dimension"/>  
  14.         <!--点的颜色-->  
  15.         <attr name="point_color" format="color"/>  
  16.         <!--图标-->  
  17.         <attr name="icon_src" format="reference"/>  
  18.     </declare-styleable>  
  19.   
  20. </resources>  

 

然后继承LinearLayout,开始我们的简易时间轴

[java] viewplain copy

print?

  1. public UnderLineLinearLayout(Context context) {  
  2.        this(context, null);  
  3.    }  
  4.   
  5.    public UnderLineLinearLayout(Context context, AttributeSet attrs) {  
  6.        this(context, attrs, 0);  
  7.    }  
  8.   
  9.    public UnderLineLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  10.        super(context, attrs, defStyleAttr);  
  11.        TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.UnderLineLinearLayout);  
  12.        lineMarginSide = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_line_margin_side, 10);  
  13.        lineDynamicDimen = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_line_dynamic_dimen, 0);  
  14.        lineStrokeWidth = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_line_stroke_width, 2);  
  15.        lineColor = attr.getColor(R.styleable.UnderLineLinearLayout_line_color, 0xff3dd1a5);  
  16.        pointSize = attr.getDimensionPixelSize(R.styleable.UnderLineLinearLayout_point_size, 8);  
  17.        pointColor = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_point_color, 0xff3dd1a5);  
  18.   
  19.        int iconRes = attr.getResourceId(R.styleable.UnderLineLinearLayout_icon_src, R.drawable.ic_ok);  
  20.        BitmapDrawable temp = (BitmapDrawable) context.getResources().getDrawable(iconRes);  
  21.        if (temp != null) mIcon = temp.getBitmap();  
  22.   
  23.        curOrientation = getOrientation();  
  24.        attr.recycle();  
  25.        initView(context);  
  26.    }  

 

构造器我们获取各种参数,至于变量就不解释了

我们的主要方法都在onDraw里面,onDraw我们执行一个方法

[java] viewplain copy

print?

  1. private void initView(Context context) {  
  2.         this.mContext = context;  
  3.   
  4.         linePaint = new Paint();  
  5.         linePaint.setAntiAlias(true);  
  6.         linePaint.setDither(true);  
  7.         linePaint.setColor(lineColor);  
  8.         linePaint.setStrokeWidth(lineStrokeWidth);  
  9.         linePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  10.   
  11.         pointPaint = new Paint();  
  12.         pointPaint.setAntiAlias(true);  
  13.         pointPaint.setDither(true);  
  14.         pointPaint.setColor(pointColor);  
  15.         pointPaint.setStyle(Paint.Style.FILL);  
  16.     }  
  17.   
  18.     @Override  
  19.     protected void onDraw(Canvas canvas) {  
  20.         super.onDraw(canvas);  
  21.         if (drawLine) {  
  22.             drawTimeLine(canvas);  
  23.         }  
  24.     }  


而我们的drawTimeLine的方法如下:

[java] viewplain copy

print?

  1. private void drawTimeLine(Canvas canvas) {  
  2.         int childCount = getChildCount();  
  3.   
  4.         if (childCount > 0) {  
  5.             //大于1,证明至少有2个,也就是第一个和第二个之间连成线,第一个和最后一个分别有点/icon  
  6.             if (childCount > 1) {  
  7.                 switch (curOrientation) {  
  8.                     case VERTICAL:  
  9.                         drawFirstChildViewVertical(canvas);  
  10.                         drawLastChildViewVertical(canvas);  
  11.                         drawBetweenLineVertical(canvas);  
  12.                         break;  
  13.                     case HORIZONTAL:  
  14.                         break;  
  15.                     default:  
  16.                         break;  
  17.                 }  
  18.             }  
  19.             else if (childCount == 1) {  
  20.                 switch (curOrientation) {  
  21.                     case VERTICAL:  
  22.                         drawFirstChildViewVertical(canvas);  
  23.                         break;  
  24.                     case HORIZONTAL:  
  25.                         break;  
  26.                     default:  
  27.                         break;  
  28.                 }  
  29.             }  
  30.         }  
  31.     }  

按照我的设想,是想着横着也做一个的,但由于时间原因,就暂时未能实现,但其实实现原理都是一样的。

接下来就是最主要的几个方法了:

[java] viewplain copy

print?

  1. private void drawFirstChildViewVertical(Canvas canvas) {  
  2.        if (getChildAt(0) != null) {  
  3.            int top = getChildAt(0).getTop();  
  4.            //记录值  
  5.            firstX = lineMarginSide;  
  6.            firstY = top + getChildAt(0).getPaddingTop() + lineDynamicDimen;  
  7.            //画一个圆  
  8.            canvas.drawCircle(firstX, firstY, pointSize, pointPaint);  
  9.        }  
  10.    }  
  11.   
  12.    private void drawLastChildViewVertical(Canvas canvas) {  
  13.        if (getChildAt(getChildCount() - 1) != null) {  
  14.            int top = getChildAt(getChildCount() - 1).getTop();  
  15.            //记录值  
  16.            lastX = lineMarginSide - (mIcon.getWidth() >> 1);  
  17.            lastY = top + getChildAt(getChildCount() - 1).getPaddingTop() + lineDynamicDimen;  
  18.            //画一个图  
  19.            canvas.drawBitmap(mIcon, lastX, lastY, null);  
  20.        }  
  21.    }  
  22.   
  23.    private void drawBetweenLineVertical(Canvas canvas) {  
  24.        for (int i = 0; i < getChildCount() - 1; i++) {  
  25.            //画剩下的  
  26.            canvas.drawLine(lineMarginSide, firstY, lineMarginSide, lastY, linePaint);  
  27.            //画了线,就画圆  
  28.            if (getChildAt(i) != null && i != 0) {  
  29.                int top = getChildAt(i).getTop();  
  30.                //记录值  
  31.                int Y = top + getChildAt(i).getPaddingTop() + lineDynamicDimen;  
  32.                canvas.drawCircle(lineMarginSide, Y, pointSize, pointPaint);  
  33.            }  
  34.        }  
  35.    }  

 

这里说说思路:

  首先我们在ondraw里面获取子控件的数量,然后通过子控件的属性定位我们的时间轴

  第一步我们先确定第一个子控件的位置,这里因为垂直的时间轴,所以我们通过top+paddingTop来确定我们的结点Y位置,同时引用我们xml定义好的dynamic值来微调。同时记录下此时第一个结点的x,y

  第二步我们确定最后一个控件的位置,方法同第一步,也记录下此时最后一个节点的x,y。同时调用drawBitmap画出我们的icon

  第三步我们就画第一个和最后一个之间的子控件的线和结点。相关注释都在代码中标注好了,所以这里就不再详细阐述了

  最后,因为是一个继承LinearLayoutViewGroup,所以使用方法直接addView或者xml里面愉悦的塞进去吧-V-

这个工程还没扩展完成,以后如果有时间我希望能把水平方向的也弄出来。当然,如果诸位有更nice的修正欢迎PullRequest.

 

这是一个简单的时间轴定制,希望能够帮到你-V-(详细代码请看github


附:

完整代码:

[java] viewplain copy

print?

  1. package razerdp.widget;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Paint;  
  8. import android.graphics.drawable.BitmapDrawable;  
  9. import android.util.AttributeSet;  
  10. import android.widget.LinearLayout;  
  11.   
  12. /** 
  13.  * Created by 大灯泡 on 2016/1/21. 
  14.  * 简易带有时间轴的linearlayout 
  15.  */  
  16. public class UnderLineLinearLayout extends LinearLayout {  
  17.     //=============================================================元素定义  
  18.     private Bitmap mIcon;  
  19.     //line location  
  20.     private int lineMarginSide;  
  21.     private int lineDynamicDimen;  
  22.     //line property  
  23.     private int lineStrokeWidth;  
  24.     private int lineColor;  
  25.     //point property  
  26.     private int pointSize;  
  27.     private int pointColor;  
  28.   
  29.     //=============================================================paint  
  30.     private Paint linePaint;  
  31.     private Paint pointPaint;  
  32.     //=============================================================其他辅助参数  
  33.     //第一个点的位置  
  34.     private int firstX;  
  35.     private int firstY;  
  36.     //最后一个图的位置  
  37.     private int lastX;  
  38.     private int lastY;  
  39.     //默认垂直  
  40.     private int curOrientation = VERTICAL;  
  41.     private Context mContext;  
  42.   
  43.     //开关  
  44.     private boolean drawLine = true;  
  45.   
  46.     public UnderLineLinearLayout(Context context) {  
  47.         this(context, null);  
  48.     }  
  49.   
  50.     public UnderLineLinearLayout(Context context, AttributeSet attrs) {  
  51.         this(context, attrs, 0);  
  52.     }  
  53.   
  54.     public UnderLineLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  55.         super(context, attrs, defStyleAttr);  
  56.         TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.UnderLineLinearLayout);  
  57.         lineMarginSide = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_line_margin_side, 10);  
  58.         lineDynamicDimen = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_line_dynamic_dimen, 0);  
  59.         lineStrokeWidth = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_line_stroke_width, 2);  
  60.         lineColor = attr.getColor(R.styleable.UnderLineLinearLayout_line_color, 0xff3dd1a5);  
  61.         pointSize = attr.getDimensionPixelSize(R.styleable.UnderLineLinearLayout_point_size, 8);  
  62.         pointColor = attr.getDimensionPixelOffset(R.styleable.UnderLineLinearLayout_point_color, 0xff3dd1a5);  
  63.   
  64.         int iconRes = attr.getResourceId(R.styleable.UnderLineLinearLayout_icon_src, R.drawable.ic_ok);  
  65.         BitmapDrawable temp = (BitmapDrawable) context.getResources().getDrawable(iconRes);  
  66.         if (temp != null) mIcon = temp.getBitmap();  
  67.   
  68.         curOrientation = getOrientation();  
  69.         attr.recycle();  
  70.         initView(context);  
  71.     }  
  72.   
  73.     private void initView(Context context) {  
  74.         this.mContext = context;  
  75.   
  76.         linePaint = new Paint();  
  77.         linePaint.setAntiAlias(true);  
  78.         linePaint.setDither(true);  
  79.         linePaint.setColor(lineColor);  
  80.         linePaint.setStrokeWidth(lineStrokeWidth);  
  81.         linePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  82.   
  83.         pointPaint = new Paint();  
  84.         pointPaint.setAntiAlias(true);  
  85.         pointPaint.setDither(true);  
  86.         pointPaint.setColor(pointColor);  
  87.         pointPaint.setStyle(Paint.Style.FILL);  
  88.     }  
  89.   
  90.     @Override  
  91.     protected void onDraw(Canvas canvas) {  
  92.         super.onDraw(canvas);  
  93.         if (drawLine) {  
  94.             drawTimeLine(canvas);  
  95.         }  
  96.     }  
  97.   
  98.     private void drawTimeLine(Canvas canvas) {  
  99.         int childCount = getChildCount();  
  100.   
  101.         if (childCount > 0) {  
  102.             //大于1,证明至少有2个,也就是第一个和第二个之间连成线,第一个和最后一个分别有点/icon  
  103.             if (childCount > 1) {  
  104.                 switch (curOrientation) {  
  105.                     case VERTICAL:  
  106.                         drawFirstChildViewVertical(canvas);  
  107.                         drawLastChildViewVertical(canvas);  
  108.                         drawBetweenLineVertical(canvas);  
  109.                         break;  
  110.                     case HORIZONTAL:  
  111.                         break;  
  112.                     default:  
  113.                         break;  
  114.                 }  
  115.             }  
  116.             else if (childCount == 1) {  
  117.                 switch (curOrientation) {  
  118.                     case VERTICAL:  
  119.                         drawFirstChildViewVertical(canvas);  
  120.                         break;  
  121.                     case HORIZONTAL:  
  122.                         break;  
  123.                     default:  
  124.                         break;  
  125.                 }  
  126.             }  
  127.         }  
  128.     }  
  129.   
  130.     private void drawFirstChildViewVertical(Canvas canvas) {  
  131.         if (getChildAt(0) != null) {  
  132.             int top = getChildAt(0).getTop();  
  133.             //记录值  
  134.             firstX = lineMarginSide;  
  135.             firstY = top + getChildAt(0).getPaddingTop() + lineDynamicDimen;  
  136.             //画一个圆  
  137.             canvas.drawCircle(firstX, firstY, pointSize, pointPaint);  
  138.         }  
  139.     }  
  140.   
  141.     private void drawLastChildViewVertical(Canvas canvas) {  
  142.         if (getChildAt(getChildCount() - 1) != null) {  
  143.             int top = getChildAt(getChildCount() - 1).getTop();  
  144.             //记录值  
  145.             lastX = lineMarginSide - (mIcon.getWidth() >> 1);  
  146.             lastY = top + getChildAt(getChildCount() - 1).getPaddingTop() + lineDynamicDimen;  
  147.             //画一个图  
  148.             canvas.drawBitmap(mIcon, lastX, lastY, null);  
  149.         }  
  150.     }  
  151.   
  152.     private void drawBetweenLineVertical(Canvas canvas) {  
  153.         for (int i = 0; i < getChildCount() - 1; i++) {  
  154.             //画剩下的  
  155.             canvas.drawLine(lineMarginSide, firstY, lineMarginSide, lastY, linePaint);  
  156.             //画了线,就画圆  
  157.             if (getChildAt(i) != null && i != 0) {  
  158.                 int top = getChildAt(i).getTop();  
  159.                 //记录值  
  160.                 int Y = top + getChildAt(i).getPaddingTop() + lineDynamicDimen;  
  161.                 canvas.drawCircle(lineMarginSide, Y, pointSize, pointPaint);  
  162.             }  
  163.         }  
  164.     }  
  165. }  

 

这篇关于快速简单的定制一个时间轴布局(UnderLineLinearLayout)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1027035

相关文章

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

shell脚本快速检查192.168.1网段ip是否在用的方法

《shell脚本快速检查192.168.1网段ip是否在用的方法》该Shell脚本通过并发ping命令检查192.168.1网段中哪些IP地址正在使用,脚本定义了网络段、超时时间和并行扫描数量,并使用... 目录脚本:检查 192.168.1 网段 IP 是否在用脚本说明使用方法示例输出优化建议总结检查 1

四种简单方法 轻松进入电脑主板 BIOS 或 UEFI 固件设置

《四种简单方法轻松进入电脑主板BIOS或UEFI固件设置》设置BIOS/UEFI是计算机维护和管理中的一项重要任务,它允许用户配置计算机的启动选项、硬件设置和其他关键参数,该怎么进入呢?下面... 随着计算机技术的发展,大多数主流 PC 和笔记本已经从传统 BIOS 转向了 UEFI 固件。很多时候,我们也

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert