100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Android 自定义View取色盘 颜色选择器 可根据颜色值反向定位坐标

Android 自定义View取色盘 颜色选择器 可根据颜色值反向定位坐标

时间:2024-03-10 08:10:16

相关推荐

Android 自定义View取色盘 颜色选择器 可根据颜色值反向定位坐标

前言:

前段时间项目中需要用到色盘取色的功能,百度了许多相关的颜色选择器,发现这方面自定义View例子比较少,有用图片代替色盘的通过bitmap取色,但是只能取色,无法通过颜色值去定位在色盘上的坐标,由于没有找到适用的,所以自己根据需求花了一些时间写了个,本着不重复造轮子的原则,于是便将原来的删除一些多余的代码,在这里分享一下。

补上Demo地址:ColorWheelPicker

效果图:

首先是绘制中间的圆形色盘,因为需要不仅能通过触摸的屏幕坐标获取选中的颜色,还需要通过指定的int型color值来获取到颜色在色盘中所处的坐标,即通过颜色值反向定位坐标。所以在这里使用到了HVS颜色空间模型。

关于HVS颜色模型:

这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。

而在我们的色盘中这里则分别是:角度(H),半径(S),明度(V)不需要用上,有兴趣的可以去网上这个模型的相关资料。

通过HSV模型绘制中间的色盘:

//创建色盘Bitmapprivate Bitmap createColorWheelBitmap(int width, int height) {Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);int colorCount = 12;int colorAngleStep = 360 / colorCount;int colors[] = new int[colorCount + 1];float hsv[] = new float[]{0f, 1f, 1f};for (int i = 0; i < colors.length; i++) {hsv[0] = 360 - (i * colorAngleStep) % 360;colors[i] = Color.HSVToColor(hsv);}colors[colorCount] = colors[0];SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null);RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorWheelRadius, 0xFFFFFFFF, 0x00FFFFFF, Shader.TileMode.CLAMP);ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER);colorWheelPaint.setShader(composeShader);Canvas canvas = new Canvas(bitmap);canvas.drawCircle(width / 2, height / 2, colorWheelRadius, colorWheelPaint);//默认取圆心颜色,给一个默认颜色用于显示点标记currentColor = getColorAtPoint(markerPoint.x, markerPoint.y);return bitmap;}

绘制色盘后白色圆形

