最近工作需要,自定了一个颜色选择器,效果图如下:

ff4296abb80b24f644619465bf6fc27a.gif

颜色种类是固定的,圆环上有个指示器,指示选中的颜色,这个定义起来应该是很简单了,直接上代码。

public class MyColorPicker extends View {

private int mThumbHeight;

private int mThumbWidth;

private String[] colors ;

private int sections;

//每个小块的度数

private int sectionAngle;

private Paint mPaint;

private int ringWidth;

private RectF mRectF;

private Drawable mThumbDrawable = null;

private float mThumbLeft;

private float mThumbTop;

private double mViewCenterX, mViewCenterY;

private double mViewRadisu;

//起始角度

private int mStartDegree = -90;

//当前view的尺寸

private int mViewSize;

private int textColor;

private String text="";

private Paint textPaint;

private Rect mBounds;

private float textSize;

private int colorType;

private int default_size = 100;

public MyColorPicker(Context context) {

this(context, null);

}

public MyColorPicker(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public MyColorPicker(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

TypedArray localTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleColorPicker);

mThumbDrawable = localTypedArray.getDrawable(R.styleable.CircleColorPicker_thumb);

ringWidth = (int) localTypedArray.getDimension(R.styleable.CircleColorPicker_ring_span, 30);

colorType = localTypedArray.getInt(R.styleable.CircleColorPicker_color_type, 0);

textColor = localTypedArray.getColor(R.styleable.CircleColorPicker_text_color, Color.BLACK);

text = localTypedArray.getString(R.styleable.CircleColorPicker_text);

textSize = localTypedArray.getDimension(R.styleable.CircleColorPicker_text_size, 20);

localTypedArray.recycle();

default_size = SystemUtils.dip2px(context, 260);

init();

}

private void init() {

colors = colorType == 1 ? ColorUtils.getMacaroon():ColorUtils.getAllColors();

sections = colors.length;

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(ringWidth);

textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

textPaint.setColor(textColor);

textPaint.setTextSize(textSize);

mThumbWidth = this.mThumbDrawable.getIntrinsicWidth();

mThumbHeight = this.mThumbDrawable.getIntrinsicHeight();

sectionAngle = 360/sections;

mBounds = new Rect();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// super.onMeasure(widthMeasureSpec, heightMeasureSpec);

setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));

int circleX = getMeasuredWidth();

int circleY = getMeasuredHeight();

if (circleY < circleX)

{

circleX = circleY;

}

mViewSize = circleX;

mViewCenterX = circleX/2;

mViewCenterY = circleY/2;

mViewRadisu = circleX/2 - mThumbWidth / 2;

setThumbPosition(Math.toRadians(mStartDegree));

}

private int getMeasuredLength(int length, boolean isWidth) {

int specMode = MeasureSpec.getMode(length);

int specSize = MeasureSpec.getSize(length);

int size;

int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();

if (specMode == MeasureSpec.EXACTLY) {

size = specSize;

} else {

size = default_size + padding;

if (specMode == MeasureSpec.AT_MOST) {

size = Math.min(size, specSize);

}

}

return size;

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

mRectF = new RectF(0+mThumbWidth/2, 0+mThumbWidth/2, mViewSize-mThumbWidth/2, mViewSize-mThumbWidth/2);

for (int i = 0; i < colors.length; i++)

{

mPaint.setColor(Color.parseColor(colors[i]));

canvas.drawArc(mRectF, i*sectionAngle-90, sectionAngle+1,false, mPaint);

}

mThumbDrawable.setBounds((int) mThumbLeft, (int) mThumbTop,

(int) (mThumbLeft + mThumbWidth), (int) (mThumbTop + mThumbHeight));

mThumbDrawable.draw(canvas);

textPaint.getTextBounds(text, 0, text.length(), mBounds);

float textWidth = mBounds.width();

float textHeight = mBounds.height();

float textLeft = (float) (mViewCenterX - textWidth/2);

float textTop = (float)(mViewCenterY + textHeight/2);

canvas.drawText(text, 0, text.length(), textLeft, textTop, textPaint);

}

