Android 自定义Canvas取色盘 和 自定义View取色盘


自定义View 

public class ColorsHSV360 extends View {


    private Context mContext;
    private Paint mPaint;
    //父布局宽高
    private int measureHeigth, measureWidth;
    private float radius = 40, stroke = 5;
    private float x = -1,y = -1;
    //子布局位置
    //圆参数
    private Event event;
    private int color = Color.WHITE;



    public ColorsHSV360(Context context) {
        super(context);
        init(context);
    }

    public ColorsHSV360(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ColorsHSV360(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mContext = context;
        mPaint = new Paint();
        mPaint.setAntiAlias(true); // 消除锯齿
    }

    public void setEvent(Event event){
        this.event = event;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean isInit = false;
        if(MeasureSpec.getSize(heightMeasureSpec)==measureHeigth && MeasureSpec.getSize(widthMeasureSpec)==measureWidth){
            isInit = true;
        }
        measureHeigth = MeasureSpec.getSize(heightMeasureSpec);
        measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        if(!isInit){
            setXY(color);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createColorWheelBitmap(radius+stroke, (int) (measureWidth-2*(radius+stroke)), (int) (measureHeigth-2*(radius+stroke)), (int) (radius+stroke)*2, canvas);
        }else {
            canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.colorblock_2),null, new RectF(0,0,measureWidth,measureHeigth),null);
        }
        createCircleImage(canvas);
        super.onDraw(canvas);
    }



    @Override
    public boolean onTouchEvent(MotionEvent e) {
        x = (int) e.getX();
        x = Math.min(Math.max(x, radius+stroke), measureWidth-radius-stroke);
        y = (int) e.getY();
        y = Math.min(Math.max(y, radius+stroke), measureHeigth-radius-stroke);
        int hsvToColor = Color.HSVToColor(new float[]{ ((x-(radius+stroke)) / (measureWidth-2f*(radius+stroke))) * 360, (y-(radius+stroke)) / (measureHeigth-2f*(radius+stroke)), 1f});
        int iMaskAct = e.getAction() & MotionEvent.ACTION_MASK;
        if(event != null){
            int red = (hsvToColor & 0x00ff0000) >> 16;
            int green = (hsvToColor & 0x0000ff00) >> 8;
            int blue = (hsvToColor & 0x000000ff);
            event.getColors(red,green,blue,iMaskAct == MotionEvent.ACTION_UP);
        }
        invalidate();
        return true;
    }

    //设置颜色
    public void setXY(Integer color){
        float[] hsv = new float[3];
        Color.colorToHSV(color,hsv);

        this.x =hsv[0]/360*(measureWidth-2*radius-2*stroke)+radius+stroke;
        this.y =hsv[1] * (measureHeigth-2*radius-2*stroke)+radius+stroke;
        if(event != null){
            int red = (color & 0x00ff0000) >> 16;
            int green = (color & 0x0000ff00) >> 8;
            int blue = (color & 0x000000ff);
            event.getColors(red,green,blue,true);
        }
        invalidate();
    }

    //创建色盘背景Bitmap
    private void createColorWheelBitmap(float radius,int width, int height, int topRadius, Canvas canvas) {
        mPaint.reset();
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        int colorCount = 360;
        int colorAngleStep = 360 / colorCount;
        int[] colors = new int[colorCount + 1];
        float[] hsv = new float[]{0f, 1f, 1f};
        for (int i = 0; i < colors.length; i++) {
            hsv[0] = (i * colorAngleStep) % 360;
            colors[i] = Color.HSVToColor(hsv);
        }
        colors[colorCount] = colors[0];
        LinearGradient linearGradient1 = new LinearGradient(radius,radius,width,radius,colors,null, Shader.TileMode.CLAMP);
        LinearGradient linearGradient2 = new LinearGradient(radius,radius,radius,height,Color.argb(255,255,255,255),Color.argb(0,255,255,255),Shader.TileMode.CLAMP);
        ComposeShader composeShader = new ComposeShader(linearGradient1, linearGradient2, PorterDuff.Mode.SRC_OVER);
        mPaint.setShader(composeShader);
        Path path = new Path();
        path.arcTo(new RectF(0, 0, topRadius, topRadius),180,90);
        path.arcTo(new RectF(width - topRadius + 2* radius , 0, width + 2*radius, topRadius),270,90);
        path.lineTo(width+2*radius,height+2*radius);
        path.lineTo(0,height+2*radius);
        path.close();
        canvas.drawPath(path, mPaint);
    }

    /**
     * 一个圆
     */
    private void createCircleImage(Canvas canvas) {
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        int hsvToColor = Color.HSVToColor(new float[]{ ((x-(radius+stroke)) / (measureWidth-2f*(radius+stroke))) * 360, (y-(radius+stroke)) / (measureHeigth-2f*(radius+stroke)), 1f});
        mPaint.setColor(hsvToColor);
        canvas.drawCircle(x,y, radius, mPaint);
        mPaint.setStrokeWidth(stroke);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.parseColor("#FFEAEAEA"));
        canvas.drawCircle(x,y, radius, mPaint);
        mPaint.setStrokeWidth(stroke-2);
        mPaint.setColor(Color.parseColor("#FFFFFFFF"));
        canvas.drawCircle(x,y, radius+2, mPaint);
    }

    public interface Event{
        void getColors(int red,int green,int blue,boolean up);
    }
}

