Android自定义控件实现时间轴

本文实例为大家分享了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)

仅供大家参考!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 教你3分钟了解Android 简易时间轴的实现方法

    一.有段时间没更了,因为一直在思索,应该写点什么,真的是无比纠结.这一回,就给大家分享一款简便好用的,小编自制的土晾时间轴. 附上XML预览图: 效果图 注:小黄鸭不是效果哈,是为了保护个人隐私P上去的: 1.新建一个自定义控件: public class WorkExcView extends LinearLayout { private TextView dataLeft; private TextView dataRight; private TextView company; priva

  • Android实现列表时间轴

    本文实例为大家分享了Android列表时间轴展示的具体代码,供大家参考,具体内容如下 实现的效果图如下: 实现的方式是利用recycleview的ItemDecoration这个抽象类,就是我们经常用来画分割线的的这个类, 具体如下 public class DividerItemDecoration extends RecyclerView.ItemDecoration{ // 写右边字的画笔(具体信息) private Paint mPaint; // 写左边日期字的画笔( 时间 + 日期)

  • Android使用自定义View实现横行时间轴效果

    前言 本篇文章会说下如何使用并且要用麻烦的自定义 view 去实现时间轴效果,以及如何分析.实现自定义 view. 需要具备的知识:Paint.Canvas.自定义 view 的绘制流程. 欢迎留言交流. 一.已经有很多 RecycleView 实现时间轴的例子,为何还要费劲的使用自定义 view 去实现时间轴? 首先看下最终想要的效果: 根据上图可以总结出以下几点: 每个阶段要显示时间.阶段名.状态图标.中间有虚线: 文字上下交错显示: 相邻阶段的文字在垂直方向上是可以相交的: 时间轴的个数不

  • Android自定义View实现垂直时间轴布局

    时间轴,顾名思义就是将发生的事件按照时间顺序罗列起来,给用户带来一种更加直观的体验.京东和淘宝的物流顺序就是一个时间轴,想必大家都不陌生,如下图: 分析 实现这个最常用的一个方法就是用ListView,我这里用继承LinearLayout的方式来实现.首先定义了一些自定义属性: attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable

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

     实现思路: 该View是通过ListView实现的,通过实体两个字段内容content和时间time来展示每个ListItem 时间轴是使用上面一条线(20dp)和中间一个圆(15dp)和下面一条线(40dp)组装成的 在ListView中,设置其分割线为空,并且没有点击效果 效果图: 步骤一:使用xml画出一个灰色的圆点(time_cycle.xml) <?xml version="1.0" encoding="utf-8"?> <shape

  • Android自定义时间轴的实现过程

    本文讲述Android自定义时间轴的实现过程,供大家参考,具体内容如下 相关视频链接: Android自定义控件系列 http://edu.csdn.net/course/detail/3719/65396 Android视频全系列 http://edu.csdn.net/course/detail/2741/43163 时间轴效果,实际上非常简单,就是listView中一个又一个的条目而已-.大家可以只关注一个条目. 首先展示一个条目的布局效果 <?xml version="1.0&qu

  • Android自定义view仿淘宝快递物流信息时间轴

    学了Android有一段时间了,一直没有时间写博客,趁着周末有点空,就把自己做的一些东西写下来. 一方面锻炼一下自己的写文档的能力,另一方面分享代码的同时也希望能与大家交流一下技术,共同学习,共同进步. 废话不多少说,我们先来看看我们自定义view要实现的效果: 效果图 自定义属性 <resources> <declare-styleable name="TimeLineView"> <attr name="timelineRadius"

  • Android自定义指示器时间轴效果实例代码详解

    指示器时间轴在外卖.购物类的APP里会经常用到,效果大概就像下面这样,看了网上很多文章,大都是自己绘制,太麻烦,其实通过ListView就可以实现. 在Activity关联的布局文件activity_main.xml中放置一个ListView,代码如下.由于这个列表只是用于展示信息,并不需要用户去点击,所以将其clickable属性置为false:为了消除ListView点击产生的波纹效果,我们设置其listSelector属性的值为透明:我们不需要列表项之间的分割线,所以设置其divider属

  • Android实现快递物流时间轴效果

    本文实例为大家分享了Android实现快递物流时间轴效果展示的具体代码,供大家参考,具体内容如下 首先,这篇参考了别人的代码.根据自己的项目需求简单改造了一下,效果图如下 xml:代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:lay

  • Android自定义控件实现时间轴

    本文实例为大家分享了Android自定义控件实现时间轴的具体代码,供大家参考,具体内容如下 由于项目中有需求,就简单的封装一个,先记录一下,有时间上传到github. 1.先增加自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="global_TimelineLayout"> <!--时间轴左

  • android自定义控件实现简易时间轴(1)

    本文实例为大家分享了android自定义控件实现简易时间轴的具体代码,供大家参考,具体内容如下 之前项目需要写一个消费记录,类似于时间轴似的控件,自身在自定义控件这里不咋地(…),最后搞了一个这个demo 效果图: 这里就是绘制圆和上下两条线 1.资源文件,定义一些基本的属性: <?xml version="1.0" encoding="utf-8"?> <resources>     <attr name="textSize

  • android自定义控件实现简易时间轴(2)

    这篇做了一个简单的时间轴控件.右侧的数据就是一个简单的字符串.问题还是有的,当右侧的文字长度不一样的时候就会有问题了.现在可以修改一下适配右侧的文字. 效果如下: 代码: private Paint bgPaint, linePaint, borderPaint,textPaint; private Rect bgRect, textRect; //基本属性 private int mTextSize; private int mTextColor; private String mTextTi

随机推荐