diff --git a/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt new file mode 100644 index 0000000..5fbc6f0 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt @@ -0,0 +1,386 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.ktd.R +import kotlin.math.abs +import kotlin.math.pow +import kotlin.math.sin +import kotlin.math.sqrt + +class SteeringWheelView constructor(context: Context, attrs: AttributeSet) : + View(context, attrs), View.OnTouchListener { + private val kTag = "SteeringWheelView" + + //画布中心x + private var canvasCenterX = 0f + + //画布中心y + private var canvasCenterY = 0f + + //控件直径 + private val diameter: Float + + //四个方位小点直径 + private val directionDiameter: Float + + //内部圆半径 + private var innerCircleRadius: Float = 0.0f + + //外圆区域 + private lateinit var outerCircleRectF: RectF + + //内部开关区域 + private lateinit var centerSwitchRectF: RectF + + //线条粗细 + private val borderStroke: Float + + //Paint + private val backgroundPaint: Paint + private val borderPaint: Paint + private val centerPaint: Paint + private val leftDirectionPaint: Paint + private val topDirectionPaint: Paint + private val rightDirectionPaint: Paint + private val bottomDirectionPaint: Paint + private val switchPaint: Paint + + //Color + private val directionColor: Int + private val switchColor: Int + + // 各控件使用状态 + private var leftTurn = false + private var topTurn = false + private var rightTurn = false + private var bottomTurn = false + private var centerTurn = false + + init { + val type = context.obtainStyledAttributes(attrs, R.styleable.SteeringWheelView) + diameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_diameter, 200f + ) + val borderColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_borderColor, Color.CYAN + ) + val backgroundColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_backgroundColor, Color.WHITE + ) + borderStroke = type.getDimension( + R.styleable.SteeringWheelView_ctrl_borderStroke, 5f + ) + switchColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_switchColor, Color.WHITE + ) + directionColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_directionColor, Color.BLUE + ) + directionDiameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_directionDiameter, 15f + ) + type.recycle() + + borderPaint = Paint() + borderPaint.isAntiAlias = true + borderPaint.isDither = true + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = borderStroke + borderPaint.color = borderColor + + backgroundPaint = Paint() + backgroundPaint.isAntiAlias = true + backgroundPaint.isDither = true + backgroundPaint.style = Paint.Style.FILL + backgroundPaint.color = backgroundColor + + centerPaint = Paint() + centerPaint.isAntiAlias = true + centerPaint.isDither = true + centerPaint.style = Paint.Style.FILL + centerPaint.color = borderColor + + switchPaint = Paint() + switchPaint.isAntiAlias = true + switchPaint.isDither = true + switchPaint.style = Paint.Style.STROKE + switchPaint.strokeWidth = borderStroke + switchPaint.strokeCap = Paint.Cap.ROUND + switchPaint.color = switchColor + + leftDirectionPaint = Paint() + leftDirectionPaint.isAntiAlias = true + leftDirectionPaint.isDither = true + leftDirectionPaint.style = Paint.Style.FILL + leftDirectionPaint.color = directionColor + + topDirectionPaint = Paint() + topDirectionPaint.isAntiAlias = true + topDirectionPaint.isDither = true + topDirectionPaint.style = Paint.Style.FILL + topDirectionPaint.color = directionColor + + rightDirectionPaint = Paint() + rightDirectionPaint.isAntiAlias = true + rightDirectionPaint.isDither = true + rightDirectionPaint.style = Paint.Style.FILL + rightDirectionPaint.color = directionColor + + bottomDirectionPaint = Paint() + bottomDirectionPaint.isAntiAlias = true + bottomDirectionPaint.isDither = true + bottomDirectionPaint.style = Paint.Style.FILL + bottomDirectionPaint.color = directionColor + + //设置控件可触摸 + setOnTouchListener(this) + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + canvasCenterX = (w shr 1).toFloat() + canvasCenterY = (h shr 1).toFloat() + + val outerCircleRadius = diameter.toInt() shr 1 //半径 + + centerSwitchRectF = RectF( + (canvasCenterX - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterX + (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY + (outerCircleRadius shr 2) * 0.75).toFloat() + ) + + // 大外圈区域 + outerCircleRectF = RectF( + canvasCenterX - outerCircleRadius - borderStroke, + canvasCenterY - outerCircleRadius - borderStroke, + canvasCenterX + outerCircleRadius + borderStroke, + canvasCenterY + outerCircleRadius + borderStroke + ) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val minimumWidth = suggestedMinimumWidth + val minimumHeight = suggestedMinimumHeight + val width = measureWidth(minimumWidth, widthMeasureSpec) + val height = measureHeight(minimumHeight, heightMeasureSpec) + setMeasuredDimension(width, height) + } + + private fun measureWidth(defaultWidth: Int, measureSpec: Int): Int { + var width = defaultWidth + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> width = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> width = specSize + MeasureSpec.UNSPECIFIED -> width = defaultWidth.coerceAtLeast(specSize) + } + return width + } + + private fun measureHeight(defaultHeight: Int, measureSpec: Int): Int { + var height = defaultHeight + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> height = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> height = specSize + MeasureSpec.UNSPECIFIED -> height = defaultHeight.coerceAtLeast(specSize) + } + return height + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val outerCircleRadius = diameter.toInt() shr 1 //半径 + //背景 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + backgroundPaint + ) + + //外圆圆圈 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + borderPaint + ) + + //内部圆背景 + innerCircleRadius = (directionDiameter.toInt() shl 1).toFloat() + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + innerCircleRadius, + centerPaint + ) + + //周围四个方向小点 + canvas.drawCircle( + (canvasCenterX - outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + leftDirectionPaint + ) + + canvas.drawCircle( + (canvasCenterX + outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + topDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY - outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + rightDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY + outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + bottomDirectionPaint + ) + + //中间开关 + canvas.drawArc( + centerSwitchRectF, -50f, 280f, false, switchPaint + ) + canvas.drawLine( + canvasCenterX, + canvasCenterY - (directionDiameter * 1.2).toFloat(), + canvasCenterX, + canvasCenterY - (directionDiameter * 0.5).toFloat(), + switchPaint + ) + invalidate() + } + + override fun onTouch(v: View, event: MotionEvent): Boolean { + val x: Float = event.x + val y: Float = event.y + when (event.action) { + MotionEvent.ACTION_DOWN -> { + //(x,y)点到(canvasCenterX,canvasCenterY)的距离 + val distance = sqrt( + abs(x - canvasCenterX).pow(2) + abs(y - canvasCenterY).pow(2) + ) +// Log.d(kTag, "onTouch: [$x,$y]") + + // 计算角度正弦值 + val sinAngle = (y - canvasCenterY) / distance +// Log.d(kTag, "sinAngle: $sinAngle") + + val sin = sin(Math.PI / 4) + + // 计算点击的距离,区分点击的是环还是中心位置 + setDefaultValue() + + // 判断 + if (distance > innerCircleRadius) { + if ((x - canvasCenterX) < 0 && abs(sinAngle) < sin) { + leftTurn = true + listener?.onLeftTurn() + } else if ((y - canvasCenterY) > 0 && abs(sinAngle) > sin) { + bottomTurn = true + listener?.onBottomTurn() + } else if ((x - canvasCenterX) > 0 && abs(sinAngle) < sin) { + rightTurn = true + listener?.onRightTurn() + } else if ((y - canvasCenterY) < 0 && abs(sinAngle) > sin) { + topTurn = true + listener?.onTopTurn() + } + } else { + centerTurn = true + listener?.onCenterTurn() + } + } + + MotionEvent.ACTION_UP -> { + if (leftTurn) { + leftTurn = false + listener?.onActionTurnUp(Direction.LEFT) + } else if (topTurn) { + topTurn = false + listener?.onActionTurnUp(Direction.TOP) + } else if (rightTurn) { + rightTurn = false + listener?.onActionTurnUp(Direction.RIGHT) + } else if (bottomTurn) { + bottomTurn = false + listener?.onActionTurnUp(Direction.BOTTOM) + } else { + centerTurn = false + listener?.onActionTurnUp(Direction.CENTER) + } + } + } + return true + } + + //每次手指抬起都重置方向状态 + private fun setDefaultValue() { + leftTurn = false + topTurn = false + rightTurn = false + bottomTurn = false + centerTurn = false + } + + private var listener: OnWheelTouchListener? = null + + interface OnWheelTouchListener { + /** + * 左 + */ + fun onLeftTurn() + + /** + * 上 + */ + fun onTopTurn() + + /** + * 右 + */ + fun onRightTurn() + + /** + * 下 + */ + fun onBottomTurn() + + /** + * 中间 + */ + fun onCenterTurn() + + /** + * 松开 + */ + fun onActionTurnUp(dir: Direction) + } + + fun setOnWheelTouchListener(listener: OnWheelTouchListener?) { + this.listener = listener + } + + enum class Direction { + LEFT, TOP, RIGHT, BOTTOM, CENTER + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt new file mode 100644 index 0000000..5fbc6f0 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt @@ -0,0 +1,386 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.ktd.R +import kotlin.math.abs +import kotlin.math.pow +import kotlin.math.sin +import kotlin.math.sqrt + +class SteeringWheelView constructor(context: Context, attrs: AttributeSet) : + View(context, attrs), View.OnTouchListener { + private val kTag = "SteeringWheelView" + + //画布中心x + private var canvasCenterX = 0f + + //画布中心y + private var canvasCenterY = 0f + + //控件直径 + private val diameter: Float + + //四个方位小点直径 + private val directionDiameter: Float + + //内部圆半径 + private var innerCircleRadius: Float = 0.0f + + //外圆区域 + private lateinit var outerCircleRectF: RectF + + //内部开关区域 + private lateinit var centerSwitchRectF: RectF + + //线条粗细 + private val borderStroke: Float + + //Paint + private val backgroundPaint: Paint + private val borderPaint: Paint + private val centerPaint: Paint + private val leftDirectionPaint: Paint + private val topDirectionPaint: Paint + private val rightDirectionPaint: Paint + private val bottomDirectionPaint: Paint + private val switchPaint: Paint + + //Color + private val directionColor: Int + private val switchColor: Int + + // 各控件使用状态 + private var leftTurn = false + private var topTurn = false + private var rightTurn = false + private var bottomTurn = false + private var centerTurn = false + + init { + val type = context.obtainStyledAttributes(attrs, R.styleable.SteeringWheelView) + diameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_diameter, 200f + ) + val borderColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_borderColor, Color.CYAN + ) + val backgroundColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_backgroundColor, Color.WHITE + ) + borderStroke = type.getDimension( + R.styleable.SteeringWheelView_ctrl_borderStroke, 5f + ) + switchColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_switchColor, Color.WHITE + ) + directionColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_directionColor, Color.BLUE + ) + directionDiameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_directionDiameter, 15f + ) + type.recycle() + + borderPaint = Paint() + borderPaint.isAntiAlias = true + borderPaint.isDither = true + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = borderStroke + borderPaint.color = borderColor + + backgroundPaint = Paint() + backgroundPaint.isAntiAlias = true + backgroundPaint.isDither = true + backgroundPaint.style = Paint.Style.FILL + backgroundPaint.color = backgroundColor + + centerPaint = Paint() + centerPaint.isAntiAlias = true + centerPaint.isDither = true + centerPaint.style = Paint.Style.FILL + centerPaint.color = borderColor + + switchPaint = Paint() + switchPaint.isAntiAlias = true + switchPaint.isDither = true + switchPaint.style = Paint.Style.STROKE + switchPaint.strokeWidth = borderStroke + switchPaint.strokeCap = Paint.Cap.ROUND + switchPaint.color = switchColor + + leftDirectionPaint = Paint() + leftDirectionPaint.isAntiAlias = true + leftDirectionPaint.isDither = true + leftDirectionPaint.style = Paint.Style.FILL + leftDirectionPaint.color = directionColor + + topDirectionPaint = Paint() + topDirectionPaint.isAntiAlias = true + topDirectionPaint.isDither = true + topDirectionPaint.style = Paint.Style.FILL + topDirectionPaint.color = directionColor + + rightDirectionPaint = Paint() + rightDirectionPaint.isAntiAlias = true + rightDirectionPaint.isDither = true + rightDirectionPaint.style = Paint.Style.FILL + rightDirectionPaint.color = directionColor + + bottomDirectionPaint = Paint() + bottomDirectionPaint.isAntiAlias = true + bottomDirectionPaint.isDither = true + bottomDirectionPaint.style = Paint.Style.FILL + bottomDirectionPaint.color = directionColor + + //设置控件可触摸 + setOnTouchListener(this) + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + canvasCenterX = (w shr 1).toFloat() + canvasCenterY = (h shr 1).toFloat() + + val outerCircleRadius = diameter.toInt() shr 1 //半径 + + centerSwitchRectF = RectF( + (canvasCenterX - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterX + (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY + (outerCircleRadius shr 2) * 0.75).toFloat() + ) + + // 大外圈区域 + outerCircleRectF = RectF( + canvasCenterX - outerCircleRadius - borderStroke, + canvasCenterY - outerCircleRadius - borderStroke, + canvasCenterX + outerCircleRadius + borderStroke, + canvasCenterY + outerCircleRadius + borderStroke + ) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val minimumWidth = suggestedMinimumWidth + val minimumHeight = suggestedMinimumHeight + val width = measureWidth(minimumWidth, widthMeasureSpec) + val height = measureHeight(minimumHeight, heightMeasureSpec) + setMeasuredDimension(width, height) + } + + private fun measureWidth(defaultWidth: Int, measureSpec: Int): Int { + var width = defaultWidth + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> width = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> width = specSize + MeasureSpec.UNSPECIFIED -> width = defaultWidth.coerceAtLeast(specSize) + } + return width + } + + private fun measureHeight(defaultHeight: Int, measureSpec: Int): Int { + var height = defaultHeight + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> height = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> height = specSize + MeasureSpec.UNSPECIFIED -> height = defaultHeight.coerceAtLeast(specSize) + } + return height + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val outerCircleRadius = diameter.toInt() shr 1 //半径 + //背景 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + backgroundPaint + ) + + //外圆圆圈 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + borderPaint + ) + + //内部圆背景 + innerCircleRadius = (directionDiameter.toInt() shl 1).toFloat() + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + innerCircleRadius, + centerPaint + ) + + //周围四个方向小点 + canvas.drawCircle( + (canvasCenterX - outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + leftDirectionPaint + ) + + canvas.drawCircle( + (canvasCenterX + outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + topDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY - outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + rightDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY + outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + bottomDirectionPaint + ) + + //中间开关 + canvas.drawArc( + centerSwitchRectF, -50f, 280f, false, switchPaint + ) + canvas.drawLine( + canvasCenterX, + canvasCenterY - (directionDiameter * 1.2).toFloat(), + canvasCenterX, + canvasCenterY - (directionDiameter * 0.5).toFloat(), + switchPaint + ) + invalidate() + } + + override fun onTouch(v: View, event: MotionEvent): Boolean { + val x: Float = event.x + val y: Float = event.y + when (event.action) { + MotionEvent.ACTION_DOWN -> { + //(x,y)点到(canvasCenterX,canvasCenterY)的距离 + val distance = sqrt( + abs(x - canvasCenterX).pow(2) + abs(y - canvasCenterY).pow(2) + ) +// Log.d(kTag, "onTouch: [$x,$y]") + + // 计算角度正弦值 + val sinAngle = (y - canvasCenterY) / distance +// Log.d(kTag, "sinAngle: $sinAngle") + + val sin = sin(Math.PI / 4) + + // 计算点击的距离,区分点击的是环还是中心位置 + setDefaultValue() + + // 判断 + if (distance > innerCircleRadius) { + if ((x - canvasCenterX) < 0 && abs(sinAngle) < sin) { + leftTurn = true + listener?.onLeftTurn() + } else if ((y - canvasCenterY) > 0 && abs(sinAngle) > sin) { + bottomTurn = true + listener?.onBottomTurn() + } else if ((x - canvasCenterX) > 0 && abs(sinAngle) < sin) { + rightTurn = true + listener?.onRightTurn() + } else if ((y - canvasCenterY) < 0 && abs(sinAngle) > sin) { + topTurn = true + listener?.onTopTurn() + } + } else { + centerTurn = true + listener?.onCenterTurn() + } + } + + MotionEvent.ACTION_UP -> { + if (leftTurn) { + leftTurn = false + listener?.onActionTurnUp(Direction.LEFT) + } else if (topTurn) { + topTurn = false + listener?.onActionTurnUp(Direction.TOP) + } else if (rightTurn) { + rightTurn = false + listener?.onActionTurnUp(Direction.RIGHT) + } else if (bottomTurn) { + bottomTurn = false + listener?.onActionTurnUp(Direction.BOTTOM) + } else { + centerTurn = false + listener?.onActionTurnUp(Direction.CENTER) + } + } + } + return true + } + + //每次手指抬起都重置方向状态 + private fun setDefaultValue() { + leftTurn = false + topTurn = false + rightTurn = false + bottomTurn = false + centerTurn = false + } + + private var listener: OnWheelTouchListener? = null + + interface OnWheelTouchListener { + /** + * 左 + */ + fun onLeftTurn() + + /** + * 上 + */ + fun onTopTurn() + + /** + * 右 + */ + fun onRightTurn() + + /** + * 下 + */ + fun onBottomTurn() + + /** + * 中间 + */ + fun onCenterTurn() + + /** + * 松开 + */ + fun onActionTurnUp(dir: Direction) + } + + fun setOnWheelTouchListener(listener: OnWheelTouchListener?) { + this.listener = listener + } + + enum class Direction { + LEFT, TOP, RIGHT, BOTTOM, CENTER + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_task.xml b/app/src/main/res/layout/fragment_task.xml index f91baab..fb35121 100644 --- a/app/src/main/res/layout/fragment_task.xml +++ b/app/src/main/res/layout/fragment_task.xml @@ -91,10 +91,20 @@ + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/dp_10" + android:layout_marginBottom="@dimen/dp_20"> + diff --git a/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt new file mode 100644 index 0000000..5fbc6f0 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt @@ -0,0 +1,386 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.ktd.R +import kotlin.math.abs +import kotlin.math.pow +import kotlin.math.sin +import kotlin.math.sqrt + +class SteeringWheelView constructor(context: Context, attrs: AttributeSet) : + View(context, attrs), View.OnTouchListener { + private val kTag = "SteeringWheelView" + + //画布中心x + private var canvasCenterX = 0f + + //画布中心y + private var canvasCenterY = 0f + + //控件直径 + private val diameter: Float + + //四个方位小点直径 + private val directionDiameter: Float + + //内部圆半径 + private var innerCircleRadius: Float = 0.0f + + //外圆区域 + private lateinit var outerCircleRectF: RectF + + //内部开关区域 + private lateinit var centerSwitchRectF: RectF + + //线条粗细 + private val borderStroke: Float + + //Paint + private val backgroundPaint: Paint + private val borderPaint: Paint + private val centerPaint: Paint + private val leftDirectionPaint: Paint + private val topDirectionPaint: Paint + private val rightDirectionPaint: Paint + private val bottomDirectionPaint: Paint + private val switchPaint: Paint + + //Color + private val directionColor: Int + private val switchColor: Int + + // 各控件使用状态 + private var leftTurn = false + private var topTurn = false + private var rightTurn = false + private var bottomTurn = false + private var centerTurn = false + + init { + val type = context.obtainStyledAttributes(attrs, R.styleable.SteeringWheelView) + diameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_diameter, 200f + ) + val borderColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_borderColor, Color.CYAN + ) + val backgroundColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_backgroundColor, Color.WHITE + ) + borderStroke = type.getDimension( + R.styleable.SteeringWheelView_ctrl_borderStroke, 5f + ) + switchColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_switchColor, Color.WHITE + ) + directionColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_directionColor, Color.BLUE + ) + directionDiameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_directionDiameter, 15f + ) + type.recycle() + + borderPaint = Paint() + borderPaint.isAntiAlias = true + borderPaint.isDither = true + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = borderStroke + borderPaint.color = borderColor + + backgroundPaint = Paint() + backgroundPaint.isAntiAlias = true + backgroundPaint.isDither = true + backgroundPaint.style = Paint.Style.FILL + backgroundPaint.color = backgroundColor + + centerPaint = Paint() + centerPaint.isAntiAlias = true + centerPaint.isDither = true + centerPaint.style = Paint.Style.FILL + centerPaint.color = borderColor + + switchPaint = Paint() + switchPaint.isAntiAlias = true + switchPaint.isDither = true + switchPaint.style = Paint.Style.STROKE + switchPaint.strokeWidth = borderStroke + switchPaint.strokeCap = Paint.Cap.ROUND + switchPaint.color = switchColor + + leftDirectionPaint = Paint() + leftDirectionPaint.isAntiAlias = true + leftDirectionPaint.isDither = true + leftDirectionPaint.style = Paint.Style.FILL + leftDirectionPaint.color = directionColor + + topDirectionPaint = Paint() + topDirectionPaint.isAntiAlias = true + topDirectionPaint.isDither = true + topDirectionPaint.style = Paint.Style.FILL + topDirectionPaint.color = directionColor + + rightDirectionPaint = Paint() + rightDirectionPaint.isAntiAlias = true + rightDirectionPaint.isDither = true + rightDirectionPaint.style = Paint.Style.FILL + rightDirectionPaint.color = directionColor + + bottomDirectionPaint = Paint() + bottomDirectionPaint.isAntiAlias = true + bottomDirectionPaint.isDither = true + bottomDirectionPaint.style = Paint.Style.FILL + bottomDirectionPaint.color = directionColor + + //设置控件可触摸 + setOnTouchListener(this) + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + canvasCenterX = (w shr 1).toFloat() + canvasCenterY = (h shr 1).toFloat() + + val outerCircleRadius = diameter.toInt() shr 1 //半径 + + centerSwitchRectF = RectF( + (canvasCenterX - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterX + (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY + (outerCircleRadius shr 2) * 0.75).toFloat() + ) + + // 大外圈区域 + outerCircleRectF = RectF( + canvasCenterX - outerCircleRadius - borderStroke, + canvasCenterY - outerCircleRadius - borderStroke, + canvasCenterX + outerCircleRadius + borderStroke, + canvasCenterY + outerCircleRadius + borderStroke + ) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val minimumWidth = suggestedMinimumWidth + val minimumHeight = suggestedMinimumHeight + val width = measureWidth(minimumWidth, widthMeasureSpec) + val height = measureHeight(minimumHeight, heightMeasureSpec) + setMeasuredDimension(width, height) + } + + private fun measureWidth(defaultWidth: Int, measureSpec: Int): Int { + var width = defaultWidth + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> width = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> width = specSize + MeasureSpec.UNSPECIFIED -> width = defaultWidth.coerceAtLeast(specSize) + } + return width + } + + private fun measureHeight(defaultHeight: Int, measureSpec: Int): Int { + var height = defaultHeight + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> height = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> height = specSize + MeasureSpec.UNSPECIFIED -> height = defaultHeight.coerceAtLeast(specSize) + } + return height + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val outerCircleRadius = diameter.toInt() shr 1 //半径 + //背景 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + backgroundPaint + ) + + //外圆圆圈 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + borderPaint + ) + + //内部圆背景 + innerCircleRadius = (directionDiameter.toInt() shl 1).toFloat() + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + innerCircleRadius, + centerPaint + ) + + //周围四个方向小点 + canvas.drawCircle( + (canvasCenterX - outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + leftDirectionPaint + ) + + canvas.drawCircle( + (canvasCenterX + outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + topDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY - outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + rightDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY + outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + bottomDirectionPaint + ) + + //中间开关 + canvas.drawArc( + centerSwitchRectF, -50f, 280f, false, switchPaint + ) + canvas.drawLine( + canvasCenterX, + canvasCenterY - (directionDiameter * 1.2).toFloat(), + canvasCenterX, + canvasCenterY - (directionDiameter * 0.5).toFloat(), + switchPaint + ) + invalidate() + } + + override fun onTouch(v: View, event: MotionEvent): Boolean { + val x: Float = event.x + val y: Float = event.y + when (event.action) { + MotionEvent.ACTION_DOWN -> { + //(x,y)点到(canvasCenterX,canvasCenterY)的距离 + val distance = sqrt( + abs(x - canvasCenterX).pow(2) + abs(y - canvasCenterY).pow(2) + ) +// Log.d(kTag, "onTouch: [$x,$y]") + + // 计算角度正弦值 + val sinAngle = (y - canvasCenterY) / distance +// Log.d(kTag, "sinAngle: $sinAngle") + + val sin = sin(Math.PI / 4) + + // 计算点击的距离,区分点击的是环还是中心位置 + setDefaultValue() + + // 判断 + if (distance > innerCircleRadius) { + if ((x - canvasCenterX) < 0 && abs(sinAngle) < sin) { + leftTurn = true + listener?.onLeftTurn() + } else if ((y - canvasCenterY) > 0 && abs(sinAngle) > sin) { + bottomTurn = true + listener?.onBottomTurn() + } else if ((x - canvasCenterX) > 0 && abs(sinAngle) < sin) { + rightTurn = true + listener?.onRightTurn() + } else if ((y - canvasCenterY) < 0 && abs(sinAngle) > sin) { + topTurn = true + listener?.onTopTurn() + } + } else { + centerTurn = true + listener?.onCenterTurn() + } + } + + MotionEvent.ACTION_UP -> { + if (leftTurn) { + leftTurn = false + listener?.onActionTurnUp(Direction.LEFT) + } else if (topTurn) { + topTurn = false + listener?.onActionTurnUp(Direction.TOP) + } else if (rightTurn) { + rightTurn = false + listener?.onActionTurnUp(Direction.RIGHT) + } else if (bottomTurn) { + bottomTurn = false + listener?.onActionTurnUp(Direction.BOTTOM) + } else { + centerTurn = false + listener?.onActionTurnUp(Direction.CENTER) + } + } + } + return true + } + + //每次手指抬起都重置方向状态 + private fun setDefaultValue() { + leftTurn = false + topTurn = false + rightTurn = false + bottomTurn = false + centerTurn = false + } + + private var listener: OnWheelTouchListener? = null + + interface OnWheelTouchListener { + /** + * 左 + */ + fun onLeftTurn() + + /** + * 上 + */ + fun onTopTurn() + + /** + * 右 + */ + fun onRightTurn() + + /** + * 下 + */ + fun onBottomTurn() + + /** + * 中间 + */ + fun onCenterTurn() + + /** + * 松开 + */ + fun onActionTurnUp(dir: Direction) + } + + fun setOnWheelTouchListener(listener: OnWheelTouchListener?) { + this.listener = listener + } + + enum class Direction { + LEFT, TOP, RIGHT, BOTTOM, CENTER + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_task.xml b/app/src/main/res/layout/fragment_task.xml index f91baab..fb35121 100644 --- a/app/src/main/res/layout/fragment_task.xml +++ b/app/src/main/res/layout/fragment_task.xml @@ -91,10 +91,20 @@ + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/dp_10" + android:layout_marginBottom="@dimen/dp_20"> + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..3b1e812 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt new file mode 100644 index 0000000..5fbc6f0 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/SteeringWheelView.kt @@ -0,0 +1,386 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.ktd.R +import kotlin.math.abs +import kotlin.math.pow +import kotlin.math.sin +import kotlin.math.sqrt + +class SteeringWheelView constructor(context: Context, attrs: AttributeSet) : + View(context, attrs), View.OnTouchListener { + private val kTag = "SteeringWheelView" + + //画布中心x + private var canvasCenterX = 0f + + //画布中心y + private var canvasCenterY = 0f + + //控件直径 + private val diameter: Float + + //四个方位小点直径 + private val directionDiameter: Float + + //内部圆半径 + private var innerCircleRadius: Float = 0.0f + + //外圆区域 + private lateinit var outerCircleRectF: RectF + + //内部开关区域 + private lateinit var centerSwitchRectF: RectF + + //线条粗细 + private val borderStroke: Float + + //Paint + private val backgroundPaint: Paint + private val borderPaint: Paint + private val centerPaint: Paint + private val leftDirectionPaint: Paint + private val topDirectionPaint: Paint + private val rightDirectionPaint: Paint + private val bottomDirectionPaint: Paint + private val switchPaint: Paint + + //Color + private val directionColor: Int + private val switchColor: Int + + // 各控件使用状态 + private var leftTurn = false + private var topTurn = false + private var rightTurn = false + private var bottomTurn = false + private var centerTurn = false + + init { + val type = context.obtainStyledAttributes(attrs, R.styleable.SteeringWheelView) + diameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_diameter, 200f + ) + val borderColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_borderColor, Color.CYAN + ) + val backgroundColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_backgroundColor, Color.WHITE + ) + borderStroke = type.getDimension( + R.styleable.SteeringWheelView_ctrl_borderStroke, 5f + ) + switchColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_switchColor, Color.WHITE + ) + directionColor = type.getColor( + R.styleable.SteeringWheelView_ctrl_directionColor, Color.BLUE + ) + directionDiameter = type.getDimension( + R.styleable.SteeringWheelView_ctrl_directionDiameter, 15f + ) + type.recycle() + + borderPaint = Paint() + borderPaint.isAntiAlias = true + borderPaint.isDither = true + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = borderStroke + borderPaint.color = borderColor + + backgroundPaint = Paint() + backgroundPaint.isAntiAlias = true + backgroundPaint.isDither = true + backgroundPaint.style = Paint.Style.FILL + backgroundPaint.color = backgroundColor + + centerPaint = Paint() + centerPaint.isAntiAlias = true + centerPaint.isDither = true + centerPaint.style = Paint.Style.FILL + centerPaint.color = borderColor + + switchPaint = Paint() + switchPaint.isAntiAlias = true + switchPaint.isDither = true + switchPaint.style = Paint.Style.STROKE + switchPaint.strokeWidth = borderStroke + switchPaint.strokeCap = Paint.Cap.ROUND + switchPaint.color = switchColor + + leftDirectionPaint = Paint() + leftDirectionPaint.isAntiAlias = true + leftDirectionPaint.isDither = true + leftDirectionPaint.style = Paint.Style.FILL + leftDirectionPaint.color = directionColor + + topDirectionPaint = Paint() + topDirectionPaint.isAntiAlias = true + topDirectionPaint.isDither = true + topDirectionPaint.style = Paint.Style.FILL + topDirectionPaint.color = directionColor + + rightDirectionPaint = Paint() + rightDirectionPaint.isAntiAlias = true + rightDirectionPaint.isDither = true + rightDirectionPaint.style = Paint.Style.FILL + rightDirectionPaint.color = directionColor + + bottomDirectionPaint = Paint() + bottomDirectionPaint.isAntiAlias = true + bottomDirectionPaint.isDither = true + bottomDirectionPaint.style = Paint.Style.FILL + bottomDirectionPaint.color = directionColor + + //设置控件可触摸 + setOnTouchListener(this) + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + canvasCenterX = (w shr 1).toFloat() + canvasCenterY = (h shr 1).toFloat() + + val outerCircleRadius = diameter.toInt() shr 1 //半径 + + centerSwitchRectF = RectF( + (canvasCenterX - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY - (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterX + (outerCircleRadius shr 2) * 0.75).toFloat(), + (canvasCenterY + (outerCircleRadius shr 2) * 0.75).toFloat() + ) + + // 大外圈区域 + outerCircleRectF = RectF( + canvasCenterX - outerCircleRadius - borderStroke, + canvasCenterY - outerCircleRadius - borderStroke, + canvasCenterX + outerCircleRadius + borderStroke, + canvasCenterY + outerCircleRadius + borderStroke + ) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val minimumWidth = suggestedMinimumWidth + val minimumHeight = suggestedMinimumHeight + val width = measureWidth(minimumWidth, widthMeasureSpec) + val height = measureHeight(minimumHeight, heightMeasureSpec) + setMeasuredDimension(width, height) + } + + private fun measureWidth(defaultWidth: Int, measureSpec: Int): Int { + var width = defaultWidth + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> width = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> width = specSize + MeasureSpec.UNSPECIFIED -> width = defaultWidth.coerceAtLeast(specSize) + } + return width + } + + private fun measureHeight(defaultHeight: Int, measureSpec: Int): Int { + var height = defaultHeight + val specMode = MeasureSpec.getMode(measureSpec) + val specSize = MeasureSpec.getSize(measureSpec) + when (specMode) { + MeasureSpec.AT_MOST -> height = (diameter + borderStroke * 2).toInt() + MeasureSpec.EXACTLY -> height = specSize + MeasureSpec.UNSPECIFIED -> height = defaultHeight.coerceAtLeast(specSize) + } + return height + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val outerCircleRadius = diameter.toInt() shr 1 //半径 + //背景 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + backgroundPaint + ) + + //外圆圆圈 + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + outerCircleRadius.toFloat(), + borderPaint + ) + + //内部圆背景 + innerCircleRadius = (directionDiameter.toInt() shl 1).toFloat() + canvas.drawCircle( + canvasCenterX, + canvasCenterY, + innerCircleRadius, + centerPaint + ) + + //周围四个方向小点 + canvas.drawCircle( + (canvasCenterX - outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + leftDirectionPaint + ) + + canvas.drawCircle( + (canvasCenterX + outerCircleRadius * 0.75).toFloat(), + canvasCenterY, + (directionDiameter.toInt() shr 1).toFloat(), + topDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY - outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + rightDirectionPaint + ) + + canvas.drawCircle( + canvasCenterX, + (canvasCenterY + outerCircleRadius * 0.75).toFloat(), + (directionDiameter.toInt() shr 1).toFloat(), + bottomDirectionPaint + ) + + //中间开关 + canvas.drawArc( + centerSwitchRectF, -50f, 280f, false, switchPaint + ) + canvas.drawLine( + canvasCenterX, + canvasCenterY - (directionDiameter * 1.2).toFloat(), + canvasCenterX, + canvasCenterY - (directionDiameter * 0.5).toFloat(), + switchPaint + ) + invalidate() + } + + override fun onTouch(v: View, event: MotionEvent): Boolean { + val x: Float = event.x + val y: Float = event.y + when (event.action) { + MotionEvent.ACTION_DOWN -> { + //(x,y)点到(canvasCenterX,canvasCenterY)的距离 + val distance = sqrt( + abs(x - canvasCenterX).pow(2) + abs(y - canvasCenterY).pow(2) + ) +// Log.d(kTag, "onTouch: [$x,$y]") + + // 计算角度正弦值 + val sinAngle = (y - canvasCenterY) / distance +// Log.d(kTag, "sinAngle: $sinAngle") + + val sin = sin(Math.PI / 4) + + // 计算点击的距离,区分点击的是环还是中心位置 + setDefaultValue() + + // 判断 + if (distance > innerCircleRadius) { + if ((x - canvasCenterX) < 0 && abs(sinAngle) < sin) { + leftTurn = true + listener?.onLeftTurn() + } else if ((y - canvasCenterY) > 0 && abs(sinAngle) > sin) { + bottomTurn = true + listener?.onBottomTurn() + } else if ((x - canvasCenterX) > 0 && abs(sinAngle) < sin) { + rightTurn = true + listener?.onRightTurn() + } else if ((y - canvasCenterY) < 0 && abs(sinAngle) > sin) { + topTurn = true + listener?.onTopTurn() + } + } else { + centerTurn = true + listener?.onCenterTurn() + } + } + + MotionEvent.ACTION_UP -> { + if (leftTurn) { + leftTurn = false + listener?.onActionTurnUp(Direction.LEFT) + } else if (topTurn) { + topTurn = false + listener?.onActionTurnUp(Direction.TOP) + } else if (rightTurn) { + rightTurn = false + listener?.onActionTurnUp(Direction.RIGHT) + } else if (bottomTurn) { + bottomTurn = false + listener?.onActionTurnUp(Direction.BOTTOM) + } else { + centerTurn = false + listener?.onActionTurnUp(Direction.CENTER) + } + } + } + return true + } + + //每次手指抬起都重置方向状态 + private fun setDefaultValue() { + leftTurn = false + topTurn = false + rightTurn = false + bottomTurn = false + centerTurn = false + } + + private var listener: OnWheelTouchListener? = null + + interface OnWheelTouchListener { + /** + * 左 + */ + fun onLeftTurn() + + /** + * 上 + */ + fun onTopTurn() + + /** + * 右 + */ + fun onRightTurn() + + /** + * 下 + */ + fun onBottomTurn() + + /** + * 中间 + */ + fun onCenterTurn() + + /** + * 松开 + */ + fun onActionTurnUp(dir: Direction) + } + + fun setOnWheelTouchListener(listener: OnWheelTouchListener?) { + this.listener = listener + } + + enum class Direction { + LEFT, TOP, RIGHT, BOTTOM, CENTER + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_task.xml b/app/src/main/res/layout/fragment_task.xml index f91baab..fb35121 100644 --- a/app/src/main/res/layout/fragment_task.xml +++ b/app/src/main/res/layout/fragment_task.xml @@ -91,10 +91,20 @@ + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/dp_10" + android:layout_marginBottom="@dimen/dp_20"> + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..3b1e812 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3b3e7e6..d54010f 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,8 @@ dependencies { classpath 'com.android.tools.build:gradle:3.6.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files