Kotlin 实现仪表盘VIew

1.定义VelocityView

package sss

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.example.sosrcpro.R


class VelocityView :View{
    private var mCenterX = 0F  //中心点x
    private var mCenterY = 0F //中心点y
    private var minRadia = 0  //次最外圈的半径
    private var maxRadia = 0   //最外圈的半径
    private var mRectF: RectF? = null  //要绘制的圆区域
    private var paint:Paint? = null
    private var fillPaint:Paint? = null
    private var textPaint:Paint? = null
    private var smallScalePaint:Paint? = null
    private var bigScalePaint:Paint? = null
    private var litterCyclePaint:Paint? = null
    private var testPaint:Paint? =null
    private var colorArray = IntArray(7)
    private var paintWidth = 0
    private var mCurrentAngle = 195F
    private  var  valueAnimator: ValueAnimator? = null
    constructor(context: Context):this(context,null){

    }
    constructor(context: Context,attributeSet: AttributeSet?):this(context,attributeSet,0){
       //从xml中获得用户设定的值
        var valueSet = context.obtainStyledAttributes(attributeSet, R.styleable.VelocityView)
        minRadia = valueSet.getInteger(R.styleable.VelocityView_min_circle_radio,180)
        maxRadia = valueSet.getInteger(R.styleable.VelocityView_max_cicle_radio,300)
        paintWidth = maxRadia-minRadia
        paint = Paint(Paint.ANTI_ALIAS_FLAG)
        fillPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        litterCyclePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        smallScalePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        bigScalePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        testPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        colorArray[0] = Color.parseColor("#ff0000")
        colorArray[1] = Color.parseColor("#1fff00")
        colorArray[2] = Color.parseColor("#0fff00")
        colorArray[3] = Color.parseColor("#0f0000")
        colorArray[4] = Color.parseColor("#001f00")
        colorArray[5] = Color.parseColor("#001c00")
        colorArray[6] = Color.parseColor("#001000")
        var ff = FloatArray(4)
        ff[0]=0.1F
        ff[1]=0.3F
        ff[2]=0.7F
        ff[3]=1F
        paint?.apply {
            isAntiAlias = true
           style = Paint.Style.STROKE
            strokeWidth = paintWidth.toFloat()
            setShader(SweepGradient(mCenterX,mCenterY,colorArray,null))
        }
        textPaint?.apply {
            color = Color.WHITE
            style = Paint.Style.FILL
            strokeWidth = 2F
            textSize = 40F
            setTextAlign(Paint.Align.CENTER) //文字居中,不然瞄点会在左下
        }
        //小圆圈画笔
        litterCyclePaint?.apply {
            isAntiAlias = true
            strokeWidth = 10F
            style = Paint.Style.STROKE
            setShader(SweepGradient(mCenterX,mCenterY,colorArray,null))
        }
      
         //小刻度画笔
        smallScalePaint?.apply {
            strokeWidth = 5F
           color = Color.WHITE
        }
          //大刻度画笔
        bigScalePaint?.apply {
           color = Color.WHITE
           strokeWidth = 10F
        }
        //三角指针画笔
        fillPaint?.apply {
            strokeWidth = 10F
           style = Paint.Style.FILL
            setShader(SweepGradient(mCenterX,mCenterY,colorArray,null))
        }
        testPaint?.apply {
            isAntiAlias = true
            style = Paint.Style.STROKE
            strokeWidth=30F

        }
  //为什么这么多画笔,不重用,是应为重进入界面时会重绘,画笔不还原到最开始的状态就会出错,你参数都改了
  //肯定画不出一样的效果了
    }
    constructor(context: Context,attributeSet: AttributeSet?,type:Int):super(context,attributeSet){

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mCenterX = measuredWidth/2F
        mCenterY = measuredHeight/2F
      //RectF就是你的上下左右离父控件的边距
        mRectF = RectF(0F+paintWidth/2,0F+paintWidth/2,measuredWidth*1F-paintWidth/2,measuredHeight*1F-paintWidth/2)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //大圈
        canvas?.drawArc(RectF(mCenterX-mCenterY+paintWidth/2F,0F+paintWidth/2F,mCenterX+mCenterY-paintWidth/2F,mCenterY*2F-paintWidth/2F),20F,-220F,false,paint!!)
       //小圈
        canvas?.drawCircle(mCenterX,mCenterY,30F,litterCyclePaint!!)
        //刻度盘
        drawDial(canvas)
        //三角指针
        drawAngleByAngle(canvas,mCurrentAngle)
    }
    fun drawDial(canvas: Canvas?){
        //大刻度
        var per = 210F/50F;
        var flag = 0
        var starA = -15F;
        for(angle in 0..50){
                var finalAngle = starA+angle*per
                var value = finalAngle/180F*Math.PI
                var starPointX = mCenterX+Math.cos(value)*mCenterY
                var starPointY = mCenterY-Math.sin(value)*mCenterY
                var minRadiu = mCenterY-paintWidth/3F
                var endPointX = mCenterX+Math.cos(value)*minRadiu
                var entPointY = mCenterY-Math.sin(value)*minRadiu
                if(angle!=0)
                canvas?.drawLine(starPointX.toFloat(),starPointY.toFloat(),endPointX.toFloat(),entPointY.toFloat(),smallScalePaint!!)
                 if(flag%5==0){
                     var minR = mCenterY-paintWidth/2F
                     var endPointXX = mCenterX+Math.cos(value)*minR
                     var entPointYY = mCenterY-Math.sin(value)*minR
                     canvas?.drawLine(starPointX.toFloat(),starPointY.toFloat(),endPointXX.toFloat(),entPointYY.toFloat(),bigScalePaint!!)
                     var pointT = getPointByAngleAndRadiu(finalAngle,mCenterY-paintWidth/1F+30F,mCenterX,mCenterY)
                     var  ss:String  = (150F-angle*3F).toInt().toString()
                     canvas?.drawText(ss,pointT.x,pointT.y+10F,textPaint!!)
                 }
                flag++
        }

    }
    //根据角度,半径,中心点得到对应的的坐标
    fun getPointByAngleAndRadiu(angle:Float,radiu:Float,centerX:Float,centerY:Float):PointF
    {
        var radian = angle/180F*Math.PI
        var x = centerX+Math.cos(radian)*radiu
        var y = centerY-Math.sin(radian)*radiu
        return PointF(x.toFloat(),y.toFloat())
    }
    //画三角指针
    fun drawAngleByAngle(canvas: Canvas?,angle: Float){
        var angelW = 10F
        var path = Path()
        var radiu = mCenterY-paintWidth;  //指针所在圆的半径
        path.reset()
        var point1 = getPointByAngleAndRadiu(angle,radiu,mCenterX,mCenterY)
        var point2 = getPointByAngleAndRadiu(angle-90F,angelW,mCenterX,mCenterY)
        var point3 = getPointByAngleAndRadiu(angle+90F,angelW,mCenterX,mCenterY)
        path.moveTo(point1.x,point1.y)  //起点
        path.lineTo(point2.x,point2.y)
        path.lineTo(point3.x,point3.y)
        path.lineTo(point1.x,point1.y) //最后连线到起点
        canvas?.drawPath(path,fillPaint!!)
        canvas?.drawArc(RectF(mCenterX-angelW,mCenterY-angelW,mCenterX+angelW,mCenterY+angelW),90F-angle,180F,false,fillPaint!!)

    }
    //提供外部接口设置转动角度
   fun setNextValue(next:Float){
       var per = 2F/210F
       var temCur = mCurrentAngle
       var time = Math.abs(next-mCurrentAngle)*per
       valueAnimator?.also {
           it.cancel()
       }
       valueAnimator = ValueAnimator.ofFloat(0F,1F)
       valueAnimator?.duration = if(time.toLong()*1000L<500) 500L else time.toLong()*1000L
       valueAnimator?.addUpdateListener(object :ValueAnimator.AnimatorUpdateListener{
           override fun onAnimationUpdate(animation: ValueAnimator) {
              var ss =  animation.animatedValue as Float
               mCurrentAngle = temCur+(next-temCur)*(animation.animatedValue as Float)
               invalidate()  //  invalidate() 会重新绘制View,由于参数不断的修改,不断的OnDraW,这就有了动态的效果
           }
       })
       valueAnimator?.start()
    }
}

2.为了能在xml布局中设置参数,在values中添加attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="VelocityView">
    <attr name="min_circle_radio" format="integer"/>
    <attr name="max_cicle_radio" format="integer"/>
</declare-styleable>

</resources>

3.xml中引用

    <sss.VelocityView
        android:layout_width="match_parent"
        android:layout_height="300dp"
        app:min_circle_radio="150"
        app:max_cicle_radio="300"
        android:id="@+id/velocity_test"
        />

4.改变指针值,代码里的角度值为-15到195

    btn.setOnClickListener {
         var value = (-15..195).random()
             velocity_test.setNextValue(value.toFloat())
     }

5效果图
在这里插入图片描述
可以参考思路来源

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值