自定义view--轮盘(带触摸旋转事件)--kotlin代码-1

最近复习了自定义view相关知识,由于也在练习kotlin的编码方式,所以就用kotlin写了一个轮盘控件,有需要的可以借鉴一下,相关错误可以回复我。
这一篇主要是复习自定义view的相关知识如下:
1.自定义view的相关属性配置,如颜色,大小等。
2.自定义view的绘制方式,如画圆,画刻度,线条等。
3.自定义view的触控滚动事件添加。
下一篇我会增加相关其他知识点:
如动画效果,如手指拨动之后的惯性滚动效果,两点触控的缩放效果,动画的相关插值器效果,事件分发等。

现在进入正片:
相关效果如图:
这里写图片描述
首先是导入相关自定义view属性:
在kotlin中,自定义view的初始化操作都是在init方法里面进行的,所以第一步是需要在init方法里面获取相关属性值。

init {
        var typeArray: TypedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.MyView, 0, 0)
        try {
            border_color = typeArray.getColor(R.styleable.MyView_border_color, R.color.material_blue_grey_800)
            border_withd = typeArray.getDimension(R.styleable.MyView_border_withd, 2.0F)
        } finally {
            typeArray.recycle()
        }
        ...
    }

相关属性文件也是需要在values里面增加一个attrs的xml资源文件,相关内容如下:
目前只给定了颜色和绘制宽度。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView">
        <attr name="border_color" format="color" />
        <attr name="border_withd" format="dimension" />
    </declare-styleable>
</resources>

接下来需要根据控件大小给定相关尺寸,这个方法会在初始化之后执行:

 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        rectF = RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())

        withd = rectF!!.right - rectF!!.left
        height = rectF!!.bottom - rectF!!.top

        radius = if (withd!! < height!!) withd!! / 4 else height!! / 4

        smallLength = 15F
        largerLength = 30F

    }

相关变量说明如下:

internal var border_color: Int? = null//指定的颜色
    internal var border_withd: Float? = null//指定的宽度
    internal var paint: Paint? = null//画笔
    internal var rectF: RectF? = null//矩形区域
    internal var withd: Float? = null//控件宽度
    internal var height: Float? = null//控件高度
    internal var radius: Float? = null//圆盘半径
    internal var smallLength: Float? = null//短刻度长度
    internal var largerLength: Float? = null//长刻度长度
    internal var actAgree: Float? = null//变化角度
    internal var nowAgree: Float? = null//当前角度

一切就绪之后就是绘制操作了,上代码:

 override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //绘制一个圆角矩形边框
        canvas?.drawColor(0xFF000000.toInt())
        paint?.color = 0x66555555
        canvas?.drawRoundRect(RectF(rectF!!.centerX() - 0.9F * withd!! / 2, rectF!!.centerY() - 0.9F * height!! / 2, rectF!!.centerX() + 0.9F * withd!! / 2, rectF!!.centerY() + 0.9F * height!! / 2), 30F, 30F, paint)
        //绘制一个半径为radius的圆
        paint?.color = border_color!!
        canvas?.drawCircle(rectF!!.centerX(), rectF!!.centerY(), radius!!, paint)
        //绘制圆上的刻度
        var startX: Float? = null
        var startY: Float? = null
        var endX: Float? = null
        var endY: Float? = null
        for (i: Int in 1..60) {
            var agree = Math.PI / 180 * (i * 6 + actAgree!!)//这行代码用于指定刻度盘的触摸滚动
            startX = radius!! * Math.cos(agree).toFloat()
            startY = radius!! * Math.sin(agree).toFloat()

            if (i % 5 == 0) {
                endX = startX + largerLength!! * Math.cos(agree).toFloat()
                endY = startY + largerLength!! * Math.sin(agree).toFloat()
            } else {
                endX = startX + smallLength!! * Math.cos(agree).toFloat()
                endY = startY + smallLength!! * Math.sin(agree).toFloat()
            }

            startX += rectF!!.centerX()
            endX += rectF!!.centerX()
            startY += rectF!!.centerY()
            endY += rectF!!.centerY()
            canvas!!.drawLine(startX, startY, endX, endY, paint)
        }

        //绘制中心的小圆
        canvas!!.drawCircle(rectF!!.centerX(), rectF!!.centerY(), 20F, paint)
        //指定偏转角度然后绘制指针
//        nowAgree = (nowAgree!!+actAgree!!)%360 //用这句可以启动速度旋转
        canvas!!.rotate(nowAgree!!, rectF!!.centerX(), rectF!!.centerY())
        Log.e("test", "nowAgree" + nowAgree)
        canvas!!.drawLine(rectF!!.centerX(), rectF!!.centerY(), rectF!!.centerX(), rectF!!.centerY() - radius!!, paint)
    }

