1.0.12: PTT-кнопка в стиле Zello (градиент, тень, глянец, ободок)
This commit is contained in:
@@ -3,15 +3,18 @@ package ru.ua1zbe.svxremote.ui
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.Shader
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import ru.ua1zbe.svxremote.R
|
||||
|
||||
/**
|
||||
* Большая круглая кнопка PTT. Удерживание — передача.
|
||||
* Большая круглая кнопка PTT в стиле Zello: градиентная заливка с глянцем,
|
||||
* мягкая тень (плавающая кнопка) и тонкий ободок.
|
||||
* Состояния: idle (синяя), нажата (красная), отключена (серая).
|
||||
*/
|
||||
class PttButton @JvmOverloads constructor(
|
||||
@@ -19,8 +22,11 @@ class PttButton @JvmOverloads constructor(
|
||||
attrs: AttributeSet? = null
|
||||
) : View(context, attrs) {
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val ringPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val glossPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val micPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val clip = Path()
|
||||
|
||||
private var radius = 0f
|
||||
private var centerX = 0f
|
||||
@@ -31,7 +37,6 @@ class PttButton @JvmOverloads constructor(
|
||||
set(value) {
|
||||
field = value
|
||||
if (!value) setPressed(false)
|
||||
refreshColors()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
@@ -50,22 +55,28 @@ class PttButton @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
init {
|
||||
fillPaint.style = Paint.Style.FILL
|
||||
fillPaint.setShadowLayer(dp(8).toFloat(), 0f, dp(4).toFloat(), Color.argb(80, 0, 0, 0))
|
||||
ringPaint.style = Paint.Style.STROKE
|
||||
ringPaint.strokeWidth = dp(3).toFloat()
|
||||
ringPaint.color = Color.parseColor("#3D444D")
|
||||
paint.style = Paint.Style.FILL
|
||||
refreshColors()
|
||||
ringPaint.strokeWidth = dp(2).toFloat()
|
||||
glossPaint.style = Paint.Style.FILL
|
||||
micPaint.style = Paint.Style.FILL
|
||||
micPaint.color = Color.WHITE
|
||||
isClickable = true
|
||||
isFocusable = true
|
||||
}
|
||||
|
||||
private fun refreshColors() {
|
||||
paint.color = when {
|
||||
!enabled2 -> Color.parseColor("#484F58")
|
||||
isPressed -> Color.parseColor("#F85149")
|
||||
else -> Color.parseColor("#1F6FEB")
|
||||
/** (верхний цвет, нижний цвет) градиента заливки. */
|
||||
private fun fillColors(): Pair<Int, Int> = when {
|
||||
!enabled2 -> 0x565E68 to 0x3C424B
|
||||
isPressed -> 0xFF8A80 to 0xDB3A30
|
||||
else -> 0x4A9BFF to 0x1561D6
|
||||
}
|
||||
invalidate()
|
||||
|
||||
private fun ringColor(): Int = when {
|
||||
!enabled2 -> 0x6B727B
|
||||
isPressed -> 0xFFA79F
|
||||
else -> 0x6FB0FF
|
||||
}
|
||||
|
||||
private fun dp(v: Int): Int = (v * resources.displayMetrics.density).toInt()
|
||||
@@ -74,32 +85,51 @@ class PttButton @JvmOverloads constructor(
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
centerX = w / 2f
|
||||
centerY = h / 2f
|
||||
radius = minOf(w, h) / 2f - dp(16)
|
||||
refreshColors()
|
||||
radius = minOf(w, h) / 2f - dp(18)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
val r = radius / (1f + if (isPressed) 0.06f else 0f)
|
||||
canvas.drawCircle(centerX, centerY, r, paint)
|
||||
canvas.drawCircle(centerX, centerY, r, ringPaint)
|
||||
val cx = centerX
|
||||
val cy = centerY
|
||||
val (top, bottom) = fillColors()
|
||||
|
||||
// Иконка микрофона (как icons8: капсула + чашка + ножка + основание)
|
||||
val micPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
micPaint.color = Color.WHITE
|
||||
micPaint.style = Paint.Style.FILL
|
||||
// Заливка с вертикальным градиентом (глянец)
|
||||
fillPaint.shader = LinearGradient(
|
||||
cx, cy - r, cx, cy + r, top, bottom, Shader.TileMode.CLAMP
|
||||
)
|
||||
canvas.drawCircle(cx, cy, r, fillPaint)
|
||||
|
||||
val x = centerX
|
||||
val y = centerY
|
||||
val s = r * 0.5f
|
||||
// Тонкий ободок по краю
|
||||
ringPaint.color = ringColor()
|
||||
canvas.drawCircle(cx, cy, r - dp(1).toFloat(), ringPaint)
|
||||
|
||||
// Верхний глянец (белое затухание сверху)
|
||||
glossPaint.shader = LinearGradient(
|
||||
cx, cy - r, cx, cy + r * 0.18f,
|
||||
Color.argb(70, 255, 255, 255), Color.TRANSPARENT, Shader.TileMode.CLAMP
|
||||
)
|
||||
clip.reset()
|
||||
clip.addCircle(cx, cy, r, Path.Direction.CW)
|
||||
canvas.save()
|
||||
canvas.clipPath(clip)
|
||||
canvas.drawRect(cx - r, cy - r, cx + r, cy + r, glossPaint)
|
||||
canvas.restore()
|
||||
glossPaint.shader = null
|
||||
|
||||
drawMic(canvas, cx, cy, r * 0.5f)
|
||||
}
|
||||
|
||||
/** Иконка микрофона (капсула + держатель + ножка + основание). */
|
||||
private fun drawMic(canvas: Canvas, x: Float, y: Float, s: Float) {
|
||||
// корпус-капсула
|
||||
val halfW = s * 0.34f
|
||||
canvas.drawRoundRect(
|
||||
x - halfW, y - s * 0.85f, x + halfW, y + s * 0.10f,
|
||||
halfW, halfW, micPaint
|
||||
)
|
||||
// чашка-держатель (U-образная, заполненная)
|
||||
// чашка-держатель (U-образная)
|
||||
val cup = Path()
|
||||
cup.fillType = Path.FillType.EVEN_ODD
|
||||
cup.moveTo(x - s * 0.52f, y + s * 0.06f)
|
||||
|
||||
Reference in New Issue
Block a user