随笔 - 245  文章 - 0  评论 - 11  阅读 - 50万

Android自定义矩形View中任意拖动圆点获取色温值(RectangleWheel)

如图所示:矩形色温条中,拖动圆点获取当前色温值

 

 1、自定义属性 res->values下创建attrs.xml文件

1
2
3
4
5
6
7
8
9
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
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、布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//色温盘
        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"
 
 
            }
        })

  

 完成 

  

 

posted on   巫山老妖  阅读(133)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示