private void setThumbPosition(double radian) {

double x = mViewCenterX + mViewRadisu * Math.cos(radian);

double y = mViewCenterY + mViewRadisu * Math.sin(radian);

mThumbLeft = (float) (x - mThumbWidth / 2);

mThumbTop = (float) (y - mThumbHeight / 2);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

float eventX = event.getX();

float eventY = event.getY();

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

seekTo(eventX, eventY, false);

break ;

case MotionEvent.ACTION_MOVE:

seekTo(eventX, eventY, false);

break ;

case MotionEvent.ACTION_UP:

// seekTo(eventX, eventY, true);

float part = sectionAngle / 4.0f;

for (int i = 0; i < sections; i++) {

if ( mSweepDegree > (i-1)*sectionAngle+part*3 && mSweepDegree < i *sectionAngle + part)

{

if (mSweepDegree < i*sectionAngle)

{

setThumbPosition(Math.toRadians((i-1)*sectionAngle+part*2));

}else {

setThumbPosition(Math.toRadians(i*sectionAngle+part*2));

}

}

}

if (mSweepDegree > ((sections-1)*sectionAngle)+part*3)

{

setThumbPosition(Math.toRadians((sections-1)*sectionAngle+part*2));

}

invalidate();

break ;

}

return true;

}

private int preColor;

private float mSweepDegree;

private void seekTo(float eventX, float eventY, boolean isUp) {

if (true == isPointOnThumb(eventX, eventY) && false == isUp) {

// mThumbDrawable.setState(mThumbPressed);

double radian = Math.atan2(eventY - mViewCenterY, eventX - mViewCenterX);

/*

* 由于atan2返回的值为[-pi,pi]

* 因此需要将弧度值转换一下,使得区间为[0,2*pi]

*/

if (radian < 0){

radian = radian + 2*Math.PI;

}

setThumbPosition(radian);

mSweepDegree = (float) Math.round(Math.toDegrees(radian));

int currentColor = getColor(mSweepDegree);

if (currentColor != preColor)

{

preColor = currentColor;

if (onColorChangeListener != null)

{

onColorChangeListener.colorChange(preColor);

}

}

invalidate();

}else{

// mThumbDrawable.setState(mThumbNormal);

invalidate();

}

}

private int getColor(float mSweepDegree) {

int tempIndex = (int) (mSweepDegree/sectionAngle);

int num = 90 / sectionAngle;

if (tempIndex ==sections)

{

tempIndex = 0;

}

int index = tempIndex;

if (tempIndex >= 0) {

index = tempIndex+num;

}

if (tempIndex >= (sections-num))

{

index = tempIndex-(sections-num);

}

return Color.parseColor(colors[index]);

}

private boolean isPointOnThumb(float eventX, float eventY) {

boolean result = false;

double distance = Math.sqrt(Math.pow(eventX - mViewCenterX, 2)

+ Math.pow(eventY - mViewCenterY, 2));

if (distance < mViewSize && distance > (mViewSize / 2 - mThumbWidth)){

result = true;

}

return result;

}

public int getCurrentColor()

{

return preColor;

}

public void setStartColor(String color)

{

for (int i = 0; i < colors.length; i++)

{

if (colors[i].equals(color))

{

preColor = Color.parseColor(colors[i]);

int sweepAngle = (i- 90 /sectionAngle)*sectionAngle+sectionAngle/2;

// postDelayed(()->{

// setThumbPosition(Math.toRadians(sweepAngle));

// invalidate();

// },200);

mStartDegree = sweepAngle;

//最好加上

invalidate();

break;

}

}

}

public void setColor(String color) {

for (int i = 0; i < colors.length; i++)

{

if (colors[i].equals(color))

{

preColor = Color.parseColor(colors[i]);

int sweepAngle = (i- 90 /sectionAngle)*sectionAngle+sectionAngle/2;

setThumbPosition(Math.toRadians(sweepAngle));

invalidate();

break;

}

}

}

public interface OnColorChangeListener

{

void colorChange(int color);

}

public void setOnColorChangeListener(OnColorChangeListener onColorChangeListener) {

this.onColorChangeListener = onColorChangeListener;

}

private OnColorChangeListener onColorChangeListener;

}

注意的几个地方:

1. 可滑动位置的判断以及如何求滑动的角度,这里还去脑补了下atan2这个三角函数

2. 设置指示器的开始的位置,外部调用setStartColor()方法时,这个View可能还没真正完成绘制。如果没有完成绘制,第几行的invalidate()方法其实是没多大作用。

上面是选择单个颜色,下面来个加强版,选择的是颜色区间,先上效果图:

3ab3e45fa3eb2bccd4b62e0fe9033b53.gif

区间可以自己选择,并且可以反转(低指示器在高指示器顺时针方向或逆时针方向)。

下面是代码:

