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" } })
完成