Android自定义控件实现时间轴

作者:梦想不只是梦与想 时间:2021-07-12 04:13:08 

本文实例为大家分享了Android自定义控件实现时间轴的具体代码,供大家参考,具体内容如下

由于项目中有需求,就简单的封装一个,先记录一下,有时间上传到github。

1、先增加自定义属性:


<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="global_TimelineLayout">
       <!--时间轴左偏移值-->
       <attr name="global_line_margin_left" format="dimension" />
       <!--时间轴上偏移值-->
       <attr name="global_line_margin_top" format="dimension" />
       <!--线宽-->
       <attr name="global_line_stroke_width" format="dimension" />
       <!--线的颜色-->
       <attr name="global_line_color" format="color" />
       <!--点的大小-->
       <attr name="global_point_inner_size" format="dimension" />
       <attr name="global_point_out_size" format="dimension" />
       <!--点的上偏移值-->
       <attr name="global_point_margin_top" format="dimension" />
       <!--点的颜色-->
       <attr name="global_point_inner_color" format="color" />
       <attr name="global_point_out_color" format="color" />
       <!--图标-->
       <attr name="global_icon_src" format="reference" />
       <!--虚线-->
       <attr name="global_dash_gap" format="dimension" />
       <attr name="global_dash_width" format="dimension" />
   </declare-styleable>
</resources>

2、自定义时间轴类:


