安卓自定义颜色选择器

复制代码
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.example.uvideomanager.R;

public class ColorPickerView extends View {
    private static final String TAG = "ColorPickerView";
    private Context mContext;
    private Paint mBitmapPaint;
    // 色盘原图
    private Bitmap mPaletteBitmap;
    // 选择器圆点图片
    private Bitmap mSelectorBitmap;
    // 选择器半径
    private int mSelectorRadius;
    // 色盘圆心、色盘半径
    private int mPaletteCenterX, mPaletteCenterY;
    private float mPaletteRadius;
    // 当前选中坐标(画布坐标)
    private final PointF mSelectPoint = new PointF();
    // 色盘原始宽高
    private int mPaletteOriginW, mPaletteOriginH;
    // 十六进制颜色
    public String colorStr = "#FFFFFF";
    private OnColorBackListener mColorListener;

    public ColorPickerView(Context context) {
        this(context, null);
    }

    public ColorPickerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initPaint();
        initBitmap();
        initDefaultSelectPos();
    }

    private void initPaint() {
        mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBitmapPaint.setFilterBitmap(true); // 图片平滑缩放
    }

    // 加载图片资源,增加空值判断
    private void initBitmap() {
        // 色盘 pcick
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), R.mipmap.pcick, opt);
        mPaletteOriginW = opt.outWidth;
        mPaletteOriginH = opt.outHeight;
        opt.inJustDecodeBounds = false;
        mPaletteBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.pcick, opt);

        // 选择器 poc
        mSelectorBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.poc);
        if (mSelectorBitmap != null) {
            mSelectorRadius = mSelectorBitmap.getWidth() / 2;
        }

        // 色盘圆心、半径
        mPaletteCenterX = mPaletteOriginW / 2;
        mPaletteCenterY = mPaletteOriginH / 2;
        mPaletteRadius = Math.min(mPaletteCenterX, mPaletteCenterY) - mSelectorRadius / 2f;
    }

    // 默认选中圆心
    private void initDefaultSelectPos() {
        mSelectPoint.set(mPaletteCenterX, mPaletteCenterY);
        getRGB();
    }

    public void setOnColorBackListener(OnColorBackListener listener) {
        mColorListener = listener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 固定尺寸为色盘原图大小,避免拉伸导致坐标错位(核心修复点)
        setMeasuredDimension(mPaletteOriginW, mPaletteOriginH);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mPaletteBitmap == null || mPaletteBitmap.isRecycled()) return;
        // 原图1:1绘制,不拉伸,画布坐标=Bitmap像素坐标
        canvas.drawBitmap(mPaletteBitmap, 0, 0, mBitmapPaint);

        // 绘制选择圆点
        if (mSelectorBitmap != null && !mSelectorBitmap.isRecycled()) {
            float drawX = mSelectPoint.x - mSelectorRadius;
            float drawY = mSelectPoint.y - mSelectorRadius;
            canvas.drawBitmap(mSelectorBitmap, drawX, drawY, mBitmapPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        // 边界校正,限制在色盘圆形内
        clampPointToPaletteCircle(x, y);
        invalidate();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                getRGB();
                break;
        }
        return true;
    }

    // 【重构】向量归一化校正坐标,替代原来错误的三角函数象限算法
    private void clampPointToPaletteCircle(float touchX, float touchY) {
        // 圆心到触摸点向量
        float dx = touchX - mPaletteCenterX;
        float dy = touchY - mPaletteCenterY;
        // 两点距离
        float dist = (float) Math.hypot(dx, dy);

        if (dist <= mPaletteRadius) {
            // 在圆内,直接使用触摸坐标
            mSelectPoint.set(touchX, touchY);
        } else {
            // 在圆外,归一化向量贴到圆环边缘
            float scale = mPaletteRadius / dist;
            float finalX = mPaletteCenterX + dx * scale;
            float finalY = mPaletteCenterY + dy * scale;
            mSelectPoint.set(finalX, finalY);
        }
    }

    // 取色:画布坐标=Bitmap像素坐标,不再越界
    private void getRGB() {
        if (mPaletteBitmap == null || mPaletteBitmap.isRecycled()) return;
        int px = (int) mSelectPoint.x;
        int py = (int) mSelectPoint.y;
        // 坐标边界保护,防止越界
        px = Math.max(0, Math.min(px, mPaletteOriginW - 1));
        py = Math.max(0, Math.min(py, mPaletteOriginH - 1));

        int pixel = mPaletteBitmap.getPixel(px, py);
        int a = Color.alpha(pixel);
        int r = Color.red(pixel);
        int g = Color.green(pixel);
        int b = Color.blue(pixel);

        colorStr = "#" + toHex2(r) + toHex2(g) + toHex2(b);
        if (mColorListener != null) {
            mColorListener.onColorBack(a, r, g, b);
        }
    }

    // 转两位十六进制
    private String toHex2(int num) {
        String s = Integer.toHexString(num & 0xFF).toUpperCase();
        return s.length() == 1 ? "0" + s : s;
    }

    // 回收资源
    @Override
    protected void onDetachedFromWindow() {
        recycleBitmap(mPaletteBitmap);
        recycleBitmap(mSelectorBitmap);
        super.onDetachedFromWindow();
    }

    private void recycleBitmap(Bitmap bitmap) {
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
    }

    public String getColorStr() {
        return colorStr;
    }

    public void setColorStr(String colorStr) {
        this.colorStr = colorStr;
    }

    public interface OnColorBackListener {
        void onColorBack(int a, int r, int g, int b);
    }
}

//布局
<ImageView
   android:id="@+id/img_sett"
   android:layout_width="match_parent"
   android:layout_height="50dp" />

<com.example.uvideomanager.view.ColorPickerView
    android:id="@+id/colorPickerView"
    android:layout_width="wrap_content"
    android:layout_gravity="center"
    android:layout_height="wrap_content" />



// 颜色选择回调使用
colorPickerView.setOnColorBackListener { a, r, g, b ->
    img_sett.setBackgroundColor(Color.argb(a, r, g, b))
}

效果

原图自取

相关推荐
Coffeeee2 小时前
ios零基础的Android开发能否靠AI让老板省一笔人工费呢
android·ios·ai编程
Joey_friends2 小时前
指纹authenticate流程图
android·java·c++
郑州光合科技余经理2 小时前
家政O2O平台解析:从0搭建上门预约小程序解决方案
android·java·开发语言·前端·小程序·架构·php
yueqc14 小时前
Android 渲染(三):掉帧监控
android·渲染·apm·掉帧
Mico184 小时前
MySQL 8.0.35 基于GTID 主从复制安装增强半同步复制
android·mysql·adb
Kapaseker4 小时前
你是不是还没用过 select?实战 Kotlin select
android·kotlin
__Witheart__5 小时前
适用于Android内核boot.img生成流程
android·rockchip
木易 士心5 小时前
Jetpack Compose 深度解析:初始组合与重组的底层奥秘
android
GitLqr14 小时前
Flutter 无障碍开发实战:玩转 Semantics 解决视障用户使用痛点
android·flutter·dart