About the use of Android color picker (1)

The content of this article is to develop a color selector from 0-1, first the renderings
renderings

Relevant source code files will be attached at the end of the article.

The effect is shown in the figure. This article will describe how to customize such a control. The code is at the end of the article.

think

(1) Implementation method
(2) Data format passed to the business layer
(3) Implementation details

The above three points need us to think about.
(1) The implementation method chosen here is recommended to be custom view, because this kind of layout is not supported by ordinary controls.
(2) For the business layer, what should be done is to pass the color data into our control, and then after the control selects the color, the color data is returned to the business layer intact. Here, the String format is selected as the color value. Pass, like "#ffffff".
(3) In implementation, how to draw arcs correctly using canvas, how to calculate real angles, data conversion of quadrant intervals, conversion of coordinate data, and coordinate calculation of click events are all issues worth thinking about.

Okay, after thinking about it, start working.

Implementation process

##### (1) Defining classes
To make similar controls, you need to have an outline of thinking, that is, what methods does my control library expose to the outside world, and how do I need to process the incoming data from the outside internally. Therefore, you must first define the abstract method:

(1) Control abstract class:

 interface LibColorCirclePickerApi {

    fun setColor(color: Array<Array<String>>)

    fun setLibColorCirclePickerListener(listener: LibColorCirclePickerListener)

    fun removeLibColorCirclePickerListener()
}

(2) External monitoring class:

interface LibColorCirclePickerListener {

    fun selInfo(info: LibColorSelInfo)

}

Once these methods are defined, they can be implemented through inheritance.

#####(2) Customize view to realize drawing color value

This involves knowledge of canvas drawing, please understand it in advance.
As shown in the figure, the drawing is divided into two levels. One is the central circle, and the other is the color block.

Before drawing, the following variables need to be determined to facilitate subsequent drawing:
(1) The center coordinate point of the drawing, x, y point. Here take x/2, y/2.
(2) Drawing radius, here the shortest side is generally taken as the drawing radius.
(3) Draw the radius of the central circle and multiply it with (2) by a certain ratio to get the radius of the central circle.
(4) Except for the central circle, the remaining radius length will be divided evenly and the radius length of each layer of color blocks will be calculated.
(5) Initialize relevant intermediate variable values ​​(such as color block coordinate set)

After initializing the relevant variables, start drawing:

Draw center circle

For the center circle, just call the circle drawing method of canvas directly, no more verbosity.

Draw color blocks

For color blocks, you must first determine how many layers your color block has. This determines the radius length of each layer of color blocks. The number of layers here is determined by the two-dimensional array passed in from the outside. For the incoming two-dimensional array, one dimension determines the level, and the two dimensions are the array of color blocks under one dimension.

Through the above understanding, the "radius length of each layer of color blocks" can be obtained by dividing the "remaining radius" by the "number of color block layers".

Then divide 360 ​​by the "number of color blocks per layer" to get the angle occupied by each color block.

Finally, by setting the strokeWidth of the brush, you can draw.

Finally, the "radius range value", "angle range value" and "color value" generated during the drawing process are saved through intermediate variables for subsequent touch event calls.

ps:

There is an implementation detail here, which is about Paint’s strokeWidth. It is actually based on the expansion of both ends of the drawing coordinates. For example, if you draw a straight line, it will expand at both ends of the Y coordinate, such as (x, y) (0,10), and strokeWidth is 10, then the width of the right-angled side will be based on (0,5)-(0,15) middle.

Therefore, after setting strokeWidth and drawing, you need to reserve the position in the drawing implementation.

touch event

For touch events, they need to be processed in onTouchEvent. The parent class method is as follows:

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {

            }
            MotionEvent.ACTION_MOVE -> {

            }
            MotionEvent.ACTION_UP -> {
                val currentX = event.x
                val currentY = event.y
                dealWithUpEvent(currentX, currentY)
            }
            MotionEvent.ACTION_CANCEL -> {

            }
        }
        return true
    }

Here, by returning true, all events are intercepted and processed by this view.

Imagine that you have the center coordinates and the x, y coordinates when you click. Is it possible to derive a "quadrant interval" logic? Then through the mathematical formula tan method, the corresponding angle can be obtained.

Now that we have the angle and the quadrant, we can calculate the final real click angle and the radius of the click coordinate point. The core code is as follows:

    /**
     * 处理手势抬起事件
     * */
    private fun dealWithUpEvent(currentX: Float, currentY: Float) {
        //判断象限
        val trainX = (currentX - mCenterX)
        val trainY = (mCenterY - currentY)

        //求半径长度
        val x = abs(abs(currentX) - abs(mCenterX)).toDouble()
        val y = abs(abs(mCenterY) - abs(currentY)).toDouble()
        //半径
        val trainRadius = sqrt((x.pow(2.0) + y.pow(2.0)))
        //角度
        val angle = atan(abs(x) / abs(y)) * 180 / PI
        //计算后,再根据象限,转换最终的角度
        var trainAngle = 0f
        if (trainX <= 0 && trainY >= 0) {
            trainAngle = (90 - angle + 270).toFloat()
        } else if (trainX >= 0 && trainY >= 0) {
            trainAngle = angle.toFloat()
        } else if (trainX <= 0 && trainY <= 0) {
            trainAngle = (angle + 180).toFloat()
        } else if (trainX >= 0 && trainY <= 0) {
            trainAngle = (90 - angle + 90).toFloat()
        } else {
            return
        }
//        Log.d("dealWithUpEvent", "angle $angle + 转换角度  $trainAngle 半径: $trainRadius")
        //通过象限,角度,半径,三个条件,确定具体的位置
        val filterList: MutableList<LibColorInnerPosInfo> = ArrayList()
        this.mColorPosInfo.filter {
            it.startAngle <= trainAngle && it.endAngle >= trainAngle
        }.filter {
            it.startRadius >= trainRadius && it.endRadius <= trainRadius
        }.filter {
            !it.color.isNullOrBlank()
        }.toCollection(filterList)

        if (filterList.size != 1) {
            return
        }
        mListener?.selInfo(LibColorSelInfo().apply {
            this.colorStr = filterList.first().color
        })
    }

At this point, the entire process has been explained. If there is still something you don’t understand, please download the source code to understand:

Source code
extraction code: naf2

that’s all--------------------------------------------------------------

Guess you like

Origin blog.csdn.net/motosheep/article/details/129680892