private void drawWhiteWheel(Canvas canvas) {//绘制白色圆圈whiteWheelPaint.setColor(Color.WHITE);canvas.drawCircle(centerWheelX, centerWheelY, whiteWheelRadius, whiteWheelPaint);}

绘制:

//绘制白色圆圈drawWhiteWheel(canvas);colorWheelBitmap = createColorWheelBitmap(colorWheelRadius * 2, colorWheelRadius * 2);//绘制色盘canvas.drawBitmap(colorWheelBitmap, mColorWheelRect.left, mColorWheelRect.top, null);

接下来绘制外圈色环:

和绘制色盘的方式一样,只不过将绘制圆形的画笔样式改为描边,并设置画笔宽度。

//设置画笔为描边 colorRingPaint.setStyle(Paint.Style.STROKE);colorRingBitmap = createColorRingBitmap(colorRingRadius * 2 + ringWidth, colorRingRadius * 2 + ringWidth);//绘制外圈彩色圆环canvas.drawBitmap(colorRingBitmap, mColorRingRect.left, mColorRingRect.top, null);//创建彩色圆环bitmapprivate Bitmap createColorRingBitmap(int width, int height) {Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);int colorCount = 12;int colorAngleStep = 360 / colorCount;int colors[] = new int[colorCount + 1];float hsv[] = new float[]{0f, 1f, 1f};for (int i = 0; i < colors.length; i++) {hsv[0] = 360 - (i * colorAngleStep) % 360;colors[i] = Color.HSVToColor(hsv);}colors[colorCount] = colors[0];SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null);RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorRingRadius, 0xFFFFFFFF, 0x00FFFFFF, Shader.TileMode.CLAMP);ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER);colorRingPaint.setShader(composeShader);Canvas canvas = new Canvas(bitmap);canvas.drawCircle(width / 2, height / 2, colorRingRadius, colorRingPaint);return bitmap;}

然后绘制色盘上的颜色点标记:

//绘制点标记drawMarker(canvas);private void drawMarker(Canvas canvas) {float markerWidth = markerBitmap.getWidth();float markerHeight = markerBitmap.getHeight();// 指定图片在屏幕上显示的区域(原图大小)float left = (markerPoint.x - (markerWidth / 2));//markerPoint 为当前所触摸的点float top = (markerPoint.y - markerHeight) + markerHeight * 1 / 10;RectF dst = new RectF(left, top, left + markerWidth, top + markerHeight);//点标记上的颜色显示float markerRadius = markerWidth / 3;markerPaint.setColor(currentColor);//设置颜色为当前颜色canvas.drawBitmap(markerBitmap, null, dst, null);//绘制点标记图片canvas.drawCircle(left + markerWidth / 2, top + markerWidth / 2 - 4, markerRadius, markerPaint);//绘制点标记中的变色小圆}

还需要绘制外圈彩色圆圆环上的滑动按钮,这里使用了Matrix矩阵将按钮图片平移到了彩色圆环上,使用Matrix目的是为了通过旋转的方式去改变滑动按钮的位置,使箭头方向准确

private void drawColorRingBtn(Canvas canvas) {int colorRingBtnWidth = colorRingBtnBitmap.getWidth();int colorRingBtnHeight = colorRingBtnBitmap.getHeight();int left = centerWheelX - colorRingBtnWidth;int top = centerWheelY - colorRingRadius - colorRingBtnHeight;// colorRingBtnRect = new RectF(left, top, left + colorRingBtnWidth, top + colorRingBtnHeight);colorRingBtnPoint.x = left + colorRingBtnWidth / 2;colorRingBtnPoint.y = top + colorRingBtnHeight / 2;colorRingMatrix.preTranslate(colorRingBtnPoint.x, colorRingBtnPoint.y);// canvas.drawBitmap(colorRingBtnBitmap, null, colorRingBtnRect, null);canvas.drawBitmap(colorRingBtnBitmap, colorRingMatrix, null);colorRingMatrix.reset();}

效果图:

最后需要的便是获取色盘以及彩色圆环上的触摸事件,让它们可移动起来并实时获取颜色值。

重写onTouchEvent方法:

private static int colorTmp;///用于判断颜色是否发生改变private PointF downPointF = new PointF();//按下的位置@Overridepublic boolean onTouchEvent(MotionEvent event) {int action = event.getActionMasked();switch (action) {case MotionEvent.ACTION_DOWN:colorTmp = currentColor;downPointF.x = event.getX();downPointF.y = event.getY();case MotionEvent.ACTION_MOVE:update(event);return true;case MotionEvent.ACTION_UP:if (colorTmp != currentColor) {onColorPickerChanger();}break;default:return true;}return super.onTouchEvent(event);}private void update(MotionEvent event) {float x = event.getX();float y = event.getY();updateSelector(x, y);updateRingSelector(x, y);}private void onColorPickerChanger() {if (onColorPickerChangerListener != null) {int red = (currentColor & 0xff0000) >> 16;int green = (currentColor & 0x00ff00) >> 8;int blue = (currentColor & 0x0000ff);onColorPickerChangerListener.onColorPickerChanger(currentColor, red, green, blue);}}

色盘选择颜色:

/*** 刷新s色盘所选择的颜色* @param eventX* @param eventY*/private void updateSelector(float eventX, float eventY) {float x = eventX - centerWheelX;float y = eventY - centerWheelY;double r = Math.sqrt(x * x + y * y);//判断是否在圆内if (r > colorWheelRadius) {//不在圆形范围内return;}//同时旋转外圈滑动按钮colorRingMatrix.preRotate(getRotationBetweenLines(centerWheelX, centerWheelY, eventX, eventY), centerWheelX, centerWheelY);currentPoint.x = x + centerWheelX;currentPoint.y = y + centerWheelY;markerPoint.x = currentPoint.x;//改变点标记位置markerPoint.y = currentPoint.y;currentColor = getColorAtPoint(eventX, eventY);//获取到的颜色invalidate();}/*** 获取两条线的夹角** @param centerX* @param centerY* @param xInView* @param yInView* @return*/public static int getRotationBetweenLines(float centerX, float centerY, float xInView, float yInView) {double rotation = 0;double k1 = (double) (centerY - centerY) / (centerX * 2 - centerX);double k2 = (double) (yInView - centerY) / (xInView - centerX);double tmpDegree = Math.atan((Math.abs(k1 - k2)) / (1 + k1 * k2)) / Math.PI * 180;if (xInView > centerX && yInView < centerY) { //第一象限rotation = 90 - tmpDegree;} else if (xInView > centerX && yInView > centerY) //第二象限{rotation = 90 + tmpDegree;} else if (xInView < centerX && yInView > centerY) { //第三象限rotation = 270 - tmpDegree;} else if (xInView < centerX && yInView < centerY) { //第四象限rotation = 270 + tmpDegree;} else if (xInView == centerX && yInView < centerY) {rotation = 0;} else if (xInView == centerX && yInView > centerY) {rotation = 180;}return (int) rotation;}/*** 根据坐标获取颜色* @param eventX* @param eventY* @return*/private int getColorAtPoint(float eventX, float eventY) {float x = eventX - centerWheelX;float y = eventY - centerWheelY;double r = Math.sqrt(x * x + y * y);float[] hsv = {0, 0, 1};hsv[0] = (float) (Math.atan2(y, -x) / Math.PI * 180f) + 180;hsv[1] = Math.max(0f, Math.min(1f, (float) (r / colorWheelRadius)));return Color.HSVToColor(hsv);}

色环颜色选择:

/*** 刷新色环选择** @param eventX* @param eventY*/private void updateRingSelector(float eventX, float eventY) {float x = downPointF.x - centerWheelX;float y = downPointF.y - centerWheelY;double r = Math.sqrt(x * x + y * y);//按下位置的半径//判断是否在圆内,或者色环上if ((r < colorRingRadius + ringWidth && r > colorRingRadius - ringWidth)) {colorRingMatrix.preRotate(getRotationBetweenLines(centerWheelX, centerWheelY, eventX, eventY), centerWheelX, centerWheelY);currentColor = getColorAtPoint(eventX, eventY);//int值颜色float[] hsv = getHSVColorAtPoint(eventX, eventY);//hsv值颜色float h = hsv[0];//hsv色盘色点角度float s = hsv[1];//hsv色盘色点相对于半径的比值float colorDotRadius = colorWheelRadius * s;//色点半径//根据角度和半径获取坐标float radian = (float) Math.toRadians(-h);float colorDotX = (float) (centerWheelX + Math.cos(radian) * colorDotRadius);float colorDotY = (float) (centerWheelY + Math.sin(radian) * colorDotRadius);markerPoint.x = colorDotX;同时改变色盘上点标记的位置markerPoint.y = colorDotY;invalidate();}}/*** 根据坐标获取HSV颜色值* @param eventX* @param eventY* @return*/private float[] getHSVColorAtPoint(float eventX, float eventY) {float x = eventX - centerWheelX;float y = eventY - centerWheelY;double r = Math.sqrt(x * x + y * y);float[] hsv = {0, 0, 1};hsv[0] = (float) (Math.atan2(y, -x) / Math.PI * 180f) + 180;hsv[1] = Math.max(0f, Math.min(1f, (float) (r / colorWheelRadius)));return hsv;}

最后贴一下设置颜色改变滑动按钮和点标记的方法,和上面的代码是一样的。

public void setColor(int color) {float[] hsv = {0, 0, 1};Color.colorToHSV(color, hsv);//根据hsv角度及半径获取坐标//根据角度和半径获取坐标float radian = (float) Math.toRadians(-hsv[0]);float colorDotRadius = hsv[1] * colorWheelRadius;float colorDotX = (float) (centerWheelX + Math.cos(radian) * colorDotRadius);float colorDotY = (float) (centerWheelY + Math.sin(radian) * colorDotRadius);//设置marker位置markerPoint.x = colorDotX;markerPoint.y = colorDotY;currentColor = getColorAtPoint(markerPoint.x, markerPoint.y);//设置当前颜色//设置色环按钮位置colorRingMatrix.preRotate(getRotationBetweenLines(centerWheelX, centerWheelY, markerPoint.x, markerPoint.y), centerWheelX, centerWheelY);invalidate();// paint.setColor(Color.rgb(red, green, blue));}

总结:

主要的实现原理其实就是通过HSV颜色模型绘制色盘,然后通过HSV颜色空间的坐标对应到色盘上,以圆心为原点H(0-360)对应角度,S(0-255)对应半径,V(明度)用不上,通过坐标系的方式取HSV模型上的颜色,这样取到的颜色值会更加的准确。反过来又根据HSV颜色值计算在模型上的坐标,对应计算出该颜色值在色盘上的坐标,即可实现通过颜色值反向定位坐标位置。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。