Android自定义矩形View中任意拖动圆点获取色温值(RectangleWheel)
如图所示:矩形色温条中,拖动圆点获取当前色温值

1、自定义属性 res->values下创建attrs.xml文件
<declare-styleable name="RectangleWheel">
<!-- 矩形宽高 -->
<attr name="rectangleWheel_witch" format="dimension" />
<attr name="rectangleWheel_height" format="dimension" />
<!-- 可滑动小球的半径 -->
<attr name="rectangleWheel_center_radius" format="dimension" />
<!-- 可滑动小球的颜色 -->
<attr name="rectangleWheel_center_color" format="color" />
</declare-styleable>
2、自定义View
package com.example.mycircle
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import com.example.mycircle.utils.MyColorUtils
import kotlin.math.acos
import kotlin.math.sqrt
class RectangleWheel : View {
//private var bigCircle = 0 //外圆半径
private var rectWitch = 0 //矩形宽
private var rectHeight = 0 //矩形高
//矩形圆角
private val radiusXY = 40f
private var rudeRadius //可移动小球的半径
= 0
private var centerColor //可移动小球的颜色
= 0
private var bitmapBack //背景图片
: Bitmap? = null
private var mPaint //背景画笔
: Paint? = null
private var mCenterPaint //可移动小球背景
: Paint? = null
private var centerPoint //中心位置
: Point? = null
private var mRockPosition //小球当前位置
: Point? = null
private var listener //小球移动监听
: OnColorChangedListener? = null
private var length //小球到中心位置的距离
= 0
var colorStr = ""
constructor(context: Context?) : super(context) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(attrs)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
fun setOnColorChangedListener(listener: OnColorChangedListener?) {
this.listener = listener
}
/**
* @describe 初始化操作
* @param attrs
*/
private fun init(attrs: AttributeSet?) {
val types = context.obtainStyledAttributes(attrs, R.styleable.RectangleWheel)
try {
//外圆半径
rectWitch = types.getDimensionPixelOffset(R.styleable.RectangleWheel_rectangleWheel_witch, 100)
rectHeight = types.getDimensionPixelOffset(R.styleable.RectangleWheel_rectangleWheel_height, 200)
//可移动小球半径
rudeRadius = types.getDimensionPixelOffset(R.styleable.RectangleWheel_rectangleWheel_center_radius, 10)
//可移动小球的颜色
centerColor = types.getColor(R.styleable.RectangleWheel_rectangleWheel_center_color, Color.WHITE)
} finally {
types.recycle() //TypeArray用完需要recycle
}
Log.i("打印宽高:","Witch = $rectWitch ,Height = $rectHeight")
//中心位置坐标
centerPoint = Point(rectWitch/2, rectHeight/2)
mRockPosition = Point(centerPoint!!)
//初始化背景画笔和可移动小球的画笔
mPaint = Paint()
mPaint!!.isAntiAlias = true
mPaint!!.isDither = true
mPaint!!.strokeCap = Paint.Cap.ROUND
mCenterPaint = Paint()
mCenterPaint!!.color = centerColor
bitmapBack = createColorBitmap(rectWitch, rectHeight)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//画背景图
canvas.drawBitmap(bitmapBack!!, 0f, 0f, null)
//画中心小球
canvas.drawCircle(mRockPosition!!.x.toFloat(), mRockPosition!!.y.toFloat(), rudeRadius.toFloat(), mCenterPaint!!)
}
private fun createColorBitmap(width: Int, height: Int): Bitmap {
//创建 Bitmap
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val colors = IntArray(2)
colors[0] = setColorTempToColor("6500K")
colors[1] = setColorTempToColor("2700K")
//colors[1] = Color.parseColor("#FFB660")
//colors[2] = Color.parseColor("#FFA757")
//线性变色
val linearGradient = LinearGradient((width/2).toFloat(),height.toFloat(),(width/2).toFloat(),0F,colors,null, Shader.TileMode.CLAMP)
//new float[]{},中的数据表示相对位置,将150,50,150,300,划分10个单位,.3,.6,.9表示它的绝对位置。300到400,将直接画出rgb(0,232,210)
mPaint!!.shader = linearGradient
val canvas = Canvas(bitmap)
//画圆角矩形
canvas.drawRoundRect(0F,0F,rectWitch.toFloat(),rectHeight.toFloat(),radiusXY,radiusXY,mPaint!!)
return bitmap
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val w = event
Log.i("打印触摸位置:","x = ${event.x} ,y= ${event.y} ,rudeRadius = $rudeRadius ,Witch = $rectWitch ,Height = $rectHeight")
when (event.action) {
MotionEvent.ACTION_BUTTON_PRESS -> {
// length = getLength(event.x, event.y, centerPoint!!.x, centerPoint!!.y)
// if (length <= bigCircle - rudeRadius) {
// mRockPosition!![event.x.toInt()] = event.y.toInt()
// } else {
// mRockPosition = getBorderPoint(centerPoint, Point(event.x.toInt(), event.y.toInt()), bigCircle - rudeRadius)
// }
var x = 0
var y = 0
if (event.x < rudeRadius){
x = rudeRadius
}else if (event.x > (rectWitch - rudeRadius)){
x = (rectWitch - rudeRadius)
}else{
x = event.x.toInt()
}
if (event.y < rudeRadius){
y = rudeRadius
}else if (event.y > (rectHeight - rudeRadius)){
y = (rectHeight - rudeRadius)
}else{
y = event.y.toInt()
}
//设置圆点位置
mRockPosition!!.set(x,y)
//获取像素点
val pixel = bitmapBack!!.getPixel(mRockPosition!!.x, mRockPosition!!.y)
val r = Color.red(pixel)
val g = Color.green(pixel)
val b = Color.blue(pixel)
val a = Color.alpha(pixel)
//十六进制的颜色字符串
colorStr = "#" + toBrowserHexValue(r) + toBrowserHexValue(g) + toBrowserHexValue(b)
if (listener != null) {
listener!!.onColorPick(a, r, g, b,0)
}
}
MotionEvent.ACTION_DOWN -> {
// length = getLength(event.x, event.y, centerPoint!!.x, centerPoint!!.y)
// if (length > bigCircle - rudeRadius) {
// return true
// }
if (event.x < 0 || event.x > rectWitch || event.y < 0 || event.y > rectHeight){
return true
}
}
MotionEvent.ACTION_MOVE -> {
// length = getLength(event.x, event.y, centerPoint!!.x, centerPoint!!.y)
// if (length <= bigCircle - rudeRadius) {
// mRockPosition!![event.x.toInt()] = event.y.toInt()
// } else {
// mRockPosition = getBorderPoint(centerPoint, Point(event.x.toInt(), event.y.toInt()), bigCircle - rudeRadius)
// }
var x = 0
var y = 0
if (event.x < rudeRadius){
x = rudeRadius
}else if (event.x > (rectWitch - rudeRadius)){
x = (rectWitch - rudeRadius)
}else{
x = event.x.toInt()
}
if (event.y < rudeRadius){
y = rudeRadius
}else if (event.y > (rectHeight - rudeRadius)){
y = (rectHeight - rudeRadius)
}else{
y = event.y.toInt()
}
mRockPosition!!.set(x,y)
}
MotionEvent.ACTION_UP -> {
// val pixel = bitmapBack!!.getPixel(mRockPosition!!.x, mRockPosition!!.y)
// val r = Color.red(pixel)
// val g = Color.green(pixel)
// val b = Color.blue(pixel)
// val a = Color.alpha(pixel)
//
// //十六进制的颜色字符串
// colorStr = "#" + toBrowserHexValue(r) + toBrowserHexValue(g) + toBrowserHexValue(b)
// if (listener != null) {
// listener!!.onColorPick(a, r, g, b)
// }
val pixel = bitmapBack!!.getPixel(mRockPosition!!.x, mRockPosition!!.y)
val r = Color.red(pixel)
val g = Color.green(pixel)
val b = Color.blue(pixel)
val a = Color.alpha(pixel)
//十六进制的颜色字符串
colorStr = "#" + toBrowserHexValue(r) + toBrowserHexValue(g) + toBrowserHexValue(b)
Log.i("打印数据:","当前Y轴值:${mRockPosition!!.y},可移动小球直径:${rudeRadius*2}")
var l = rectHeight - (rudeRadius*2) -2
var p = mRockPosition!!.y - rudeRadius -1
Log.i("打印数据:","总长度:${l} ,当前位置:${p} ,百分比:${100-((100F/l.toFloat()).toDouble()*p).toInt()}")
var location = 100-((100F/l.toFloat()).toDouble()*p).toInt()
if (listener != null) {
listener!!.onColorPick(a, r, g, b,location)
}
}
else -> {
}
}
rGB
invalidate() //更新画布
return true
}
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
parent.requestDisallowInterceptTouchEvent(true)
return super.dispatchTouchEvent(event)
}
/*
*转16进制数
*/
private fun toBrowserHexValue(number: Int): String {
val builder = StringBuilder(Integer.toHexString(number and 0xff))
while (builder.length < 2) {
builder.append("0")
}
return builder.toString().toUpperCase()
}//十六进制的颜色字符串
/*
*像素转RGB
*/
private val rGB: Unit
get() {
val pixel = bitmapBack!!.getPixel(mRockPosition!!.x, mRockPosition!!.y)
val r = Color.red(pixel)
val g = Color.green(pixel)
val b = Color.blue(pixel)
val a = Color.alpha(pixel)
//十六进制的颜色字符串
colorStr = "#" + toBrowserHexValue(r) + toBrowserHexValue(g) + toBrowserHexValue(b)
if (listener != null) {
listener!!.onColorChange(a, r, g, b)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//视图大小设置为直径
//setMeasuredDimension(bigCircle * 2, bigCircle * 2)
setMeasuredDimension(rectWitch, rectHeight)
}
//颜色发生变化的回调接口
interface OnColorChangedListener {
fun onColorChange(a: Int, r: Int, g: Int, b: Int)
fun onColorPick(a: Int, r: Int, g: Int, b: Int, value: Int)
}
companion object {
//计算两点之间的位置
private fun getLength(x1: Float, y1: Float, x2: Int, y2: Int): Int {
return Math.sqrt(Math.pow((x1 - x2).toDouble(), 2.0) + Math.pow((y1 - y2).toDouble(), 2.0)).toInt()
}
//当触摸点超出圆的范围的时候,设置小球边缘位置
private fun getBorderPoint(a: Point?, b: Point, cutRadius: Int): Point {
val radian = getRadian(a, b)
return Point(a!!.x + (cutRadius * Math.cos(radian.toDouble())).toInt(), a.x + (cutRadius * Math.sin(radian.toDouble())).toInt())
}
//触摸点与中心点之间直线与水平方向的夹角角度
fun getRadian(a: Point?, b: Point): Float {
val lenA = (b.x - a!!.x).toFloat()
val lenB = (b.y - a.y).toFloat()
val lenC = sqrt((lenA * lenA + lenB * lenB).toDouble()).toFloat()
var ang = acos((lenA / lenC).toDouble()).toFloat()
ang *= if (b.y < a.y) -1 else 1
return ang
}
}
fun setColorTempToColor(color: String): Int{
var zhi = color.replace("K", "").toDouble()
//Log.i("打印色温值:","$zhi")
var rgb = IntArray(3)
rgb = MyColorUtils.getRgbFromTemperature(zhi,false)
//Log.i("打印色温值转颜色:","R=${rgb[0]} ,G=${rgb[1]} ,B=${rgb[2]}")
var blue = rgb[2]
val c = Color.argb(255,rgb[0], rgb[1], rgb[2])
//Log.i("打印选择的值3","c=${c}")
//返回颜色
return c
}
}
3、布局文件
<com.example.mycircle.RectangleWheel
android:id="@+id/rectWheel"
android:layout_width="100dp"
android:layout_height="300dp"
app:rectangleWheel_witch="100dp"
app:rectangleWheel_height="300dp"
app:rectangleWheel_center_radius="10dp"
android:layout_marginTop="50dp"
app:layout_constraintTop_toTop="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"></com.example.mycircle.RectangleWheel>
<TextView
android:id="@+id/tv_color_temp2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="色温: 2700K"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/rectWheel"/>
4、activity
//色温盘
binding.rectWheel.setOnColorChangedListener(object: RectangleWheel.OnColorChangedListener {
override fun onColorChange(a: Int, r: Int, g: Int, b: Int) {
}
override fun onColorPick(a: Int, r: Int, g: Int, b: Int,value: Int) {
// 执行设置色温命令
Log.i("打印选择的颜色:","a=$a , r= $r ,g= $g ,b= $b ,value= $value")
// 将 rgb 转换成 hsv,s 即为饱和度 色调(H),饱和度(S),明度(V)
val c = Color.argb(a, r, g, b)
val hsv = FloatArray(3)
Color.colorToHSV(c, hsv)
Log.i("打印选择的颜色:","hsv[0]=${hsv[0]} , hsv[1]=${hsv[1]} ,hsv[2]=${hsv[2]}")
Log.i("打印选择的颜色:","色调(H)=${hsv[0]/360*254} , 饱和度(S)=${hsv[1]* 254} ,hsv[2]=${hsv[2]}")
//hsv[0].toInt()
// val rgb = MyColorUtils.setRGBToTemperature(r,g,b)
// Log.i("RGB转色温值后:","$rgb")
// val colorTempValue = "${(rgb).toInt()}K"
// binding.tvColorTemp.text = "色温: $colorTempValue"
val colorTempValue = "${2700+(38*value)}K"
Log.i("打印选择的颜色:","色温值:$colorTempValue")
binding.tvColorTemp2.text = "色温: $colorTempValue"
}
})
完成
浙公网安备 33010602011771号