/* * Copyright (C) 2017 Jared Rummler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jaredrummler.android.colorpicker; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ComposeShader; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Shader.TileMode; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; /** * Displays a color picker to the user and allow them to select a color. A slider for the alpha channel is also * available. * Enable it by setting setAlphaSliderVisible(boolean) to true. */ public class ColorPickerView extends View { private final static int DEFAULT_BORDER_COLOR = 0xFF6E6E6E; private final static int DEFAULT_SLIDER_COLOR = 0xFFBDBDBD; private final static int HUE_PANEL_WDITH_DP = 30; private final static int ALPHA_PANEL_HEIGH_DP = 20; private final static int PANEL_SPACING_DP = 10; private final static int CIRCLE_TRACKER_RADIUS_DP = 5; private final static int SLIDER_TRACKER_SIZE_DP = 4; private final static int SLIDER_TRACKER_OFFSET_DP = 2; /** * The width in pixels of the border * surrounding all color panels. */ private final static int BORDER_WIDTH_PX = 1; /** * The width in px of the hue panel. */ private int huePanelWidthPx; /** * The height in px of the alpha panel */ private int alphaPanelHeightPx; /** * The distance in px between the different * color panels. */ private int panelSpacingPx; /** * The radius in px of the color palette tracker circle. */ private int circleTrackerRadiusPx; /** * The px which the tracker of the hue or alpha panel * will extend outside of its bounds. */ private int sliderTrackerOffsetPx; /** * Height of slider tracker on hue panel, * width of slider on alpha panel. */ private int sliderTrackerSizePx; private Paint satValPaint; private Paint satValTrackerPaint; private Paint alphaPaint; private Paint alphaTextPaint; private Paint hueAlphaTrackerPaint; private Paint borderPaint; private Shader valShader; private Shader satShader; private Shader alphaShader; /* * We cache a bitmap of the sat/val panel which is expensive to draw each time. * We can reuse it when the user is sliding the circle picker as long as the hue isn't changed. */ private BitmapCache satValBackgroundCache; /* We cache the hue background to since its also very expensive now. */ private BitmapCache hueBackgroundCache; /* Current values */ private int alpha = 0xff; private float hue = 360f; private float sat = 0f; private float val = 0f; private boolean showAlphaPanel = false; private String alphaSliderText = null; private int sliderTrackerColor = DEFAULT_SLIDER_COLOR; private int borderColor = DEFAULT_BORDER_COLOR; /** * Minimum required padding. The offset from the * edge we must have or else the finger tracker will * get clipped when it's drawn outside of the view. */ private int mRequiredPadding; /** * The Rect in which we are allowed to draw. * Trackers can extend outside slightly, * due to the required padding we have set. */ private Rect drawingRect; private Rect satValRect; private Rect hueRect; private Rect alphaRect; private Point startTouchPoint = null; private AlphaPatternDrawable alphaPatternDrawable; private OnColorChangedListener onColorChangedListener; public ColorPickerView(Context context) { this(context, null); } public ColorPickerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ColorPickerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } @Override public Parcelable onSaveInstanceState() { Bundle state = new Bundle(); state.putParcelable("instanceState", super.onSaveInstanceState()); state.putInt("alpha", alpha); state.putFloat("hue", hue); state.putFloat("sat", sat); state.putFloat("val", val); state.putBoolean("show_alpha", showAlphaPanel); state.putString("alpha_text", alphaSliderText); return state; } @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; alpha = bundle.getInt("alpha"); hue = bundle.getFloat("hue"); sat = bundle.getFloat("sat"); val = bundle.getFloat("val"); showAlphaPanel = bundle.getBoolean("show_alpha"); alphaSliderText = bundle.getString("alpha_text"); state = bundle.getParcelable("instanceState"); } super.onRestoreInstanceState(state); } private void init(Context context, AttributeSet attrs) { //Load those if set in xml resource file. TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPickerView); showAlphaPanel = a.getBoolean(R.styleable.ColorPickerView_cpv_alphaChannelVisible, false); alphaSliderText = a.getString(R.styleable.ColorPickerView_cpv_alphaChannelText); sliderTrackerColor = a.getColor(R.styleable.ColorPickerView_cpv_sliderColor, 0xFFBDBDBD); borderColor = a.getColor(R.styleable.ColorPickerView_cpv_borderColor, 0xFF6E6E6E); a.recycle(); applyThemeColors(context); huePanelWidthPx = DrawingUtils.dpToPx(getContext(), HUE_PANEL_WDITH_DP); alphaPanelHeightPx = DrawingUtils.dpToPx(getContext(), ALPHA_PANEL_HEIGH_DP); panelSpacingPx = DrawingUtils.dpToPx(getContext(), PANEL_SPACING_DP); circleTrackerRadiusPx = DrawingUtils.dpToPx(getContext(), CIRCLE_TRACKER_RADIUS_DP); sliderTrackerSizePx = DrawingUtils.dpToPx(getContext(), SLIDER_TRACKER_SIZE_DP); sliderTrackerOffsetPx = DrawingUtils.dpToPx(getContext(), SLIDER_TRACKER_OFFSET_DP); mRequiredPadding = getResources().getDimensionPixelSize(R.dimen.cpv_required_padding); initPaintTools(); //Needed for receiving trackball motion events. setFocusable(true); setFocusableInTouchMode(true); } private void applyThemeColors(Context c) { // If no specific border/slider color has been // set we take the default secondary text color // as border/slider color. Thus it will adopt // to theme changes automatically. final TypedValue value = new TypedValue(); TypedArray a = c.obtainStyledAttributes(value.data, new int[]{android.R.attr.textColorSecondary}); if (borderColor == DEFAULT_BORDER_COLOR) { borderColor = a.getColor(0, DEFAULT_BORDER_COLOR); } if (sliderTrackerColor == DEFAULT_SLIDER_COLOR) { sliderTrackerColor = a.getColor(0, DEFAULT_SLIDER_COLOR); } a.recycle(); } private void initPaintTools() { satValPaint = new Paint(); satValTrackerPaint = new Paint(); hueAlphaTrackerPaint = new Paint(); alphaPaint = new Paint(); alphaTextPaint = new Paint(); borderPaint = new Paint(); satValTrackerPaint.setStyle(Style.STROKE); satValTrackerPaint.setStrokeWidth(DrawingUtils.dpToPx(getContext(), 2)); satValTrackerPaint.setAntiAlias(true); hueAlphaTrackerPaint.setColor(sliderTrackerColor); hueAlphaTrackerPaint.setStyle(Style.STROKE); hueAlphaTrackerPaint.setStrokeWidth(DrawingUtils.dpToPx(getContext(), 2)); hueAlphaTrackerPaint.setAntiAlias(true); alphaTextPaint.setColor(0xff1c1c1c); alphaTextPaint.setTextSize(DrawingUtils.dpToPx(getContext(), 14)); alphaTextPaint.setAntiAlias(true); alphaTextPaint.setTextAlign(Align.CENTER); alphaTextPaint.setFakeBoldText(true); } @Override protected void onDraw(Canvas canvas) { if (drawingRect.width() <= 0 || drawingRect.height() <= 0) { return; } drawSatValPanel(canvas); drawHuePanel(canvas); drawAlphaPanel(canvas); } private void drawSatValPanel(Canvas canvas) { final Rect rect = satValRect; if (BORDER_WIDTH_PX > 0) { borderPaint.setColor(borderColor); canvas.drawRect(drawingRect.left, drawingRect.top, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, borderPaint); } if (valShader == null) { //Black gradient has either not been created or the view has been resized. valShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, 0xffffffff, 0xff000000, TileMode.CLAMP); } //If the hue has changed we need to recreate the cache. if (satValBackgroundCache == null || satValBackgroundCache.value != hue) { if (satValBackgroundCache == null) { satValBackgroundCache = new BitmapCache(); } //We create our bitmap in the cache if it doesn't exist. if (satValBackgroundCache.bitmap == null) { satValBackgroundCache.bitmap = Bitmap.createBitmap(rect.width(), rect.height(), Config.ARGB_8888); } //We create the canvas once so we can draw on our bitmap and the hold on to it. if (satValBackgroundCache.canvas == null) { satValBackgroundCache.canvas = new Canvas(satValBackgroundCache.bitmap); } int rgb = Color.HSVToColor(new float[]{hue, 1f, 1f}); satShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, 0xffffffff, rgb, TileMode.CLAMP); ComposeShader mShader = new ComposeShader(valShader, satShader, PorterDuff.Mode.MULTIPLY); satValPaint.setShader(mShader); // Finally we draw on our canvas, the result will be // stored in our bitmap which is already in the cache. // Since this is drawn on a canvas not rendered on // screen it will automatically not be using the // hardware acceleration. And this was the code that // wasn't supported by hardware acceleration which mean // there is no need to turn it of anymore. The rest of // the view will still be hw accelerated. satValBackgroundCache.canvas.drawRect(0, 0, satValBackgroundCache.bitmap.getWidth(), satValBackgroundCache.bitmap.getHeight(), satValPaint); //We set the hue value in our cache to which hue it was drawn with, //then we know that if it hasn't changed we can reuse our cached bitmap. satValBackgroundCache.value = hue; } // We draw our bitmap from the cached, if the hue has changed // then it was just recreated otherwise the old one will be used. canvas.drawBitmap(satValBackgroundCache.bitmap, null, rect, null); Point p = satValToPoint(sat, val); satValTrackerPaint.setColor(0xff000000); canvas.drawCircle(p.x, p.y, circleTrackerRadiusPx - DrawingUtils.dpToPx(getContext(), 1), satValTrackerPaint); satValTrackerPaint.setColor(0xffdddddd); canvas.drawCircle(p.x, p.y, circleTrackerRadiusPx, satValTrackerPaint); } private void drawHuePanel(Canvas canvas) { final Rect rect = hueRect; if (BORDER_WIDTH_PX > 0) { borderPaint.setColor(borderColor); canvas.drawRect(rect.left - BORDER_WIDTH_PX, rect.top - BORDER_WIDTH_PX, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, borderPaint); } if (hueBackgroundCache == null) { hueBackgroundCache = new BitmapCache(); hueBackgroundCache.bitmap = Bitmap.createBitmap(rect.width(), rect.height(), Config.ARGB_8888); hueBackgroundCache.canvas = new Canvas(hueBackgroundCache.bitmap); int[] hueColors = new int[(int) (rect.height() + 0.5f)]; // Generate array of all colors, will be drawn as individual lines. float h = 360f; for (int i = 0; i < hueColors.length; i++) { hueColors[i] = Color.HSVToColor(new float[]{h, 1f, 1f}); h -= 360f / hueColors.length; } // Time to draw the hue color gradient, // its drawn as individual lines which // will be quite many when the resolution is high // and/or the panel is large. Paint linePaint = new Paint(); linePaint.setStrokeWidth(0); for (int i = 0; i < hueColors.length; i++) { linePaint.setColor(hueColors[i]); hueBackgroundCache.canvas.drawLine(0, i, hueBackgroundCache.bitmap.getWidth(), i, linePaint); } } canvas.drawBitmap(hueBackgroundCache.bitmap, null, rect, null); Point p = hueToPoint(hue); RectF r = new RectF(); r.left = rect.left - sliderTrackerOffsetPx; r.right = rect.right + sliderTrackerOffsetPx; r.top = p.y - (sliderTrackerSizePx / 2); r.bottom = p.y + (sliderTrackerSizePx / 2); canvas.drawRoundRect(r, 2, 2, hueAlphaTrackerPaint); } private void drawAlphaPanel(Canvas canvas) { /* * Will be drawn with hw acceleration, very fast. * Also the AlphaPatternDrawable is backed by a bitmap * generated only once if the size does not change. */ if (!showAlphaPanel || alphaRect == null || alphaPatternDrawable == null) return; final Rect rect = alphaRect; if (BORDER_WIDTH_PX > 0) { borderPaint.setColor(borderColor); canvas.drawRect(rect.left - BORDER_WIDTH_PX, rect.top - BORDER_WIDTH_PX, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, borderPaint); } alphaPatternDrawable.draw(canvas); float[] hsv = new float[]{hue, sat, val}; int color = Color.HSVToColor(hsv); int acolor = Color.HSVToColor(0, hsv); alphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, color, acolor, TileMode.CLAMP); alphaPaint.setShader(alphaShader); canvas.drawRect(rect, alphaPaint); if (alphaSliderText != null && !alphaSliderText.equals("")) { canvas.drawText(alphaSliderText, rect.centerX(), rect.centerY() + DrawingUtils.dpToPx(getContext(), 4), alphaTextPaint); } Point p = alphaToPoint(alpha); RectF r = new RectF(); r.left = p.x - (sliderTrackerSizePx / 2); r.right = p.x + (sliderTrackerSizePx / 2); r.top = rect.top - sliderTrackerOffsetPx; r.bottom = rect.bottom + sliderTrackerOffsetPx; canvas.drawRoundRect(r, 2, 2, hueAlphaTrackerPaint); } private Point hueToPoint(float hue) { final Rect rect = hueRect; final float height = rect.height(); Point p = new Point(); p.y = (int) (height - (hue * height / 360f) + rect.top); p.x = rect.left; return p; } private Point satValToPoint(float sat, float val) { final Rect rect = satValRect; final float height = rect.height(); final float width = rect.width(); Point p = new Point(); p.x = (int) (sat * width + rect.left); p.y = (int) ((1f - val) * height + rect.top); return p; } private Point alphaToPoint(int alpha) { final Rect rect = alphaRect; final float width = rect.width(); Point p = new Point(); p.x = (int) (width - (alpha * width / 0xff) + rect.left); p.y = rect.top; return p; } private float[] pointToSatVal(float x, float y) { final Rect rect = satValRect; float[] result = new float[2]; float width = rect.width(); float height = rect.height(); if (x < rect.left) { x = 0f; } else if (x > rect.right) { x = width; } else { x = x - rect.left; } if (y < rect.top) { y = 0f; } else if (y > rect.bottom) { y = height; } else { y = y - rect.top; } result[0] = 1.f / width * x; result[1] = 1.f - (1.f / height * y); return result; } private float pointToHue(float y) { final Rect rect = hueRect; float height = rect.height(); if (y < rect.top) { y = 0f; } else if (y > rect.bottom) { y = height; } else { y = y - rect.top; } float hue = 360f - (y * 360f / height); return hue; } private int pointToAlpha(int x) { final Rect rect = alphaRect; final int width = rect.width(); if (x < rect.left) { x = 0; } else if (x > rect.right) { x = width; } else { x = x - rect.left; } return 0xff - (x * 0xff / width); } @Override public boolean onTouchEvent(MotionEvent event) { boolean update = false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startTouchPoint = new Point((int) event.getX(), (int) event.getY()); update = moveTrackersIfNeeded(event); break; case MotionEvent.ACTION_MOVE: update = moveTrackersIfNeeded(event); break; case MotionEvent.ACTION_UP: startTouchPoint = null; update = moveTrackersIfNeeded(event); break; } if (update) { if (onColorChangedListener != null) { onColorChangedListener.onColorChanged(Color.HSVToColor(alpha, new float[]{hue, sat, val})); } invalidate(); return true; } return super.onTouchEvent(event); } private boolean moveTrackersIfNeeded(MotionEvent event) { if (startTouchPoint == null) { return false; } boolean update = false; int startX = startTouchPoint.x; int startY = startTouchPoint.y; if (hueRect.contains(startX, startY)) { hue = pointToHue(event.getY()); update = true; } else if (satValRect.contains(startX, startY)) { float[] result = pointToSatVal(event.getX(), event.getY()); sat = result[0]; val = result[1]; update = true; } else if (alphaRect != null && alphaRect.contains(startX, startY)) { alpha = pointToAlpha((int) event.getX()); update = true; } return update; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int finalWidth; int finalHeight; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthAllowed = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); int heightAllowed = MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingTop(); if (widthMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.EXACTLY) { //A exact value has been set in either direction, we need to stay within this size. if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) { //The with has been specified exactly, we need to adopt the height to fit. int h = (widthAllowed - panelSpacingPx - huePanelWidthPx); if (showAlphaPanel) { h += panelSpacingPx + alphaPanelHeightPx; } if (h > heightAllowed) { //We can't fit the view in this container, set the size to whatever was allowed. finalHeight = heightAllowed; } else { finalHeight = h; } finalWidth = widthAllowed; } else if (heightMode == MeasureSpec.EXACTLY && widthMode != MeasureSpec.EXACTLY) { //The height has been specified exactly, we need to stay within this height and adopt the width. int w = (heightAllowed + panelSpacingPx + huePanelWidthPx); if (showAlphaPanel) { w -= (panelSpacingPx + alphaPanelHeightPx); } if (w > widthAllowed) { //we can't fit within this container, set the size to whatever was allowed. finalWidth = widthAllowed; } else { finalWidth = w; } finalHeight = heightAllowed; } else { //If we get here the dev has set the width and height to exact sizes. For example match_parent or 300dp. //This will mean that the sat/val panel will not be square but it doesn't matter. It will work anyway. //In all other senarios our goal is to make that panel square. //We set the sizes to exactly what we were told. finalWidth = widthAllowed; finalHeight = heightAllowed; } } else { //If no exact size has been set we try to make our view as big as possible //within the allowed space. //Calculate the needed width to layout using max allowed height. int widthNeeded = (heightAllowed + panelSpacingPx + huePanelWidthPx); //Calculate the needed height to layout using max allowed width. int heightNeeded = (widthAllowed - panelSpacingPx - huePanelWidthPx); if (showAlphaPanel) { widthNeeded -= (panelSpacingPx + alphaPanelHeightPx); heightNeeded += panelSpacingPx + alphaPanelHeightPx; } boolean widthOk = false; boolean heightOk = false; if (widthNeeded <= widthAllowed) { widthOk = true; } if (heightNeeded <= heightAllowed) { heightOk = true; } if (widthOk && heightOk) { finalWidth = widthAllowed; finalHeight = heightNeeded; } else if (!heightOk && widthOk) { finalHeight = heightAllowed; finalWidth = widthNeeded; } else if (!widthOk && heightOk) { finalHeight = heightNeeded; finalWidth = widthAllowed; } else { finalHeight = heightAllowed; finalWidth = widthAllowed; } } setMeasuredDimension(finalWidth + getPaddingLeft() + getPaddingRight(), finalHeight + getPaddingTop() + getPaddingBottom()); } private int getPreferredWidth() { //Our preferred width and height is 200dp for the square sat / val rectangle. int width = DrawingUtils.dpToPx(getContext(), 200); return (width + huePanelWidthPx + panelSpacingPx); } private int getPreferredHeight() { int height = DrawingUtils.dpToPx(getContext(), 200); if (showAlphaPanel) { height += panelSpacingPx + alphaPanelHeightPx; } return height; } @Override public int getPaddingTop() { return Math.max(super.getPaddingTop(), mRequiredPadding); } @Override public int getPaddingBottom() { return Math.max(super.getPaddingBottom(), mRequiredPadding); } @Override public int getPaddingLeft() { return Math.max(super.getPaddingLeft(), mRequiredPadding); } @Override public int getPaddingRight() { return Math.max(super.getPaddingRight(), mRequiredPadding); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); drawingRect = new Rect(); drawingRect.left = getPaddingLeft(); drawingRect.right = w - getPaddingRight(); drawingRect.top = getPaddingTop(); drawingRect.bottom = h - getPaddingBottom(); //The need to be recreated because they depend on the size of the view. valShader = null; satShader = null; alphaShader = null; // Clear those bitmap caches since the size may have changed. satValBackgroundCache = null; hueBackgroundCache = null; setUpSatValRect(); setUpHueRect(); setUpAlphaRect(); } private void setUpSatValRect() { //Calculate the size for the big color rectangle. final Rect dRect = drawingRect; int left = dRect.left + BORDER_WIDTH_PX; int top = dRect.top + BORDER_WIDTH_PX; int bottom = dRect.bottom - BORDER_WIDTH_PX; int right = dRect.right - BORDER_WIDTH_PX - panelSpacingPx - huePanelWidthPx; if (showAlphaPanel) { bottom -= (alphaPanelHeightPx + panelSpacingPx); } satValRect = new Rect(left, top, right, bottom); } private void setUpHueRect() { //Calculate the size for the hue slider on the left. final Rect dRect = drawingRect; int left = dRect.right - huePanelWidthPx + BORDER_WIDTH_PX; int top = dRect.top + BORDER_WIDTH_PX; int bottom = dRect.bottom - BORDER_WIDTH_PX - (showAlphaPanel ? (panelSpacingPx + alphaPanelHeightPx) : 0); int right = dRect.right - BORDER_WIDTH_PX; hueRect = new Rect(left, top, right, bottom); } private void setUpAlphaRect() { if (!showAlphaPanel) return; final Rect dRect = drawingRect; int left = dRect.left + BORDER_WIDTH_PX; int top = dRect.bottom - alphaPanelHeightPx + BORDER_WIDTH_PX; int bottom = dRect.bottom - BORDER_WIDTH_PX; int right = dRect.right - BORDER_WIDTH_PX; alphaRect = new Rect(left, top, right, bottom); alphaPatternDrawable = new AlphaPatternDrawable(DrawingUtils.dpToPx(getContext(), 4)); alphaPatternDrawable.setBounds(Math.round(alphaRect.left), Math.round(alphaRect.top), Math.round(alphaRect.right), Math.round(alphaRect.bottom)); } /** * Set a OnColorChangedListener to get notified when the color * selected by the user has changed. * * @param listener the listener */ public void setOnColorChangedListener(OnColorChangedListener listener) { onColorChangedListener = listener; } /** * Get the current color this view is showing. * * @return the current color. */ public int getColor() { return Color.HSVToColor(alpha, new float[]{hue, sat, val}); } /** * Set the color the view should show. * * @param color The color that should be selected. #argb */ public void setColor(int color) { setColor(color, false); } /** * Set the color this view should show. * * @param color The color that should be selected. #argb * @param callback If you want to get a callback to your OnColorChangedListener. */ public void setColor(int color, boolean callback) { int alpha = Color.alpha(color); int red = Color.red(color); int blue = Color.blue(color); int green = Color.green(color); float[] hsv = new float[3]; Color.RGBToHSV(red, green, blue, hsv); this.alpha = alpha; hue = hsv[0]; sat = hsv[1]; val = hsv[2]; if (callback && onColorChangedListener != null) { onColorChangedListener.onColorChanged(Color.HSVToColor(this.alpha, new float[]{hue, sat, val})); } invalidate(); } /** * Set if the user is allowed to adjust the alpha panel. Default is false. * If it is set to false no alpha will be set. * * @param visible {@code true} to show the alpha slider */ public void setAlphaSliderVisible(boolean visible) { if (showAlphaPanel != visible) { showAlphaPanel = visible; /* * Force recreation. */ valShader = null; satShader = null; alphaShader = null; hueBackgroundCache = null; satValBackgroundCache = null; requestLayout(); } } /** * Get color of the tracker slider on the hue and alpha panel. * * @return the color value */ public int getSliderTrackerColor() { return sliderTrackerColor; } /** * Set the color of the tracker slider on the hue and alpha panel. * * @param color a color value */ public void setSliderTrackerColor(int color) { sliderTrackerColor = color; hueAlphaTrackerPaint.setColor(sliderTrackerColor); invalidate(); } /** * Get the color of the border surrounding all panels. */ public int getBorderColor() { return borderColor; } /** * Set the color of the border surrounding all panels. * * @param color a color value */ public void setBorderColor(int color) { borderColor = color; invalidate(); } /** * Get the current value of the text * that will be shown in the alpha * slider. * * @return the slider text */ public String getAlphaSliderText() { return alphaSliderText; } /** * Set the text that should be shown in the * alpha slider. Set to null to disable text. * * @param res string resource id. */ public void setAlphaSliderText(int res) { String text = getContext().getString(res); setAlphaSliderText(text); } /** * Set the text that should be shown in the * alpha slider. Set to null to disable text. * * @param text Text that should be shown. */ public void setAlphaSliderText(String text) { alphaSliderText = text; invalidate(); } public interface OnColorChangedListener { void onColorChanged(int newColor); } private class BitmapCache { public Canvas canvas; public Bitmap bitmap; public float value; } }