本文实例为大家分享了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、自定义时间轴类:
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) {
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)
仅供大家参考!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。