首页 > 系统 > Android > 正文

android自定义View实现圆环颜色选择器

2019-10-22 18:10:17
字体:
来源:转载
供稿:网友

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

android,View,颜色选择器

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

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()方法其实是没多大作用。

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

android,View,颜色选择器

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

下面是代码:

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<Integer> 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<Integer> colors); } public void setOnColorChangeListener(OnColorChangeListener onColorChangeListener) {  this.onColorChangeListener = onColorChangeListener; } private OnColorChangeListener onColorChangeListener;}

注意的地方:

1. 手势抬起时用了一个postDelayed方法,还是避免绘制的先后问题。 
2. isDown变量的作用是判断,手势按下时是否在圆环上。当手势从圆环外滑倒圆环上时,避免指示器一下弹到手指位置。

github地址:colorpicker

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


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表