SubwayTooter-Android-App/colorpicker/src/main/java/com/jrummyapps/android/colorpicker/ColorPickerView.kt

838 lines
29 KiB
Kotlin

/*
* Copyright (C) 2017 JRummy Apps Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jrummyapps.android.colorpicker
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.graphics.Paint.Align
import android.graphics.Shader.TileMode
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import androidx.annotation.ColorInt
import kotlin.math.max
import kotlin.math.min
/**
* Displays a color picker to the user and allow them to select a color. A slider for the alpha channel is also available.
* Enable it by setting setAlphaSliderVisible(boolean) to true.
*/
class ColorPickerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
) : View(context, attrs, defStyle) {
companion object {
private const val DEFAULT_BORDER_COLOR = -0x919192
private const val DEFAULT_SLIDER_COLOR = -0x424243
private const val HUE_PANEL_WDITH_DP = 30
private const val ALPHA_PANEL_HEIGH_DP = 20
private const val PANEL_SPACING_DP = 10
private const val CIRCLE_TRACKER_RADIUS_DP = 5
private const val SLIDER_TRACKER_SIZE_DP = 4
private const val SLIDER_TRACKER_OFFSET_DP = 2
/**
* The width in pixels of the border
* surrounding all color panels.
*/
private const val BORDER_WIDTH_PX = 1
}
interface OnColorChangedListener {
fun onColorChanged(newColor: Int)
}
private class BitmapCache(
var canvas: Canvas? = null,
var bitmap: Bitmap? = null,
var value: Float = 0f,
)
/**
* The width in px of the hue panel.
*/
private val huePanelWidthPx =
DrawingUtils.dpToPx(context, HUE_PANEL_WDITH_DP.toFloat())
/**
* The height in px of the alpha panel
*/
private val alphaPanelHeightPx =
DrawingUtils.dpToPx(context, ALPHA_PANEL_HEIGH_DP.toFloat())
/**
* The distance in px between the different
* color panels.
*/
private val panelSpacingPx =
DrawingUtils.dpToPx(context, PANEL_SPACING_DP.toFloat())
/**
* The radius in px of the color palette tracker circle.
*/
private val circleTrackerRadiusPx =
DrawingUtils.dpToPx(context, CIRCLE_TRACKER_RADIUS_DP.toFloat())
/**
* The px which the tracker of the hue or alpha panel
* will extend outside of its bounds.
*/
private val sliderTrackerOffsetPx =
DrawingUtils.dpToPx(context, SLIDER_TRACKER_OFFSET_DP.toFloat())
/**
* Height of slider tracker on hue panel,
* width of slider on alpha panel.
*/
private val sliderTrackerSizePx =
DrawingUtils.dpToPx(context, SLIDER_TRACKER_SIZE_DP.toFloat())
/**
* the current value of the text that will be shown in the alpha slider.
* null to disable text.
*/
@Suppress("MemberVisibilityCanBePrivate")
var alphaSliderText: String? = null
set(value) {
if (field != value) {
field = value
invalidate()
}
}
// the color the view should show.
var color: Int
get() = Color.HSVToColor(alpha, floatArrayOf(hue, sat, bri))
set(color) {
setColor(color, false)
}
@ColorInt
var sliderTrackerColor = DEFAULT_SLIDER_COLOR
set(value) {
field = value
hueAlphaTrackerPaint.color = value
invalidate()
}
@ColorInt
var borderColor = DEFAULT_BORDER_COLOR
set(value) {
if (field != value) {
field = value
invalidate()
}
}
private val satValPaint = Paint()
private val satValTrackerPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = DrawingUtils.dpToPx(context, 2f).toFloat()
isAntiAlias = true
}
private val alphaPaint = Paint()
private val alphaTextPaint = Paint().apply {
color = -0xe3e3e4
textSize = DrawingUtils.dpToPx(context, 14f).toFloat()
isAntiAlias = true
textAlign = Align.CENTER
isFakeBoldText = true
}
private val hueAlphaTrackerPaint = Paint().apply {
color = sliderTrackerColor
style = Paint.Style.STROKE
strokeWidth = DrawingUtils.dpToPx(context, 2f).toFloat()
isAntiAlias = true
}
private val borderPaint = Paint()
private var valShader: Shader? = null
private var satShader: Shader? = null
private var alphaShader: Shader? = null
/*
* We cache a bitmap of the sat/val panel which is expensive to draw each time.
* We can reuse it when the user is sliding the circle picker as long as the hue isn't changed.
*/
private var satValBackgroundCache: BitmapCache? = null
/* We cache the hue background to since its also very expensive now. */
private var hueBackgroundCache: BitmapCache? = null
/* Current values */
private var alpha = 0xff
private var hue = 360f
private var sat = 0f
private var bri = 0f
private var showAlphaPanel = false
/**
* Minimum required padding. The offset from the
* edge we must have or else the finger tracker will
* get clipped when it's drawn outside of the view.
*/
private val mRequiredPadding =
context.resources.getDimensionPixelSize(R.dimen.cpv_required_padding)
/**
* The Rect in which we are allowed to draw.
* Trackers can extend outside slightly,
* due to the required padding we have set.
*/
private var drawingRect: Rect? = null
private var satValRect: Rect? = null
private var hueRect: Rect? = null
private var alphaRect: Rect? = null
private var startTouchPoint: Point? = null
private var alphaPatternDrawable: AlphaPatternDrawable? = null
/**
* OnColorChangedListener to get notified when the color selected by the user has changed.
*/
var onColorChangedListener: OnColorChangedListener? = null
public override fun onSaveInstanceState(): Parcelable {
val state = Bundle()
state.putParcelable("instanceState", super.onSaveInstanceState())
state.putInt("alpha", alpha)
state.putFloat("hue", hue)
state.putFloat("sat", sat)
state.putFloat("val", bri)
state.putBoolean("show_alpha", showAlphaPanel)
state.putString("alpha_text", alphaSliderText)
return state
}
public override fun onRestoreInstanceState(state: Parcelable) {
if (state is Bundle) {
alpha = state.getInt("alpha")
hue = state.getFloat("hue")
sat = state.getFloat("sat")
bri = state.getFloat("val")
showAlphaPanel = state.getBoolean("show_alpha")
alphaSliderText = state.getString("alpha_text")
super.onRestoreInstanceState(state.getParcelableCompat("instanceState"))
} else {
super.onRestoreInstanceState(state)
}
}
init {
//Load those if set in xml resource file.
var a = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerView)
showAlphaPanel = a.getBoolean(R.styleable.ColorPickerView_cpv_alphaChannelVisible, false)
alphaSliderText = a.getString(R.styleable.ColorPickerView_cpv_alphaChannelText)
sliderTrackerColor = a.getColor(R.styleable.ColorPickerView_cpv_sliderColor, -0x424243)
borderColor = a.getColor(R.styleable.ColorPickerView_cpv_borderColor, -0x919192)
a.recycle()
// If no specific border/slider color has been
// set we take the default secondary text color
// as border/slider color. Thus it will adopt
// to theme changes automatically.
val value = TypedValue()
a = context.obtainStyledAttributes(
value.data,
intArrayOf(android.R.attr.textColorSecondary)
)
if (borderColor == DEFAULT_BORDER_COLOR) {
borderColor = a.getColor(0, DEFAULT_BORDER_COLOR)
}
if (sliderTrackerColor == DEFAULT_SLIDER_COLOR) {
sliderTrackerColor = a.getColor(0, DEFAULT_SLIDER_COLOR)
}
a.recycle()
//Needed for receiving trackball motion events.
isFocusable = true
isFocusableInTouchMode = true
}
override fun onDraw(canvas: Canvas) {
val drawingRect = this.drawingRect
if (drawingRect == null || drawingRect.width() <= 0 || drawingRect.height() <= 0) {
return
}
drawSatValPanel(canvas)
drawHuePanel(canvas)
drawAlphaPanel(canvas)
}
private fun drawSatValPanel(canvas: Canvas) {
val rect = this.satValRect ?: return
val drawingRect = this.drawingRect ?: return
if (BORDER_WIDTH_PX > 0) {
borderPaint.color = borderColor
canvas.drawRect(
drawingRect.left.toFloat(),
drawingRect.top.toFloat(),
(rect.right + BORDER_WIDTH_PX).toFloat(),
(rect.bottom + BORDER_WIDTH_PX).toFloat(), borderPaint
)
}
if (valShader == null) {
//Black gradient has either not been created or the view has been resized.
valShader = LinearGradient(
rect.left.toFloat(),
rect.top.toFloat(),
rect.left.toFloat(),
rect.bottom.toFloat(),
-0x1,
-0x1000000,
TileMode.CLAMP
)
}
//If the hue has changed we need to recreate the cache.
if (satValBackgroundCache == null || satValBackgroundCache!!.value != hue) {
if (satValBackgroundCache == null) {
satValBackgroundCache = BitmapCache()
}
//We create our bitmap in the cache if it doesn't exist.
if (satValBackgroundCache!!.bitmap == null) {
satValBackgroundCache!!.bitmap = Bitmap
.createBitmap(rect.width(), rect.height(), Bitmap.Config.ARGB_8888)
}
//We create the canvas once so we can draw on our bitmap and the hold on to it.
if (satValBackgroundCache!!.canvas == null) {
satValBackgroundCache!!.canvas = Canvas(satValBackgroundCache!!.bitmap!!)
}
val rgb = Color.HSVToColor(floatArrayOf(hue, 1f, 1f))
satShader = LinearGradient(
rect.left.toFloat(),
rect.top.toFloat(),
rect.right.toFloat(),
rect.top.toFloat(),
-0x1,
rgb,
TileMode.CLAMP
)
satValPaint.shader = ComposeShader(
valShader!!,
satShader!!,
PorterDuff.Mode.MULTIPLY
)
// Finally we draw on our canvas, the result will be
// stored in our bitmap which is already in the cache.
// Since this is drawn on a canvas not rendered on
// screen it will automatically not be using the
// hardware acceleration. And this was the code that
// wasn't supported by hardware acceleration which mean
// there is no need to turn it of anymore. The rest of
// the view will still be hw accelerated.
satValBackgroundCache!!.canvas!!.drawRect(
0f, 0f,
satValBackgroundCache!!.bitmap!!.width.toFloat(),
satValBackgroundCache!!.bitmap!!.height.toFloat(),
satValPaint
)
//We set the hue value in our cache to which hue it was drawn with,
//then we know that if it hasn't changed we can reuse our cached bitmap.
satValBackgroundCache!!.value = hue
}
// We draw our bitmap from the cached, if the hue has changed
// then it was just recreated otherwise the old one will be used.
canvas.drawBitmap(satValBackgroundCache!!.bitmap!!, null, rect, null)
val p = satValToPoint(sat, bri)
satValTrackerPaint.color = -0x1000000
canvas.drawCircle(
p.x.toFloat(), p.y.toFloat(), (circleTrackerRadiusPx - DrawingUtils.dpToPx(
context, 1f
)).toFloat(), satValTrackerPaint
)
satValTrackerPaint.color = -0x222223
canvas.drawCircle(
p.x.toFloat(),
p.y.toFloat(),
circleTrackerRadiusPx.toFloat(),
satValTrackerPaint
)
}
private fun drawHuePanel(canvas: Canvas) {
val rect = hueRect
if (BORDER_WIDTH_PX > 0) {
borderPaint.color = borderColor
canvas.drawRect(
(rect!!.left - BORDER_WIDTH_PX).toFloat(), (
rect.top - BORDER_WIDTH_PX).toFloat(), (
rect.right + BORDER_WIDTH_PX).toFloat(), (
rect.bottom + BORDER_WIDTH_PX).toFloat(),
borderPaint
)
}
if (hueBackgroundCache == null) {
val hueBackgroundCache = BitmapCache()
.also { this.hueBackgroundCache = it }
Bitmap.createBitmap(rect!!.width(), rect.height(), Bitmap.Config.ARGB_8888)
.also {
hueBackgroundCache.bitmap = it
hueBackgroundCache.canvas = Canvas(it)
}
val hueColors = IntArray((rect.height() + 0.5f).toInt())
// Generate array of all colors, will be drawn as individual lines.
var h = 360f
for (i in hueColors.indices) {
hueColors[i] = Color.HSVToColor(floatArrayOf(h, 1f, 1f))
h -= 360f / hueColors.size
}
// Time to draw the hue color gradient,
// its drawn as individual lines which
// will be quite many when the resolution is high
// and/or the panel is large.
val linePaint = Paint()
linePaint.strokeWidth = 0f
for (i in hueColors.indices) {
linePaint.color = hueColors[i]
hueBackgroundCache.canvas?.drawLine(
0f,
i.toFloat(),
hueBackgroundCache.bitmap?.width?.toFloat() ?: 0f,
i.toFloat(),
linePaint
)
}
}
canvas.drawBitmap(hueBackgroundCache!!.bitmap!!, null, rect!!, null)
val p = hueToPoint(hue)
val r = RectF()
r.left = (rect.left - sliderTrackerOffsetPx).toFloat()
r.right = (rect.right + sliderTrackerOffsetPx).toFloat()
r.top = p.y - sliderTrackerSizePx / 2f
r.bottom = p.y + sliderTrackerSizePx / 2f
canvas.drawRoundRect(r, 2f, 2f, hueAlphaTrackerPaint)
}
private fun drawAlphaPanel(canvas: Canvas) {
if (!showAlphaPanel) return
val rect = this.alphaRect ?: return
val alphaPatternDrawable = this.alphaPatternDrawable ?: return
/*
* Will be drawn with hw acceleration, very fast.
* Also the AlphaPatternDrawable is backed by a bitmap
* generated only once if the size does not change.
*/
if (BORDER_WIDTH_PX > 0) {
borderPaint.color = borderColor
canvas.drawRect(
(rect.left - BORDER_WIDTH_PX).toFloat(), (
rect.top - BORDER_WIDTH_PX).toFloat(), (
rect.right + BORDER_WIDTH_PX).toFloat(), (
rect.bottom + BORDER_WIDTH_PX).toFloat(),
borderPaint
)
}
alphaPatternDrawable.draw(canvas)
val hsv = floatArrayOf(hue, sat, bri)
val color = Color.HSVToColor(hsv)
val acolor = Color.HSVToColor(0, hsv)
alphaShader = LinearGradient(
rect.left.toFloat(),
rect.top.toFloat(),
rect.right.toFloat(),
rect.top.toFloat(),
color, acolor, TileMode.CLAMP
)
alphaPaint.shader = alphaShader
canvas.drawRect(rect, alphaPaint)
alphaSliderText
?.takeIf { it.isNotEmpty() }
?.let {
canvas.drawText(
it,
rect.centerX().toFloat(),
(rect.centerY() + DrawingUtils.dpToPx(
context, 4f
)).toFloat(),
alphaTextPaint
)
}
val p = alphaToPoint(alpha)
val r = RectF()
r.left = p.x - sliderTrackerSizePx / 2f
r.right = p.x + sliderTrackerSizePx / 2f
r.top = (rect.top - sliderTrackerOffsetPx).toFloat()
r.bottom = (rect.bottom + sliderTrackerOffsetPx).toFloat()
canvas.drawRoundRect(r, 2f, 2f, hueAlphaTrackerPaint)
}
private fun hueToPoint(hue: Float): Point {
val rect = hueRect
val height = rect!!.height().toFloat()
val p = Point()
p.y = (height - hue * height / 360f + rect.top).toInt()
p.x = rect.left
return p
}
private fun satValToPoint(sat: Float, inValue: Float): Point {
val rect = satValRect!!
val height = rect.height().toFloat()
val width = rect.width().toFloat()
val p = Point()
p.x = (sat * width + rect.left).toInt()
p.y = ((1f - inValue) * height + rect.top).toInt()
return p
}
private fun alphaToPoint(alpha: Int): Point {
val rect = alphaRect
val width = rect!!.width().toFloat()
val p = Point()
p.x = (width - alpha * width / 0xff + rect.left).toInt()
p.y = rect.top
return p
}
private fun pointToSatVal(xArg: Float, yArg: Float): FloatArray {
var x = xArg
var y = yArg
val rect = satValRect
val result = FloatArray(2)
val width = rect!!.width().toFloat()
val height = rect.height().toFloat()
x = when {
x < rect.left -> 0f
x > rect.right -> width
else -> x - rect.left
}
y = when {
y < rect.top -> 0f
y > rect.bottom -> height
else -> y - rect.top
}
result[0] = 1f / width * x
result[1] = 1f - 1f / height * y
return result
}
private fun pointToHue(yArg: Float): Float {
var y = yArg
val rect = hueRect
val height = rect!!.height().toFloat()
y = when {
y < rect.top -> 0f
y > rect.bottom -> height
else -> y - rect.top
}
return 360f - y * 360f / height
}
private fun pointToAlpha(xArg: Int): Int {
var x = xArg
val rect = alphaRect
val width = rect!!.width()
x = when {
x < rect.left -> 0
x > rect.right -> width
else -> x - rect.left
}
return 0xff - x * 0xff / width
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
try {
this.parent.requestDisallowInterceptTouchEvent(true)
} catch (ignored: Throwable) {
}
var update = false
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startTouchPoint = Point(event.x.toInt(), event.y.toInt())
update = moveTrackersIfNeeded(event)
}
MotionEvent.ACTION_MOVE -> update = moveTrackersIfNeeded(event)
MotionEvent.ACTION_UP -> {
startTouchPoint = null
update = moveTrackersIfNeeded(event)
}
}
if (update) {
onColorChangedListener?.onColorChanged(
Color.HSVToColor(
alpha,
floatArrayOf(hue, sat, bri)
)
)
invalidate()
return true
}
return super.onTouchEvent(event)
}
private fun moveTrackersIfNeeded(event: MotionEvent): Boolean {
val startTouchPoint = this.startTouchPoint ?: return false
val startX = startTouchPoint.x
val startY = startTouchPoint.y
return when {
hueRect?.contains(startX, startY) == true -> {
hue = pointToHue(event.y)
true
}
satValRect?.contains(startX, startY) == true -> {
val result = pointToSatVal(event.x, event.y)
sat = result[0]
bri = result[1]
true
}
alphaRect?.contains(startX, startY) == true -> {
alpha = pointToAlpha(event.x.toInt())
true
}
else -> false
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val finalWidth: Int
val finalHeight: Int
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val widthAllowed = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight
val heightAllowed = MeasureSpec.getSize(heightMeasureSpec) - paddingBottom - paddingTop
if (widthMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.EXACTLY) {
//A exact value has been set in either direction, we need to stay within this size.
if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
//The with has been specified exactly, we need to adopt the height to fit.
var h = widthAllowed - panelSpacingPx - huePanelWidthPx
if (showAlphaPanel) {
h += panelSpacingPx + alphaPanelHeightPx
}
//We can't fit the view in this container, set the size to whatever was allowed.
finalHeight = min(h, heightAllowed)
finalWidth = widthAllowed
} else if (widthMode != MeasureSpec.EXACTLY) {
//The height has been specified exactly, we need to stay within this height and adopt the width.
var w = heightAllowed + panelSpacingPx + huePanelWidthPx
if (showAlphaPanel) {
w -= panelSpacingPx + alphaPanelHeightPx
}
//we can't fit within this container, set the size to whatever was allowed.
finalWidth = min(w, widthAllowed)
finalHeight = heightAllowed
} else {
//If we get here the dev has set the width and height to exact sizes. For example match_parent or 300dp.
//This will mean that the sat/val panel will not be square but it doesn't matter. It will work anyway.
//In all other senarios our goal is to make that panel square.
//We set the sizes to exactly what we were told.
finalWidth = widthAllowed
finalHeight = heightAllowed
}
} else {
//If no exact size has been set we try to make our view as big as possible
//within the allowed space.
//Calculate the needed width to layout using max allowed height.
var widthNeeded = heightAllowed + panelSpacingPx + huePanelWidthPx
//Calculate the needed height to layout using max allowed width.
var heightNeeded = widthAllowed - panelSpacingPx - huePanelWidthPx
if (showAlphaPanel) {
widthNeeded -= panelSpacingPx + alphaPanelHeightPx
heightNeeded += panelSpacingPx + alphaPanelHeightPx
}
val widthOk = widthNeeded <= widthAllowed
val heightOk = heightNeeded <= heightAllowed
when {
widthOk && heightOk -> {
finalWidth = widthAllowed
finalHeight = heightNeeded
}
widthOk -> {
finalHeight = heightAllowed
finalWidth = widthNeeded
}
heightOk -> {
finalHeight = heightNeeded
finalWidth = widthAllowed
}
else -> {
finalHeight = heightAllowed
finalWidth = widthAllowed
}
}
}
setMeasuredDimension(
finalWidth + paddingLeft + paddingRight,
finalHeight + paddingTop + paddingBottom
)
}
override fun getPaddingTop(): Int =
max(super.getPaddingTop(), mRequiredPadding)
override fun getPaddingBottom(): Int =
max(super.getPaddingBottom(), mRequiredPadding)
override fun getPaddingLeft(): Int =
max(super.getPaddingLeft(), mRequiredPadding)
override fun getPaddingRight(): Int =
max(super.getPaddingRight(), mRequiredPadding)
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val drawingRect = Rect().also { this.drawingRect = it }
drawingRect.left = paddingLeft
drawingRect.right = w - paddingRight
drawingRect.top = paddingTop
drawingRect.bottom = h - paddingBottom
//The need to be recreated because they depend on the size of the view.
valShader = null
satShader = null
alphaShader = null
// Clear those bitmap caches since the size may have changed.
satValBackgroundCache = null
hueBackgroundCache = null
setUpSatValRect()
setUpHueRect()
setUpAlphaRect()
}
private fun setUpSatValRect() {
//Calculate the size for the big color rectangle.
val dRect = drawingRect!!
val left = dRect.left + BORDER_WIDTH_PX
val top = dRect.top + BORDER_WIDTH_PX
var bottom = dRect.bottom - BORDER_WIDTH_PX
val right = dRect.right - BORDER_WIDTH_PX - panelSpacingPx - huePanelWidthPx
if (showAlphaPanel) {
bottom -= alphaPanelHeightPx + panelSpacingPx
}
satValRect = Rect(left, top, right, bottom)
}
private fun setUpHueRect() {
//Calculate the size for the hue slider on the left.
val dRect = drawingRect!!
val left = dRect.right - huePanelWidthPx + BORDER_WIDTH_PX
val top = dRect.top + BORDER_WIDTH_PX
val bottom = dRect.bottom - BORDER_WIDTH_PX -
if (showAlphaPanel) panelSpacingPx + alphaPanelHeightPx else 0
val right = dRect.right - BORDER_WIDTH_PX
hueRect = Rect(left, top, right, bottom)
}
private fun setUpAlphaRect() {
if (!showAlphaPanel) return
val dRect = drawingRect ?: return
val left = dRect.left + BORDER_WIDTH_PX
val top = dRect.bottom - alphaPanelHeightPx + BORDER_WIDTH_PX
val bottom = dRect.bottom - BORDER_WIDTH_PX
val right = dRect.right - BORDER_WIDTH_PX
val alphaRect = Rect(left, top, right, bottom)
.also { this.alphaRect = it }
alphaPatternDrawable = AlphaPatternDrawable(DrawingUtils.dpToPx(context, 4f))
.apply {
setBounds(
alphaRect.left,
alphaRect.top,
alphaRect.right,
alphaRect.bottom
)
}
}
/**
* Set the color this view should show.
*
* @param color The color that should be selected. #argb
* @param callback If you want to get a callback to your OnColorChangedListener.
*/
fun setColor(color: Int, callback: Boolean) {
val alpha = Color.alpha(color)
val red = Color.red(color)
val blue = Color.blue(color)
val green = Color.green(color)
val hsv = FloatArray(3)
Color.RGBToHSV(red, green, blue, hsv)
this.alpha = alpha
hue = hsv[0]
sat = hsv[1]
bri = hsv[2]
if (callback) {
onColorChangedListener
?.onColorChanged(Color.HSVToColor(this.alpha, floatArrayOf(hue, sat, bri)))
}
invalidate()
}
/**
* Set if the user is allowed to adjust the alpha panel. Default is false.
* If it is set to false no alpha will be set.
*
* @param visible `true` to show the alpha slider
*/
fun setAlphaSliderVisible(visible: Boolean) {
if (showAlphaPanel != visible) {
showAlphaPanel = visible
/*
* Force recreation.
*/
valShader = null
satShader = null
alphaShader = null
hueBackgroundCache = null
satValBackgroundCache = null
requestLayout()
}
}
/**
* Set the text that should be shown in the
* alpha slider. Set to null to disable text.
*
* @param res string resource id.
*/
fun setAlphaSliderText(res: Int) {
alphaSliderText = context.getString(res)
}
}