package com.hdl.sdk.ttl_sdk.utlis; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.MaskFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.RadialGradient; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Shader; import android.graphics.SweepGradient; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import com.hdl.sdk.ttl_sdk.R; import java.util.Locale; /** * Created by Tong on 2021/10/27. * Rgb颜色圆环选择器 */ public class RGBColorPicker extends View { private int mWidth, mHeight; //色 private SweepGradient mHueShader; //饱和度 private RadialGradient mSaturationShader; //色板画笔 private Paint mPalettePaint; //圆盘范围 private final Region mPaletteScopeRegion = new Region(); private Paint paint; //指针半径 private int markSize; private int markColor; private int mCurrentColor; private MaskFilter maskFilter; private float touchX = Float.MAX_VALUE; private float touchY = Float.MAX_VALUE; private ColorListener listener; private ValueAnimator mDelayAnim; public abstract static class DelayColorListener implements ColorListener { /** * 延迟500毫秒 */ private static final long DEF_DELAY = 500; private long lastTime = 0; private long delay = DEF_DELAY; public DelayColorListener(long delay) { this.delay = delay; } public DelayColorListener() { } public abstract void onDelayColorChanged(@ColorInt int color, boolean fromUser); @Override public void onColorSelected(int color, boolean fromUser) { onDelayColorChanged(color, fromUser); } @Override public void onColorChange(int color, boolean fromUser) { if (System.currentTimeMillis() - lastTime > delay) { onDelayColorChanged(color, fromUser); lastTime = System.currentTimeMillis(); } } } public interface ColorListener { /** * 稳定 */ void onColorSelected(@ColorInt int color, boolean fromUser); /** * 颜色变化 */ void onColorChange(@ColorInt int color, boolean fromUser); } public RGBColorPicker(Context context) { this(context, null); } public RGBColorPicker(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public RGBColorPicker(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (attrs != null) { TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.RGBColorPicker, defStyleAttr, 0); markColor = a.getColor(R.styleable.RGBColorPicker_markColor, Color.WHITE); markSize = a.getDimensionPixelOffset(R.styleable.RGBColorPicker_markSize, 0); mCurrentColor = a.getColor(R.styleable.RGBColorPicker_markColor, Color.WHITE); a.recycle(); } init(); } private void init() { setLayerType(View.LAYER_TYPE_SOFTWARE, null); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.FILL); paint.setStrokeCap(Paint.Cap.ROUND); mPalettePaint = new Paint(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); this.mWidth = getWidth(); this.mHeight = getHeight(); initPalette(); } public void setColorListener(ColorListener listener) { this.listener = listener; } @Override protected void onDraw(Canvas canvas) { drawPalette(canvas); drawDropper(canvas); } /** * 初始化调色板 */ private void initPalette() { float centerX = mWidth * 0.5f; float centerY = mHeight * 0.5f; float radius = Math.min(centerX, centerY); mHueShader = new SweepGradient( mWidth * 0.5f, mHeight * 0.5f, new int[]{ Color.RED, Color.MAGENTA, Color.BLUE, Color.CYAN, Color.GREEN, Color.YELLOW, Color.RED, }, new float[]{0.000f, 0.166f, 0.333f, 0.499f, 0.666f, 0.833f, 0.999f}); mSaturationShader = new RadialGradient( centerX, centerY, radius, Color.WHITE, Color.parseColor("#00FFFFFF"), Shader.TileMode.CLAMP); //色盘范围 final Path mPaletteScopePath = new Path(); mPaletteScopePath.addCircle(centerX, centerY, radius - markSize * 0.5f, Path.Direction.CW); final RectF bounds = new RectF(); mPaletteScopePath.computeBounds(bounds, true); mPaletteScopeRegion.setPath(mPaletteScopePath, new Region((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom)); maskFilter = new BlurMaskFilter(markSize * 0.1f, BlurMaskFilter.Blur.OUTER); } /** * 画取色器 */ private void drawDropper(Canvas canvas) { if (maskFilter == null) return; final float centerX = mWidth * 0.5f; final float centerY = mHeight * 0.5f; final float radius = Math.min(centerX, centerY); final Point point = getNearPoint(touchX, touchY, centerX, centerY, radius, markSize / 2); float x = point.x; float y = point.y; paint.reset(); paint.setStyle(Paint.Style.STROKE); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setMaskFilter(maskFilter); paint.setColor(Color.BLACK); canvas.drawCircle(x, y, markSize * 0.5f, paint); paint.reset(); paint.setStyle(Paint.Style.FILL); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setColor(markColor); paint.setStrokeWidth(dp2pxAdapt(0.5f)); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); canvas.drawCircle(x, y, markSize * 0.5f, paint); } /** * 画调色板 */ private void drawPalette(Canvas canvas) { final float centerX = mWidth * 0.5f; final float centerY = mHeight * 0.5f; final float radius = Math.min(centerX, centerY); mPalettePaint.reset(); mPalettePaint.setFlags(Paint.ANTI_ALIAS_FLAG); mPalettePaint.setShader(mHueShader); canvas.drawCircle(centerX, centerY, radius, mPalettePaint); mPalettePaint.reset(); mPalettePaint.setFlags(Paint.ANTI_ALIAS_FLAG); mPalettePaint.setShader(mSaturationShader); canvas.drawCircle(centerX, centerY, radius, mPalettePaint); } @Override public boolean onTouchEvent(MotionEvent event) { if (mDelayAnim != null) { mDelayAnim.cancel(); } float x = event.getX(); float y = event.getY(); int color = getColorByPoint(x, y); this.mCurrentColor = color; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: if (listener != null) { listener.onColorChange(color, true); } touchX = x; touchY = y; invalidate(); return true; case MotionEvent.ACTION_UP: if (listener != null) { listener.onColorChange(color, true); listener.onColorSelected(color, true); } touchX = x; touchY = y; invalidate(); return true; } return super.onTouchEvent(event); } /** * 指定坐标是否在圆上,减了小圆圈半径 */ private boolean isPointInPalette(float x, float y) { return mPaletteScopeRegion.contains((int) x, (int) y); } /** * RGB 0红 120 绿 240蓝 */ private int getColorByPoint(float x, float y) { final float centerX = mWidth * 0.5f; final float centerY = mHeight * 0.5f; final float radius = Math.min(centerX, centerY); x = x - centerX; y = y - centerY; //色调0-360,饱和度0-1,亮度范围0-1 float[] hsv = {0, 0, 1}; //色调计算角度 hsv[0] = (float) (Math.atan2(y, -x) * 180f / Math.PI) + 180; //饱和度,从圆心出发0~1 double r = Math.sqrt(x * x + y * y); hsv[1] = Math.max(0f, Math.min(1f, (float) (r / radius))); return Color.HSVToColor(hsv); } public void setCurrentColor(int mCurrentColor) { if (mDelayAnim != null) { mDelayAnim.cancel(); } setCurrentColorInner(mCurrentColor); } private void setCurrentColorInner(int currentColor) { this.mCurrentColor = currentColor; post(new Runnable() { @Override public void run() { Point point = getPointByColor(mCurrentColor); touchX = point.x; touchY = point.y; invalidate(); } }); } public void setCurrentColorDelay(final int color) { if (mDelayAnim != null) { mDelayAnim.cancel(); } //不相等才处理 if (mCurrentColor != color) { mDelayAnim = ValueAnimator.ofFloat(0, 1); mDelayAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { final float p = (float) animation.getAnimatedValue(); if (p == 1) { setCurrentColorInner(color); } } }); mDelayAnim.setStartDelay(300); mDelayAnim.setDuration(100); mDelayAnim.start(); } } public int getCurrentColor() { return mCurrentColor; } public static String getHexCode(@ColorInt int color) { int a = Color.alpha(color); int r = Color.red(color); int g = Color.green(color); int b = Color.blue(color); return String.format(Locale.getDefault(), "%02X%02X%02X%02X", a, r, g, b); } /** * 通过颜色获取坐标 * 相对坐标 */ private Point getPointByColor(@ColorInt int color) { float[] hsv = {0, 0, 1}; Color.colorToHSV(color, hsv); final float centerX = mWidth * 0.5f; final float centerY = mHeight * 0.5f; final float radius = Math.min(centerX, centerY); final float d = hsv[1] * radius; //转为弧度 double radians = Math.toRadians(hsv[0]); int x = (int) (centerX + (float) (d * Math.cos(radians))); int y = (int) (centerY - (float) (d * Math.sin(radians))); return new Point(x, y); } /** * 获取最近距离坐标点 */ private Point getNearPoint(float x, float y, float centerX, float centerY, float parentRadius, float markRadius) { Point point = new Point(); if (isPointInPalette(x, y)) { point.x = (int) x; point.y = (int) y; } else { //计算最近坐标 x = x - parentRadius; y = y - parentRadius; int r = (int) (parentRadius - markRadius); //角度 float angle = (float) (Math.atan2(y, -x) * 180f / Math.PI) + 180; //转为弧度 double radians = Math.toRadians(angle); point.x = (int) (centerX + (float) (r * Math.cos(radians))); point.y = (int) (centerY - (float) (r * Math.sin(radians))); } return point; } public void setMarkColor(int markColor) { this.markColor = markColor; } private static final float WIDTH_DP_BASE = 360; //==================根据密度适配========================= public static int dp2pxAdapt(float dp) { return dp2pxAdapt(dp, WIDTH_DP_BASE); } public static int dp2pxAdapt(float dp, float widthDpBase) { DisplayMetrics dm = Resources.getSystem().getDisplayMetrics(); float density = dm.density; float heightDP = dm.heightPixels / density; float widthDP = dm.widthPixels / density; float w = Math.min(widthDP, heightDP); return (int) (dp * w / widthDpBase * density + 0.5f); } }