/**
    * 时间轴控件
* <p>The following snippet shows how to include a linear layout in your layout XML file:</p>
*
* <com.taoche.mars.commonres.widget.TimelineLayout
   android:id="@+id/tl_02"
   android:layout_width="40dp"
   android:layout_height="match_parent"
   app:global_line_margin_left="10dp"
   app:global_line_margin_top="0dp"
   app:global_point_margin_top="10dp"
   app:global_point_inner_color="#377CFF"
   app:global_point_out_color="#FFE8F0FF"
   app:global_point_out_size="8dp"
   app:global_point_inner_size="4dp"
   app:global_dash_width="8dp"
   app:global_dash_gap="2dp"
   app:global_line_color="#C9DCFF">
   </com.taoche.mars.commonres.widget.TimelineLayout>
*
* <p>The following snippet shows how to java file:</p>
*  timelineLayout.setPointMarginTop(10)
   timelineLayout.setLineMarginTop(10)
   timelineLayout.setPointMarginTop(40)
   timelineLayout.setInterrupt(true)
*/
class TimeLinearLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
                                              defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {
   private var mContext: Context? = null

private var mLineMarginLeft: Int = 10
   private var mLineMarginTop: Int = 0
   private var mPointMarginTop: Int = 0
   private var mLineStrokeWidth: Int = 2
   private var mLineColor: Int = 0
   //内圆半径
   private var mPointInnerSize: Int = 8
   //外圆半径
   private var mPointOutSize: Int = 18
   //内圆颜色
   private var mPointInnerColor: Int = 0
   //外圆颜色
   private var mPointOutColor: Int = 0
   //虚线宽
   private var mDashWidth: Int = 0
   //虚线空白宽
   private var mDashGap: Int = 0
   //是否中断
   private var mInterrupt: Boolean = false
   private var mIcon: Bitmap? = null
   //线的画笔
   private var mLinePaint: Paint? = null
   //点的画笔
   private var mPointPaint: Paint? = null

//第一个点的位置
   private var mFirstX = 0
   private var mFirstY = 0

//最后一个图标的位置
   private var mLastX = 0
   private var mLastY = 0

init {
       setLayerType(View.LAYER_TYPE_SOFTWARE, null) //开启硬件加速
       val ta = context.obtainStyledAttributes(attrs, R.styleable.global_TimelineLayout)
       mLineMarginLeft = ta.getDimensionPixelOffset(R.styleable.global_TimelineLayout_global_line_margin_left, 10)
       mLineMarginTop = ta.getDimensionPixelOffset(R.styleable.global_TimelineLayout_global_line_margin_top, 0)
       mPointMarginTop = ta.getDimensionPixelOffset(R.styleable.global_TimelineLayout_global_point_margin_top, 0)
       mLineStrokeWidth = ta.getDimensionPixelOffset(R.styleable.global_TimelineLayout_global_line_stroke_width, 2)
       mLineColor = ta.getColor(R.styleable.global_TimelineLayout_global_line_color, -0xc22e5b)
       mPointInnerSize = ta.getDimensionPixelSize(R.styleable.global_TimelineLayout_global_point_inner_size, 8)
       mPointOutSize = ta.getDimensionPixelSize(R.styleable.global_TimelineLayout_global_point_out_size, 18)
       mPointInnerColor = ta.getColor(R.styleable.global_TimelineLayout_global_point_inner_color, -0xc22e5b)
       mPointOutColor = ta.getColor(R.styleable.global_TimelineLayout_global_point_out_color, -0x170f01)
       mDashGap = ta.getDimensionPixelOffset(R.styleable.global_TimelineLayout_global_dash_gap, 0)
       mDashWidth = ta.getDimensionPixelOffset(R.styleable.global_TimelineLayout_global_dash_width, 0)
       val iconRes = ta.getResourceId(R.styleable.global_TimelineLayout_global_icon_src, View.NO_ID)
       if (iconRes > View.NO_ID) {
           val drawable = ContextCompat.getDrawable(context,iconRes) as? BitmapDrawable
           if (drawable != null) {
               mIcon = drawable.bitmap
           }
       }
       ta.recycle()
       setWillNotDraw(false)
       initView(context)
   }

fun setLineMarginTop(lineMarginTop:Int){
       this.mLineMarginTop = lineMarginTop
   }

fun setPointMarginTop(pointMarginTop:Int){
       this.mPointMarginTop = pointMarginTop
   }

fun setInterrupt(interrupt:Boolean){
       this.mInterrupt = interrupt
   }

private fun initView(context: Context) {
       mContext = context
       mLinePaint = Paint()
       mLinePaint?.apply {
           isAntiAlias = true
           isDither = true
           color = mLineColor
           strokeWidth = mLineStrokeWidth.toFloat()
           style = Paint.Style.FILL_AND_STROKE
           //虚线设置
           if (mDashGap > 0 && mDashWidth > 0) {
               //mLinePaint.setPathEffect(new DashPathEffect(new float[]{20,5}, 20));
               pathEffect = DashPathEffect(floatArrayOf(mDashWidth.toFloat(), mDashGap.toFloat()), mDashWidth.toFloat())
           }
       }

mPointPaint = Paint()
       mPointPaint?.apply {
           isAntiAlias = true
           isDither = true
           color = mPointInnerColor
           style = Paint.Style.FILL
       }
   }

override fun onDraw(canvas: Canvas) {
       super.onDraw(canvas)
       drawTimeline(canvas)
   }

private fun drawTimeline(canvas: Canvas) {
       drawBetweenLine(canvas)
       drawFirstPoint(canvas)
       drawLastIcon(canvas)
   }

private fun drawFirstPoint(canvas: Canvas) {
       val top = top
       mFirstX = paddingLeft + mLineMarginLeft + max(mPointOutSize, mPointInnerSize)
       mFirstY = top + paddingTop + mPointMarginTop + max(mPointOutSize, mPointInnerSize)

//画圆外环
       mPointPaint?.color = mPointOutColor
       canvas.drawCircle(mFirstX.toFloat(), mFirstY.toFloat(), mPointOutSize.toFloat(), mPointPaint)
       //画圆内环
       mPointPaint?.color = mPointInnerColor
       canvas.drawCircle(mFirstX.toFloat(), mFirstY.toFloat(), mPointInnerSize.toFloat(), mPointPaint)
   }

private fun drawLastIcon(canvas: Canvas) {
       /*if (child != null) {
           int top = child.getTop();
           mLastX = mLineMarginLeft;
           mLastY = top + child.getPaddingTop() + mLineMarginTop;
           //画图
           canvas.drawBitmap(mIcon, mLastX - (mIcon.getWidth() >> 1), mLastY, null);
       }*/
       val top = top
       mLastX = mLineMarginLeft + paddingLeft
       mLastY = top + paddingTop + mLineMarginTop

//画图
       if (mIcon != null) {
           canvas.drawBitmap(mIcon, mLastX - (mIcon!!.width shr 1).toFloat(), height - mIcon!!.height.toFloat(), null)
       }
   }

private fun drawBetweenLine(canvas: Canvas) {
       val top = top
       mFirstX = paddingLeft + mLineMarginLeft + max(mPointOutSize, mPointInnerSize)
       mFirstY = top + paddingTop + mLineMarginTop
       mLastX = paddingLeft + mLineMarginLeft + max(mPointOutSize, mPointInnerSize)
       mLastY = if(!mInterrupt) {top + paddingTop + mLineMarginTop + height} else mPointMarginTop

//从开始的点到最后的图标之间,画一条线
       canvas.drawLine(mFirstX.toFloat(), mFirstY.toFloat(), mLastX.toFloat(), mLastY.toFloat(), mLinePaint)
       //画圆
       //val y = top + paddingTop + mLineMarginTop + mPointInnerSize
       //canvas.drawCircle(mFirstX, y, mPointSize, mPointPaint);
   }

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec)
       val mode = MeasureSpec.getMode(widthMeasureSpec)
       var measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
       val measuredHeight = MeasureSpec.getSize(heightMeasureSpec)
       if (mode == MeasureSpec.AT_MOST) {
           measuredWidth = paddingLeft + mLineMarginLeft + max(mPointOutSize, mPointInnerSize) * 2
       }
       setMeasuredDimension(measuredWidth, measuredHeight)
   }
}

