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"


            }
        })

  

 完成 

  

 

posted on 2022-08-14 20:03  巫山老妖  阅读(126)  评论(1编辑  收藏  举报