亮度调节条的实现
/**
* Copyright:MicoLauncher
* Author: liyang
* Date:2021/8/23 2:08 下午
* Desc:
*/
class VerticalSeekView(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attributeSet, defStyleAttr, defStyleRes) {
constructor(context: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0) : this(
context,
attributeSet,
defStyleAttr,
0
)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context) : this(context, null)
....
....
....
companion object {
const val TAG = "VerticalSeekView:"
@JvmStatic
@BindingAdapter("data")
fun bindData(
view: VerticalSeekView,
data: DeviceInfoWrapper
) {
data.deviceInfo.apply {
when (data.modelInfo.deviceType) {
DeviceTypeDic.DT_LIGHT_LIGHT.deviceType, DeviceTypeDic.DT_LIGHT_MIRROR.deviceType, DeviceTypeDic.DT_LIGHT_NIGHT_LIGHT.deviceType -> {
view.showIcon = true
view.deviceTypeIcon = if (currentStatus) {
R.drawable.sh_ic_light_icon_on
} else {
R.drawable.sh_ic_light_icon_off
}
}
DeviceTypeDic.DT_ENV_HEATER.deviceType -> {
view.showIcon = true
view.deviceTypeIcon = if (currentStatus) {
R.drawable.sh_ic_heater_temperature_on
} else {
R.drawable.sh_ic_heater_temperature_off
}
view.foregroundColor = ContextCompat.getColor(
view.context,
if (currentStatus) R.color.sh_miot_heater_foreground_color else R.color.sh_miot_heater_foreground_color_off
)
}
}
}
}
}
....
....
....
var volume: Double = 0.0
set(value) {
if (value in 0.0..1.0 && field != value) {
field = value
volumeChangeListener?.onVolumeChanged(value)
refresh()
}
}
var volumeChangeListener: OnVolumeChangeListener? = null
private val minTouchSlop by lazy {
ViewConfiguration.get(context).scaledTouchSlop
}
init {
val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.VerticalSeekView)
radius = typedArray.getDimensionPixelOffset(R.styleable.VerticalSeekView_radius, 0).let {
var num = it
if (it < 0) {
num = 0
}
num
}
deviceTypeIcon = typedArray.getResourceId(
R.styleable.VerticalSeekView_deviceTypeIcon,
R.drawable.sh_ic_iot_lamp
)
bitmap = ContextCompat.getDrawable(context, deviceTypeIcon)?.toBitmap()
underlyingColor = typedArray.getColor(
R.styleable.VerticalSeekView_underlyingLayerColor,
context.getColor(R.color.sh_miot_seek_view_background_color)
)
foregroundColor = typedArray.getColor(
R.styleable.VerticalSeekView_foregroundLayerColor,
context.getColor(R.color.sh_miot_seek_view_foreground_color)
)
volume = (typedArray.getFloat(R.styleable.VerticalSeekView_volume, 0.0f)
.takeIf {
it >= 0.0f || it <= 100.0f
} ?: 0.0f)
.toDouble()
startColor = typedArray.getColor(R.styleable.VerticalSeekView_startColor, foregroundColor)
endColor = typedArray.getColor(R.styleable.VerticalSeekView_endColor, foregroundColor)
canDrag = typedArray.getBoolean(R.styleable.VerticalSeekView_canDrag, true)
paint.isAntiAlias = true
paint.isDither = true
typedArray.recycle()
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (!canDrag) return super.onTouchEvent(event)
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
downTimestamp = System.currentTimeMillis()
L.smarthome.d("$TAG onTouchEvent ACTION_DOWN downX=$startX,downY=$startY,minTouchSlop=$minTouchSlop")
volumeChangeListener?.onVolumeChangedBegin(volume)
startVolume = volume
return true
}
MotionEvent.ACTION_MOVE -> {
currentX = event.x
currentY = event.y
val offsetX = startX - currentX
val offsetY = startY - currentY
if (abs(offsetY) > abs(offsetX)) {//纵向滑动时,请求RecyclerView不拦截事件
parent.requestDisallowInterceptTouchEvent(true)
if (abs(offsetY) > minTouchSlop) {
if (!isFistMoveAction) {
volume = (offsetY / height).plus(startVolume).let {
var newVolume = it
if (it >= 1) {
newVolume = 1.0
}
if (it <= 0) {
newVolume = 0.0
}
newVolume
}
isVolumeChange = true
L.smarthome.d("$TAG onTouchEvent ACTION_MOVE setVolume=$volume")
return true
}
isFistMoveAction = false
}
}
lastX = currentX
lastY = currentY
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
currentX = 0f
currentY = 0f
lastY = 0f
lastX = 0f
isFistMoveAction = true
parent.requestDisallowInterceptTouchEvent(false)
L.smarthome.d("$TAG ${if (event.action == MotionEvent.ACTION_UP) "ACTION_UP" else "ACTION_CANCEL"} requestDisallowInterceptTouchEvent:false")
val isLongClick =
(System.currentTimeMillis() - downTimestamp) >= ViewConfiguration.getLongPressTimeout()
if (event.action == MotionEvent.ACTION_UP && !isVolumeChange && !isLongClick) {
performClick()
}
if (isVolumeChange) {
isVolumeChange = false
}
volumeChangeListener?.onVolumeChangedEnd(isVolumeChange, volume)
}
}
return super.onTouchEvent(event)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.apply {
layerId = saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null)
//画底层背景色
onDrawUnderlyingLayer(this)
//画上层填充色
onDrawForegroundLayer(this)
//画小横条
onDrawHandler(this)
//画底部的icon
onDrawIcon(this)
restoreToCount(layerId)
}
}
private fun onDrawIcon(canvas: Canvas) {
if (!showIcon)return
bitmap?.apply {
L.smarthome.d("$TAG,width=$width,height=$height")
canvas.drawBitmap(this, Rect(0, 0, width, height), destRect, null)
}
}
private fun onDrawHandler(canvas: Canvas) {
if (!canDrag) return
rectF.left = ((width - 40.toPx()) / 2).toFloat()
rectF.top = (canvas.height - canvas.height * volume + 8.toPx()).toFloat()
rectF.right = (width / 2 + 17).toFloat()
rectF.bottom = rectF.top.minus(4)
paint.color = ContextCompat.getColor(context, R.color.black_12_transparent)
canvas.drawRoundRect(rectF, 1.5f, 1.5f, paint)
}
private fun onDrawUnderlyingLayer(canvas: Canvas) {
paint.style = Paint.Style.FILL
paint.color = underlyingColor
rectF.left = 0f
rectF.top = 0f
rectF.right = canvas.width.toFloat()
rectF.bottom = canvas.height.toFloat()
canvas.drawRoundRect(rectF, radius.toFloat(), radius.toFloat(), paint)
}
private fun onDrawForegroundLayer(canvas: Canvas) {
paint.style = Paint.Style.FILL
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)
val top = (canvas.height - canvas.height * volume).toFloat()
val bottom = canvas.height.toFloat()
if (startColor != foregroundColor || endColor != foregroundColor) {
val gradient = LinearGradient(
(width / 2).toFloat(),
top,
(width / 2).toFloat(),
bottom,
startColor,
endColor,
Shader.TileMode.CLAMP
)
paint.shader = gradient
} else {
paint.color = foregroundColor
}
rectF.left = 0f
rectF.top = top
rectF.right = canvas.width.toFloat()
rectF.bottom = bottom
canvas.drawRect(rectF, paint)
paint.xfermode = null
paint.shader = null
}
private fun refresh() {
if (Looper.myLooper() != Looper.getMainLooper()) {
postInvalidate()
} else {
invalidate()
}
}
}
interface OnVolumeChangeListener {
fun onVolumeChangedBegin(volume: Double)
fun onVolumeChanged(volume: Double)
fun onVolumeChangedEnd(flag: Boolean, volume: Double)
}
色温调节条的实现
package com.xiaomi.smarthome.ui.widgets
/**
* Copyright:MicoLauncher
* Author: liyang
* Date:2021/8/23 2:08 下午
* Desc:
*/
class VerticalSeekView2(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attributeSet, defStyleAttr, defStyleRes) {
constructor(context: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0) : this(
context,
attributeSet,
defStyleAttr,
0
)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context) : this(context, null)
......
......
var volume: Double = 0.0
set(value) {
if (value in 0.0..1.0 && field != value) {
field = value
refresh()
volumeChangeListener?.onVolumeChanged(value)
}
}
var volumeChangeListener: OnVolumeChangeListener? = null
......
......
......
init {
val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.VerticalSeekView2)
radius = typedArray.getDimensionPixelOffset(R.styleable.VerticalSeekView2_radius, 0).let {
var num = it
if (it < 0) {
num = 0
}
num
}
startColor = typedArray.getColor(R.styleable.VerticalSeekView2_startColor, Color.WHITE)
endColor = typedArray.getColor(R.styleable.VerticalSeekView2_endColor, Color.BLUE)
volume = (typedArray.getFloat(R.styleable.VerticalSeekView2_volume, 0.0f)
.takeIf {
it >= 0.0f || it <= 100.0f
} ?: 0.0f)
.toDouble()
paint.isAntiAlias = true
paint.isDither = true
typedArray.recycle()
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
if (event.action == MotionEvent.ACTION_DOWN) {
parent.requestDisallowInterceptTouchEvent(true)
}
currentX = event.y
currentY = event.x
val offsetX = lastX - currentX
val offsetY = lastY - currentY
if (abs(offsetX) < abs(offsetY)) {//纵向滑动时,请求RecyclerView不拦截事件
parent.requestDisallowInterceptTouchEvent(true)
}
volume = ((height - currentX) / height).toDouble().let {
var newVolume = it
if (it >= 1) {
newVolume = 1.0
}
if (it <= 0) {
newVolume = 0.0
}
newVolume
}
lastX = currentX
lastY = currentY
L.smarthome.d("$TAG onTouchEvent currentX=$currentX,currentY=$currentY")
return true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
currentX = 0f
currentY = 0f
lastY = 0f
lastX = 0f
parent.requestDisallowInterceptTouchEvent(false)
L.smarthome.d("${TAG}requestDisallowInterceptTouchEvent:false")
}
}
return super.onTouchEvent(event)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.apply {
layerId = saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null)
onDrawUnderlyingLayer(this)
onDrawHandler(this)
restoreToCount(layerId)
}
}
private fun onDrawHandler(canvas: Canvas) {
rectF.left = 0f
rectF.top = (canvas.height - canvas.height * volume).toFloat().let {
var newVal=it
if (it>=canvas.height+48.toPx()){
newVal=(canvas.height+48.toPx()).toFloat()
}else if (it<=48.toPx()){
newVal=48.toPx().toFloat()
}
newVal
}
rectF.right = width.toFloat()
rectF.bottom = rectF.top.minus(48.toPx())
paint.color = Color.WHITE
canvas.drawRoundRect(rectF, 26f, 26f, paint)
}
private fun onDrawUnderlyingLayer(canvas: Canvas) {
paint.style = Paint.Style.FILL
val gradient = LinearGradient((width / 2).toFloat(), 0f, (width / 2).toFloat(), height.toFloat(),startColor,endColor,Shader.TileMode.REPEAT)
paint.shader=gradient
rectF.left = 0f
rectF.top = 0f
rectF.right = canvas.width.toFloat()
rectF.bottom = canvas.height.toFloat()
canvas.drawRoundRect(rectF, radius.toFloat(), radius.toFloat(), paint)
paint.shader=null
}
private fun refresh() {
if (Looper.myLooper() != Looper.getMainLooper()) {
postInvalidate()
} else {
invalidate()
}
}
}