现在来处理相关触摸事件:

  internal var start_x: Float? = null//开始位置x
    internal var start_y: Float? = null//开始位置y
    internal var end_x: Float? = null//结束位置x
    internal var end_y: Float? = null//结束位置y
    internal var startAgree: Float? = null//开始角度
    internal var initAgree: Float? = null//初始化角度
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event!!.action) {
            MotionEvent.ACTION_DOWN -> {
                start_x = event.x
                start_y = event.y
                startAgree = getAgree(start_x, start_y)//获取开始位置的角度
                initAgree = nowAgree
                Log.e("test", "---------------------------")
            }
            MotionEvent.ACTION_MOVE -> {
                end_x = event.x
                end_y = event.y
                var endAgree = getAgree(end_x, end_y)//获取结束位置的角度
                actAgree = endAgree - startAgree!!
                //根据偏转角度进行界面刷新
                nowAgree = (initAgree!! + actAgree!!) % 360
                postInvalidate()
            }
            MotionEvent.ACTION_UP -> {
                initAgree = nowAgree//进行初始化角度的赋值
            }
        }
        return true
    }

下面是相关角度计算的算法,这个算法是通过角度相减的方式实现:

private fun getAgree(x: Float?, y: Float?): Float {
        if (x!! < rectF!!.centerX() && y!! < rectF!!.centerY()) {//a-180
            return Math.toDegrees((Math.atan(((rectF!!.centerY() - y) / (rectF!!.centerX() - x)).toDouble()))).toFloat() - 180
        } else if (x!! > rectF!!.centerX() && y!! < rectF!!.centerY()) {//-a
            return -Math.toDegrees((Math.atan(((rectF!!.centerY() - y) / (x - rectF!!.centerX())).toDouble()))).toFloat()
        } else if (x!! < rectF!!.centerX() && y!! > rectF!!.centerY()) {//180-a
            return 180 - Math.toDegrees((Math.atan(((y - rectF!!.centerY()) / (rectF!!.centerX() - x)).toDouble()))).toFloat()
        } else if (x!! > rectF!!.centerX() && y!! > rectF!!.centerY()) {//a
            return Math.toDegrees((Math.atan(((y - rectF!!.centerY()) / (x - rectF!!.centerX())).toDouble()))).toFloat()
        } else
            return 0F
    }

角度算法的相关原理如下(画的不好见谅):
这里写图片描述

将相关区域分成ABCD四个,触发起始点为q,结束点为w,相关角度计算都是以x轴线为基础:
AB区计算结果均为负角度,CD区角度均为正角度
A区:计算出来的角度为:(当前坐标以圆心点为直线相对x轴线的角度-180°)
B区:计算出来的角度为:-(当前坐标以圆心点为直线相对x轴线的角度)
C区:计算出来的角度为:180°-(当前坐标以圆心点为直线相对x轴线的角度)
D区:计算出来的角度为:当前坐标以圆心点为直线相对x轴线的角度
偏转角度值为:w点角度减去q点角度
将偏转角度值与当前实际角度值相加,再进行偏转绘制onDraw即可进行触控滚动了

完整代码如下:

package wlw.kotlintest

import android.animation.Animator
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.os.Handler
import android.os.Message
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.SurfaceView
import android.view.VelocityTracker
import java.util.*
import android.animation.AnimatorListenerAdapter
import android.support.v4.view.ViewCompat
import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.DecelerateInterpolator


/**
 * 标题:
 * 描述:
 * 作者: 陈聪
 * 创建时间:2017/10/9.
 */
open class MyView(context: Context, attrs: AttributeSet) : SurfaceView(context) {
    internal var border_color: Int? = null//指定的颜色
    internal var border_withd: Float? = null//指定的宽度
    internal var paint: Paint? = null//画笔
    internal var rectF: RectF? = null//矩形区域
    internal var withd: Float? = null//控件宽度
    internal var height: Float? = null//控件高度
    internal var radius: Float? = null//圆盘半径
    internal var smallLength: Float? = null//短刻度长度
    internal var largerLength: Float? = null//长刻度长度
    internal var actAgree: Float? = null//变化角度
    internal var nowAgree: Float? = null//当前角度