public class IntervalColorPicker extends View {

private int mThumbHeight;

private int mThumbWidth;

private int mThumbLowHeight, mThumbLowWidth;

private String[] colors = ColorUtils.getAllColors();

private int sections;

//每个小块的度数

private int sectionAngle;

private Paint mPaint;

private Paint arcPaint;

private int ringWidth;

private RectF mRectF;

private Drawable mThumbHighDrawable = null;

private Drawable mThumbLowDrawable;

private float mThumbLeft;

private float mThumbTop;

private float mThumbLowLeft, mThumbLowTop;

private double mViewCenterX, mViewCenterY;

private double mViewRadisu;

//起始角度

private float mStartDegree = 270;

//当前view的尺寸

private int mViewSize;

//区间

private int interval = 7;

private boolean reverse;

private float tempStartAngle = mStartDegree;

public IntervalColorPicker(Context context) {

this(context, null);

}

public IntervalColorPicker(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public IntervalColorPicker(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

TypedArray localTypedArray = context.obtainStyledAttributes(attrs, R.styleable.IntervalColorPicker);

mThumbHighDrawable = localTypedArray.getDrawable(R.styleable.IntervalColorPicker_thumbHigh);

mThumbLowDrawable = localTypedArray.getDrawable(R.styleable.IntervalColorPicker_thumbLow);

ringWidth = (int) localTypedArray.getDimension(R.styleable.IntervalColorPicker_ring_breadth, 30);

localTypedArray.recycle();

init();

}

private void init() {

sections = colors.length;

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(ringWidth);

arcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

arcPaint.setStyle(Paint.Style.STROKE);

arcPaint.setStrokeWidth(ringWidth + 1);

arcPaint.setColor(Color.GRAY);

mThumbWidth = this.mThumbHighDrawable.getIntrinsicWidth();

mThumbHeight = this.mThumbHighDrawable.getIntrinsicHeight();

mThumbLowHeight = mThumbLowDrawable.getIntrinsicHeight();

mThumbLowWidth = mThumbHighDrawable.getIntrinsicWidth();

sectionAngle = 360 / sections;

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int circleX = getMeasuredWidth();

int circleY = getMeasuredHeight();

if (circleY < circleX) {

circleX = circleY;

}

mViewSize = circleX;

mViewCenterX = circleX / 2;

mViewCenterY = circleY / 2;

mViewRadisu = circleX / 2 - mThumbWidth / 2;

}

private float sweepAngle;

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

mRectF = new RectF(0 + mThumbWidth / 2, 0 + mThumbWidth / 2, mViewSize - mThumbWidth / 2, mViewSize - mThumbWidth / 2);

for (int i = 0; i < colors.length; i++) {

mPaint.setColor(Color.parseColor(colors[i]));

canvas.drawArc(mRectF, i * sectionAngle - 90, sectionAngle + 1, false, mPaint);

}

int tempAng = (int) (tempStartAngle + sweepAngle);

int intervalAngle = interval * sectionAngle;

if (reverse) {

setThumbPosition(Math.toRadians(tempAng));

setThumbLowPosition(Math.toRadians(tempAng - intervalAngle));

canvas.drawArc(mRectF, tempAng, 360 - intervalAngle, false, arcPaint);

} else {

setThumbPosition(Math.toRadians(tempAng));

setThumbLowPosition(Math.toRadians(tempAng + intervalAngle));

canvas.drawArc(mRectF, (int) (tempAng + intervalAngle), 360 - intervalAngle, false, arcPaint);

}

mThumbHighDrawable.setBounds((int) mThumbLeft, (int) mThumbTop,

(int) (mThumbLeft + mThumbWidth), (int) (mThumbTop + mThumbHeight));

mThumbLowDrawable.setBounds((int) mThumbLowLeft, (int) mThumbLowTop, (int) (mThumbLowLeft + mThumbLowWidth), (int) (mThumbLowTop + mThumbLowHeight));

mThumbHighDrawable.draw(canvas);

mThumbLowDrawable.draw(canvas);

}

private void setThumbPosition(double radian) {

double x = mViewCenterX + mViewRadisu * Math.cos(radian);

double y = mViewCenterY + mViewRadisu * Math.sin(radian);

mThumbLeft = (float) (x - mThumbWidth / 2);

mThumbTop = (float) (y - mThumbHeight / 2);

}

private void setThumbLowPosition(double radian) {

double x = mViewCenterX + mViewRadisu * Math.cos(radian);

double y = mViewCenterY + mViewRadisu * Math.sin(radian);

mThumbLowLeft = (float) (x - mThumbLowWidth / 2);

mThumbLowTop = (float) (y - mThumbLowHeight / 2);

}

private boolean isDown = true;

@Override

public boolean onTouchEvent(MotionEvent event) {

float eventX = event.getX();

float eventY = event.getY();

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

getEventDegree(eventX, eventY);

// seekTo(eventX, eventY, false);

break;

case MotionEvent.ACTION_MOVE:

seekTo(eventX, eventY);

break;

case MotionEvent.ACTION_UP:

postDelayed(() -> {

tempStartAngle = tempStartAngle + sweepAngle;

sweepAngle = 0;

getSelectedColor();

if (onColorChangeListener != null) {

onColorChangeListener.colorChange(selectedColors);

}

}, 100);

break;

}

return true;

}

