由于项目中有需求,就简单的封装一个,先记录一下,有时间上传到github。
1、先增加自定义属性:
2、自定义时间轴类:
/**
* 时间轴控件
* The following snippet shows how to include a linear layout in your layout XML file:
*
*
*
* The following snippet shows how to java file:
* 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)
}
}
布局中可以直接引用,如下:
也可以在代码里面动态设置相关属性(相关属性注释,在自定义属性时有说明):
timelineLayout.setPointMarginTop(10)
timelineLayout.setLineMarginTop(10)
timelineLayout.setPointMarginTop(40)
timelineLayout.setInterrupt(true)
仅供大家参考!