定义Canvas

@OptIn(ExperimentalGraphicsApi::class)
    @Composable
    fun colorPicker(radius:Float,stroke:Float) {
        val colorCount = 360
        val colorAngleStep = 360 / colorCount
        val colors = mutableListOf()
        //存储选中颜色
        val hsv = floatArrayOf(0f, 1f, 1f)
        for (i in 0..colorCount) {
            hsv[0] = (i * colorAngleStep % 360).toFloat()
            colors.add(Color.hsv(hsv[0],hsv[1],hsv[2]))
        }
        val x = rememberSaveable{ mutableStateOf(-1f) }
        val y = rememberSaveable{ mutableStateOf(-1f) }
        Canvas(modifier = Modifier
            .fillMaxWidth()
            .height(200.dp)
            .padding(5.dp)
            .pointerInput(Unit) {
                forEachGesture {
                    awaitPointerEventScope {
//PointerEventPass.Initial解决点击和拖动的冲突
                        val event = awaitPointerEvent(PointerEventPass.Initial)
                        if(event.changes.firstOrNull()?.changedToDown() == true) {
                            var newValue = Offset(
                                x = event.changes.first().position.x.coerceIn(radius.dp.toPx()+stroke, size.width - radius.dp.toPx()-stroke),
                                y = event.changes.first().position.y.coerceIn(radius.dp.toPx()+stroke, size.height - radius.dp.toPx()-stroke)
                            )
                            x.value = newValue.x
                            y.value = newValue.y
                            hsv[0] = (newValue.x-(radius.dp.toPx()+stroke)) / (size.width - 2* radius.dp.toPx()) * 360
                            hsv[1] = (newValue.y-(radius.dp.toPx()+stroke)) / (size.height - 2* radius.dp.toPx())
                            Log.d("TAG","newValue111-->$newValue")
                            val down = awaitFirstDown(requireUnconsumed = true)
                            var drag: PointerInputChange?
                            do {
                                drag = awaitTouchSlopOrCancellation(down.id) { change, _ ->
                                    change.consumePositionChange()
                                }
                            } while (drag != null && !drag.positionChangeConsumed())
                            if (drag != null) {
                                !drag(drag.id) {
                                    newValue = Offset(
                                        x = it.position.x.coerceIn(radius.dp.toPx()+stroke, size.width - radius.dp.toPx()-stroke),
                                        y = it.position.y.coerceIn(radius.dp.toPx()+stroke, size.height - radius.dp.toPx()-stroke)
                                    )
                                    x.value = newValue.x
                                    y.value = newValue.y
                                    hsv[0] = (newValue.x-(radius.dp.toPx()+stroke)) / (size.width - 2* radius.dp.toPx()) * 360
                                    hsv[1] = (newValue.y-(radius.dp.toPx()+stroke)) / (size.height - 2* radius.dp.toPx())
                                    Log.d("TAG","newValue333-->$newValue")
                                    it.consumePositionChange()
                                }
                            }
                        }
                    }
                }
            }
        ) {
            //初始化
            if(x.value == -1f){
                x.value = radius.dp.toPx()+stroke
                y.value = radius.dp.toPx()+stroke
                hsv[0] = 0f;
                hsv[1] = 0f;
                hsv[2] = 1f;
            }
            //hsv h 的渐变色
            drawRoundRect(
                brush = Brush.horizontalGradient(colors = colors, startX = radius.dp.toPx(), endX = size.width -radius.dp.toPx()),
                cornerRadius = CornerRadius(radius.dp.toPx(), radius.dp.toPx()),
                style = Fill,
                size = Size(size.width , size.height)
            )
            //白色到透明的渐变色
            drawRoundRect(
                brush = Brush.verticalGradient(colors = listOf(Color.White, Color.Transparent), startY = radius.dp.toPx(), endY = size.height-radius.dp.toPx()),
                cornerRadius = CornerRadius(radius.dp.toPx(), radius.dp.toPx()),
                style = Fill,
                size = Size(size.width , size.height)
            )
            //白色边框
            drawRoundRect(
                color= Color.White,
                cornerRadius = CornerRadius(radius.dp.toPx(), radius.dp.toPx()),
                style = Stroke(width = stroke),
                topLeft = Offset(stroke/2f, stroke/2f),
                size = Size(size.width-stroke, size.height-stroke)
            )
            //黑色边框
            drawRoundRect(
                color= Color.Black,
                cornerRadius = CornerRadius(radius.dp.toPx(), radius.dp.toPx()),
                style = Stroke(width = stroke/5f),
                topLeft = Offset(stroke, stroke),
                size = Size(size.width-2*stroke, size.height-2*stroke)
            )
            //
            drawCircle(
                color = Color.hsv(hsv[0],hsv[1],hsv[2]),
                radius = radius.dp.toPx(),
                style = Fill,
                center = Offset(x.value,y.value)
            )
            //圆黑色边框
            drawCircle(
                color = Color.Gray,
                radius = radius.dp.toPx()-stroke,
                style = Stroke(width = stroke),
                center = Offset(x.value,y.value)
            )
            //白色黑色边框
            drawCircle(
                color = Color.White,
                radius = radius.dp.toPx()-stroke/2,
                style = Stroke(width = stroke),
                center = Offset(x.value,y.value)
            )
        }
    }