private float downDegree;

private void getEventDegree(float eventX, float eventY) {

if (isPointOnThumb(eventX, eventY)) {

double radian = Math.atan2(eventY - mViewCenterY, eventX - mViewCenterX);

/*

* 由于atan2返回的值为[-pi,pi]

* 因此需要将弧度值转换一下,使得区间为[0,2*pi]

*/

if (radian < 0) {

radian = radian + 2 * Math.PI;

}

isDown = true;

downDegree = Math.round(Math.toDegrees(radian));

} else {

isDown = false;

}

}

private void seekTo(float eventX, float eventY) {

if (true == isPointOnThumb(eventX, eventY)) {

// mThumbHighDrawable.setState(mThumbPressed);

if (!isDown) {

getEventDegree(eventX, eventY);

isDown = true;

}

double radian = Math.atan2(eventY - mViewCenterY, eventX - mViewCenterX);

/*

* 由于atan2返回的值为[-pi,pi]

* 因此需要将弧度值转换一下,使得区间为[0,2*pi]

*/

if (radian < 0) {

radian = radian + 2 * Math.PI;

}

setThumbPosition(radian);

float mSweepDegree = (float) Math.round(Math.toDegrees(radian));

sweepAngle = mSweepDegree - downDegree;

invalidate();

}

}

//选中的颜色

private ArrayList selectedColors = new ArrayList<>(interval);

public void getSelectedColor() {

int tempIndex = (int) (tempStartAngle / sectionAngle);

int num = 90 / sectionAngle;

if (tempIndex == sections) {

tempIndex = 0;

}

int index = tempIndex;

if (tempIndex >= 0) {

index = tempIndex + num;

}

if (tempIndex >= (sections - num)) {

index = tempIndex - (sections - num);

}

if (index>colors.length)

index = index%colors.length;

while (index<0)

{

index = colors.length+index;

}

selectedColors.clear();

int startIndex = 0;

if (reverse)

{

startIndex = index - interval -1;

while (startIndex < 0)

{

startIndex = startIndex+colors.length;

}

if (startIndex > index)

{

for (int i = startIndex+1; i < colors.length; i++) {

selectedColors.add(Color.parseColor(colors[i]));

}

for (int i = 0; i <= index; i++) {

selectedColors.add(Color.parseColor(colors[i]));

}

}else {

for (int i = startIndex+1; i <= index; i++) {

selectedColors.add(Color.parseColor(colors[i]));

}

}

}else {

startIndex = index+interval+1;

while (startIndex>colors.length)

{

startIndex = startIndex-colors.length;

}

if (startIndex < index)

{

for (int i = startIndex-1; i >= 0; i--) {

selectedColors.add(Color.parseColor(colors[i]));

}

for (int i = colors.length-1; i >= index; i--) {

selectedColors.add(Color.parseColor(colors[i]));

}

}else {

for (int i = startIndex-1; i >=index; i--) {

selectedColors.add(Color.parseColor(colors[i]));

}

}

}

}

private boolean isPointOnThumb(float eventX, float eventY) {

boolean result = false;

double distance = Math.sqrt(Math.pow(eventX - mViewCenterX, 2)

+ Math.pow(eventY - mViewCenterY, 2));

if (distance < mViewSize && distance > (mViewSize / 2 - mThumbWidth)) {

result = true;

}

return result;

}

public boolean isReverse() {

return reverse;

}

public void setReverse(boolean reverse) {

this.reverse = reverse;

invalidate();

}

public interface OnColorChangeListener {

void colorChange(ArrayList colors);

}

public void setOnColorChangeListener(OnColorChangeListener onColorChangeListener) {

this.onColorChangeListener = onColorChangeListener;

}

private OnColorChangeListener onColorChangeListener;

}

注意的地方:

1. 手势抬起时用了一个postDelayed方法,还是避免绘制的先后问题。

2. isDown变量的作用是判断,手势按下时是否在圆环上。当手势从圆环外滑倒圆环上时,避免指示器一下弹到手指位置。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