package com.casic.endoscope.widgets import android.content.Context import android.graphics.BlurMaskFilter import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.graphics.Rect import android.util.AttributeSet import android.util.Log import android.view.MotionEvent import android.view.View import com.casic.endoscope.R import com.pengxh.kt.lite.extensions.dp2px import kotlin.math.acos import kotlin.math.cos import kotlin.math.hypot import kotlin.math.pow import kotlin.math.sin import kotlin.math.sqrt class DirectionControlView constructor(context: Context, attrs: AttributeSet) : View(context, attrs), View.OnTouchListener { private val kTag = "DirectionControlView" private val maskFilter = BlurMaskFilter(15f, BlurMaskFilter.Blur.SOLID) //View中心X坐标 private var centerX = 0f //View中心Y坐标 private var centerY = 0f private lateinit var tickPaint: Paint private lateinit var outerPaint: Paint private lateinit var innerPaint: Paint //View属性 private val circleRadius: Int private val outerColor: Int private val outerStroke: Int private val innerRadius: Int private val innerColor: Int private val innerStroke: Int private val renderColor: Int //滑动圆的圆心 private var moveCircleX = 0f private var moveCircleY = 0f //控件外边界 private val viewRadius: Int private val rect: Rect private val maxMoveRadius: Int private lateinit var directionPaints: ArrayList<Paint> private lateinit var leftPath: Path private lateinit var topPath: Path private lateinit var rightPath: Path private lateinit var bottomPath: Path // 各方位状态 private var leftTurn = false private var topTurn = false private var rightTurn = false private var bottomTurn = false init { val attr = context.obtainStyledAttributes(attrs, R.styleable.DirectionControlView) circleRadius = attr.getDimensionPixelOffset( R.styleable.DirectionControlView_ctrl_radius, 150 ) //中心圆半径 innerRadius = circleRadius / 3 outerColor = attr.getColor(R.styleable.DirectionControlView_ctrl_outer_color, Color.LTGRAY) outerStroke = attr.getDimensionPixelOffset( R.styleable.DirectionControlView_ctrl_outer_stroke, 3 ) innerColor = attr.getColor(R.styleable.DirectionControlView_ctrl_inner_color, Color.WHITE) innerStroke = attr.getDimensionPixelOffset( R.styleable.DirectionControlView_ctrl_inner_stroke, 3 ) renderColor = attr.getColor(R.styleable.DirectionControlView_ctrl_render_color, Color.BLUE) attr.recycle() //辅助框 viewRadius = circleRadius + 5.dp2px(context) rect = Rect(-viewRadius, -viewRadius, viewRadius, viewRadius) //滑动圆心最大距离 maxMoveRadius = innerRadius * 2 //初始化画笔 initPaint() //事件监听 setOnTouchListener(this) } private fun initPaint() { tickPaint = Paint() tickPaint.color = Color.DKGRAY tickPaint.style = Paint.Style.STROKE tickPaint.strokeWidth = 1f tickPaint.isAntiAlias = true outerPaint = Paint() outerPaint.color = outerColor outerPaint.style = Paint.Style.STROKE outerPaint.strokeWidth = outerStroke.toFloat() outerPaint.isAntiAlias = true innerPaint = Paint() innerPaint.color = innerColor innerPaint.style = Paint.Style.FILL innerPaint.isAntiAlias = true //设置光晕 innerPaint.maskFilter = maskFilter /** * 左上右下 * */ directionPaints = createDirectionPaint() //箭头顶点距离圆心的距离 val distance = maxMoveRadius * 1.35f //箭头每边长 val l = innerRadius * 0.25f //箭头顶点与圆心的连线,箭头边长之间的夹角 val cos = cos(Math.PI / 4).toFloat() val sin = sin(Math.PI / 4).toFloat() leftPath = Path() leftPath.moveTo(-distance, 0f) leftPath.lineTo(-distance + l * cos, -l * sin) leftPath.moveTo(-distance, 0f) leftPath.lineTo(-distance + l * cos, l * sin) topPath = Path() topPath.moveTo(0f, -distance) topPath.lineTo(-l * cos, -distance + l * sin) topPath.moveTo(0f, -distance) topPath.lineTo(l * cos, -distance + l * sin) rightPath = Path() rightPath.moveTo(distance, 0f) rightPath.lineTo(distance - l * cos, -l * sin) rightPath.moveTo(distance, 0f) rightPath.lineTo(distance - l * cos, l * sin) bottomPath = Path() bottomPath.moveTo(0f, distance) bottomPath.lineTo(-l * cos, distance - l * sin) bottomPath.moveTo(0f, distance) bottomPath.lineTo(l * cos, distance - l * sin) } //批量创建画笔 private fun createDirectionPaint(): ArrayList<Paint> { val result = ArrayList<Paint>() for (i in 0 until 4) { val paint = Paint() paint.color = innerColor paint.style = Paint.Style.STROKE paint.strokeCap = Paint.Cap.ROUND paint.strokeWidth = innerStroke.toFloat() paint.isAntiAlias = true //设置光晕 paint.maskFilter = maskFilter result.add(paint) } return result } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) centerX = (w shr 1).toFloat() centerY = (h shr 1).toFloat() } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec) val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec) val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec) val heightSpecSize = MeasureSpec.getSize(heightMeasureSpec) // 获取宽 val mWidth: Int = if (widthSpecMode == MeasureSpec.EXACTLY) { // match_parent/精确值 widthSpecSize } else { // wrap_content,外边界宽 (viewRadius * 2) } // 获取高 val mHeight: Int = if (heightSpecMode == MeasureSpec.EXACTLY) { // match_parent/精确值 heightSpecSize } else { // wrap_content,外边界高 (viewRadius * 2) } // 设置该view的宽高 setMeasuredDimension(mWidth, mHeight) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) /** * 画布移到中心位置,方便绘制一系列图形 */ canvas.translate(centerX, centerY) // drawGuides(canvas) //画外圆 canvas.drawCircle(0f, 0f, circleRadius.toFloat(), outerPaint) //画内圆 canvas.drawCircle(moveCircleX, moveCircleY, innerRadius.toFloat(), innerPaint) //画上下左右箭头 canvas.drawPath(leftPath, directionPaints[0]) canvas.drawPath(topPath, directionPaints[1]) canvas.drawPath(rightPath, directionPaints[2]) canvas.drawPath(bottomPath, directionPaints[3]) } override fun onTouch(v: View, event: MotionEvent): Boolean { val action = event.action val x = event.x val y = event.y when (action) { MotionEvent.ACTION_DOWN -> { Log.d(kTag, "onTouch => 按下") } MotionEvent.ACTION_MOVE -> { /** * 计算可滑动圆的圆心位置 * */ var deltaX = x - circleRadius val deltaY = y - circleRadius //计算滑动的圆心距离圆心的距离。hypot 表示平方和的开平方 var distance = hypot(deltaX, deltaY) if (distance >= maxMoveRadius) { distance = maxMoveRadius.toFloat() } if (deltaX >= maxMoveRadius) { deltaX = maxMoveRadius - 0.00001f } else if (deltaX < -maxMoveRadius) { deltaX = -maxMoveRadius + 0.00001f } moveCircleX = deltaX val tempY = distance.pow(2) - deltaX.pow(2) moveCircleY = if (deltaY < 0) { -sqrt(tempY) } else { sqrt(tempY) } if (distance >= innerRadius) { Log.d(kTag, "onTouch => $distance, $deltaX, $tempY, $moveCircleY") val arcCos = acos(deltaX / distance) val degree = Math.toDegrees(arcCos.toDouble()) if (degree in 45.0..135.0) { if (moveCircleY < 0) { updateDirectionColor(ControlDirection.TOP) } else { updateDirectionColor(ControlDirection.BOTTOM) } } else { if (moveCircleX < 0) { updateDirectionColor(ControlDirection.LEFT) } else { updateDirectionColor(ControlDirection.RIGHT) } } } else { Log.d(kTag, "onTouch => 点击了中心") } } MotionEvent.ACTION_UP -> { moveCircleX = 0f moveCircleY = 0f directionPaints.forEach { it.color = Color.WHITE } } } invalidate() return true } private fun updateDirectionColor(direction: ControlDirection) { when (direction) { ControlDirection.LEFT -> { directionPaints[0].color = renderColor for (i in 1 until directionPaints.size) { directionPaints[i].color = Color.WHITE } } ControlDirection.TOP -> { directionPaints[0].color = Color.WHITE directionPaints[1].color = renderColor directionPaints[2].color = Color.WHITE directionPaints[3].color = Color.WHITE } ControlDirection.RIGHT -> { directionPaints[0].color = Color.WHITE directionPaints[1].color = Color.WHITE directionPaints[2].color = renderColor directionPaints[3].color = Color.WHITE } ControlDirection.BOTTOM -> { for (i in 0 until directionPaints.size - 1) { directionPaints[i].color = Color.WHITE } directionPaints[3].color = renderColor } } } /** * 辅助线 * */ private fun drawGuides(canvas: Canvas) { //最外层方框,即自定义View的边界 canvas.drawRect(rect, tickPaint) //触摸圆可到达的最大圆 canvas.drawCircle(0f, 0f, (innerRadius * 2).toFloat(), tickPaint) //中心横线 canvas.drawLine(-viewRadius.toFloat(), 0f, viewRadius.toFloat(), 0f, tickPaint) //中心竖线 canvas.drawLine(0f, -viewRadius.toFloat(), 0f, viewRadius.toFloat(), tickPaint) //对角线 canvas.drawLine( -viewRadius.toFloat(), -viewRadius.toFloat(), viewRadius.toFloat(), viewRadius.toFloat(), tickPaint ) canvas.drawLine( -viewRadius.toFloat(), viewRadius.toFloat(), viewRadius.toFloat(), -viewRadius.toFloat(), tickPaint ) } enum class ControlDirection { LEFT, TOP, RIGHT, BOTTOM } private var listener: OnWheelTouchListener? = null fun setOnWheelTouchListener(listener: OnWheelTouchListener?) { this.listener = listener } interface OnWheelTouchListener { /** * 中间 */ fun onCenterClicked() /** * 左 */ fun onLeftTurn() /** * 上 */ fun onTopTurn() /** * 右 */ fun onRightTurn() /** * 下 */ fun onBottomTurn() /** * 松开 */ fun onActionTurnUp(dir: ControlDirection) } }