布局中可以直接引用,如下:


<com.example.demo1224.TimelineLayout
               android:layout_width="wrap_content"
               android:layout_height="match_parent"
               app:line_margin_left="25dp"
               app:line_margin_top="0dp"
               app:point_margin_top="10dp"
               app:point_inner_color="#377CFF"
               app:point_out_color="#FFE8F0FF"
               app:point_out_size="8dp"
               app:point_inner_size="4dp"
               app:dash_width="8dp"
               app:dash_gap="2dp"
               app:line_color="#C9DCFF"
               android:orientation="vertical"
               android:background="@android:color/white">
           </com.example.demo1224.TimelineLayout>

也可以在代码里面动态设置相关属性(相关属性注释,在自定义属性时有说明):


timelineLayout.setPointMarginTop(10)
   timelineLayout.setLineMarginTop(10)
   timelineLayout.setPointMarginTop(40)
   timelineLayout.setInterrupt(true)

仅供大家参考!

来源:https://blog.csdn.net/wangsen927/article/details/116116943

标签:Android,时间轴
0
投稿

猜你喜欢

  • Java非法字符: ‘\\ufeff‘问题及说明

    2023-02-01 09:06:07
  • Spring Boot 利用 XML 方式整合 MyBatis

    2023-02-09 23:01:18
  • Java+Nginx实现POP、IMAP、SMTP邮箱代理服务

    2023-11-26 10:31:47
  • Java数据结构之常见排序算法(下)

    2022-05-03 12:49:51
  • Java常见数据结构面试题(带答案)

    2023-11-24 19:44:05
  • MyBatis持久层框架的用法知识小结

    2022-05-21 17:12:55
  • Java编写日历表的3种方式

    2023-02-16 00:50:31
  • C#实现ini文件读写操作

    2022-02-26 11:01:19
  • 详解Android通知栏沉浸式/透明化完整解决方案

    2023-09-06 03:59:11
  • Kotlin协程之Flow异常示例处理

    2022-10-02 09:30:58
  • Android返回键功能的实现方法

    2021-10-01 12:54:53
  • hadoop分布式环境搭建过程

    2023-09-23 15:18:50
  • Java各种排序算法汇总(冒泡,选择,归并,希尔及堆排序等)

    2021-10-05 14:46:23
  • java中使用zxing批量生成二维码立牌

    2021-12-31 04:31:03
  • 关于Java中的try-with-resources语句

    2022-10-21 16:49:54
  • Java读取Properties文件的七种方法的总结

    2022-08-12 13:27:18
  • java中Swing会奔跑的线程侠

    2021-12-14 23:47:36
  • Android开发ThreadPoolExecutor与自定义线程池详解

    2022-03-17 18:16:22
  • Java实现FTP上传与下载功能

    2021-09-22 18:28:51
  • Java中统计字符个数以及反序非相同字符的方法详解

    2022-10-21 10:48:02
  • asp之家 软件编程 m.aspxhome.com