前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android自定义View实现圆环颜色选择器

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

作者头像
砸漏
发布2020-11-05 10:22:02
1.5K0
发布2020-11-05 10:22:02
举报
文章被收录于专栏:恩蓝脚本

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

这里写图片描述
这里写图片描述

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

代码语言:javascript
复制
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()方法其实是没多大作用。

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

这里写图片描述
这里写图片描述

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

下面是代码:

代码语言:javascript
复制
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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档