    init {
        var typeArray: TypedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.MyView, 0, 0)
        try {
            border_color = typeArray.getColor(R.styleable.MyView_border_color, R.color.material_blue_grey_800)
            border_withd = typeArray.getDimension(R.styleable.MyView_border_withd, 2.0F)
        } finally {
            typeArray.recycle()
        }
        paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint?.color = border_color!!
        paint?.style = Paint.Style.STROKE
        paint?.strokeWidth = border_withd!!
        nowAgree = 90F
        actAgree = 0F
        setWillNotDraw(false)//用于自定义viewonDraw方法的执行
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //绘制一个圆角矩形边框
        canvas?.drawColor(0xFF000000.toInt())
        paint?.color = 0x66555555
        canvas?.drawRoundRect(RectF(rectF!!.centerX() - 0.9F * withd!! / 2, rectF!!.centerY() - 0.9F * height!! / 2, rectF!!.centerX() + 0.9F * withd!! / 2, rectF!!.centerY() + 0.9F * height!! / 2), 30F, 30F, paint)
        //绘制一个半径为radius的圆
        paint?.color = border_color!!
        canvas?.drawCircle(rectF!!.centerX(), rectF!!.centerY(), radius!!, paint)
        //绘制圆上的刻度
        var startX: Float? = null
        var startY: Float? = null
        var endX: Float? = null
        var endY: Float? = null
        for (i: Int in 1..60) {
            var agree = Math.PI / 180 * (i * 6 + actAgree!!)//这行代码用于指定刻度盘的触摸滚动
            startX = radius!! * Math.cos(agree).toFloat()
            startY = radius!! * Math.sin(agree).toFloat()

            if (i % 5 == 0) {
                endX = startX + largerLength!! * Math.cos(agree).toFloat()
                endY = startY + largerLength!! * Math.sin(agree).toFloat()
            } else {
                endX = startX + smallLength!! * Math.cos(agree).toFloat()
                endY = startY + smallLength!! * Math.sin(agree).toFloat()
            }

            startX += rectF!!.centerX()
            endX += rectF!!.centerX()
            startY += rectF!!.centerY()
            endY += rectF!!.centerY()
            canvas!!.drawLine(startX, startY, endX, endY, paint)
        }

        //绘制中心的小圆
        canvas!!.drawCircle(rectF!!.centerX(), rectF!!.centerY(), 20F, paint)
        //指定偏转角度然后绘制指针
//        nowAgree = (nowAgree!!+actAgree!!)%360 //用这句可以启动速度旋转
        canvas!!.rotate(nowAgree!!, rectF!!.centerX(), rectF!!.centerY())
        Log.e("test", "nowAgree" + nowAgree)
        canvas!!.drawLine(rectF!!.centerX(), rectF!!.centerY(), rectF!!.centerX(), rectF!!.centerY() - radius!!, paint)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        rectF = RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())

        withd = rectF!!.right - rectF!!.left
        height = rectF!!.bottom - rectF!!.top

        radius = if (withd!! < height!!) withd!! / 4 else height!! / 4

        smallLength = 15F
        largerLength = 30F

    }

    internal var start_x: Float? = null//开始位置x
    internal var start_y: Float? = null//开始位置y
    internal var end_x: Float? = null//结束位置x
    internal var end_y: Float? = null//结束位置y
    internal var startAgree: Float? = null//开始角度
    internal var initAgree: Float? = null//初始化角度
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event!!.action) {
            MotionEvent.ACTION_DOWN -> {
                start_x = event.x
                start_y = event.y
                startAgree = getAgree(start_x, start_y)//获取开始位置的角度
                initAgree = nowAgree
                Log.e("test", "---------------------------")
            }
            MotionEvent.ACTION_MOVE -> {
                end_x = event.x
                end_y = event.y
                var endAgree = getAgree(end_x, end_y)//获取结束位置的角度
                actAgree = endAgree - startAgree!!
                //根据偏转角度进行界面刷新
                nowAgree = (initAgree!! + actAgree!!) % 360
                postInvalidate()
            }
            MotionEvent.ACTION_UP -> {
                initAgree = nowAgree//进行初始化角度的赋值
            }
        }
        return true
    }

    private fun getAgree(x: Float?, y: Float?): Float {
        if (x!! < rectF!!.centerX() && y!! < rectF!!.centerY()) {//a-180
            return Math.toDegrees((Math.atan(((rectF!!.centerY() - y) / (rectF!!.centerX() - x)).toDouble()))).toFloat() - 180
        } else if (x!! > rectF!!.centerX() && y!! < rectF!!.centerY()) {//-a
            return -Math.toDegrees((Math.atan(((rectF!!.centerY() - y) / (x - rectF!!.centerX())).toDouble()))).toFloat()
        } else if (x!! < rectF!!.centerX() && y!! > rectF!!.centerY()) {//180-a
            return 180 - Math.toDegrees((Math.atan(((y - rectF!!.centerY()) / (rectF!!.centerX() - x)).toDouble()))).toFloat()
        } else if (x!! > rectF!!.centerX() && y!! > rectF!!.centerY()) {//a
            return Math.toDegrees((Math.atan(((y - rectF!!.centerY()) / (x - rectF!!.centerX())).toDouble()))).toFloat()
        } else
            return 0F
    }
}

码云代码如下:
https://gitee.com/ccdy00/codes/089lrvatd1wki7upb3zqn65

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值