Skip to content

Instantly share code, notes, and snippets.

@garymabin
Last active February 14, 2017 07:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garymabin/3249ea43d6bb7b27fb2b6ea265439259 to your computer and use it in GitHub Desktop.
Save garymabin/3249ea43d6bb7b27fb2b6ea265439259 to your computer and use it in GitHub Desktop.
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import static android.content.DialogInterface.BUTTON_NEGATIVE;
import static android.content.DialogInterface.BUTTON_NEUTRAL;
import static android.content.DialogInterface.BUTTON_POSITIVE;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
final AlertDialog dialog = new AlertDialog(this);
HSVColorPickerView.OnColorSelectedListener listener = new HSVColorPickerView.OnColorSelectedListener() ;
DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
switch ( which ) {
case BUTTON_NEGATIVE:
dialog.dismiss();
break;
case BUTTON_NEUTRAL:
dialog.dismiss();
listener.colorSelected( -1 );
break;
case BUTTON_POSITIVE:
listener.colorSelected( selectedColor );
break;
}
}
};
dialog.setButton( BUTTON_NEGATIVE, getString( android.R.string.cancel ), clickListener );
dialog.setButton( BUTTON_POSITIVE, cgetString( android.R.string.ok ), clickListener );
HSVColorPickerView view = getLayoutInflater().inflate(R.id.your_custom_layout, null);
dialog.setView(view);
super.onCreate(savedInstanceState);
}
}
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
public class HSVColorPickerView extends View {
private static final int PADDING_DP = 20;
private static final int CONTROL_SPACING_DP = 20;
private static final int SELECTED_COLOR_HEIGHT_DP = 50;
private static final int BORDER_DP = 1;
private static final int BORDER_COLOR = Color.BLACK;
private OnColorSelectedListener listener;
private int selectedColor;
public interface OnColorSelectedListener {
/**
* @param color The color code selected, or null if no color. No color is only
* possible if {@link com.buzzingandroid.ui.HSVColorPickerDialog#setNoColorButton(int) setNoColorButton()}
* has been called on the dialog before showing it
*/
public void colorSelected(Integer color);
}
public HSVColorPickerView(Context context) {
super(context);
this.selectedColor = initialColor;
colorWheel = new HSVColorWheel( context );
valueSlider = new HSVValueSlider( context );
int padding = (int) (context.getResources().getDisplayMetrics().density * PADDING_DP);
int borderSize = (int) (context.getResources().getDisplayMetrics().density * BORDER_DP);
RelativeLayout layout = new RelativeLayout( context );
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT );
lp.bottomMargin = (int) (context.getResources().getDisplayMetrics().density * CONTROL_SPACING_DP);
colorWheel.setListener( new OnColorSelectedListener() {
public void colorSelected(Integer color) {
valueSlider.setColor( color, true );
}
} );
colorWheel.setColor( initialColor );
colorWheel.setId( 1 );
layout.addView( colorWheel, lp );
int selectedColorHeight = (int) (context.getResources().getDisplayMetrics().density * SELECTED_COLOR_HEIGHT_DP);
FrameLayout valueSliderBorder = new FrameLayout( context );
valueSliderBorder.setBackgroundColor( BORDER_COLOR );
valueSliderBorder.setPadding( borderSize, borderSize, borderSize, borderSize );
valueSliderBorder.setId( 2 );
lp = new RelativeLayout.LayoutParams( LayoutParams.MATCH_PARENT, selectedColorHeight + 2 * borderSize );
lp.bottomMargin = (int) (context.getResources().getDisplayMetrics().density * CONTROL_SPACING_DP);
lp.addRule( RelativeLayout.BELOW, 1 );
layout.addView( valueSliderBorder, lp );
valueSlider.setColor( initialColor, false );
valueSlider.setListener( new OnColorSelectedListener() {
@Override
public void colorSelected(Integer color) {
selectedColor = color;
selectedColorView.setBackgroundColor( color );
}
});
valueSliderBorder.addView( valueSlider );
FrameLayout selectedColorborder = new FrameLayout( context );
selectedColorborder.setBackgroundColor( BORDER_COLOR );
lp = new RelativeLayout.LayoutParams( LayoutParams.MATCH_PARENT, selectedColorHeight + 2 * borderSize );
selectedColorborder.setPadding( borderSize, borderSize, borderSize, borderSize );
lp.addRule( RelativeLayout.BELOW, 2 );
layout.addView( selectedColorborder, lp );
}
private HSVColorWheel colorWheel;
private HSVValueSlider valueSlider;
private View selectedColorView;
public void setOnColorChangeListener(final OnColorSelectedListener listener) {
this.listener = listener;
}
public void setSelectedColor(int color) {
this.selectedColor = color;
}
private static class HSVColorWheel extends View {
private static final float SCALE = 2f;
private static final float FADE_OUT_FRACTION = 0.03f;
private static final int POINTER_LINE_WIDTH_DP = 2;
private static final int POINTER_LENGTH_DP = 10;
private final Context context;
private OnColorSelectedListener listener;
public HSVColorWheel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
init();
}
public HSVColorWheel(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public HSVColorWheel(Context context) {
super(context);
this.context = context;
init();
}
private int scale;
private int pointerLength;
private int innerPadding;
private Paint pointerPaint = new Paint();
private void init() {
float density = context.getResources().getDisplayMetrics().density;
scale = (int) (density * SCALE);
pointerLength = (int) (density * POINTER_LENGTH_DP );
pointerPaint.setStrokeWidth( (int) (density * POINTER_LINE_WIDTH_DP ) );
innerPadding = pointerLength / 2;
}
public void setListener( OnColorSelectedListener listener ) {
this.listener = listener;
}
float[] colorHsv = { 0f, 0f, 1f };
public void setColor( int color ) {
Color.colorToHSV(color, colorHsv);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if ( bitmap != null ) {
canvas.drawBitmap(bitmap, null, rect, null);
float hueInPiInterval = colorHsv[0] / 180f * (float)Math.PI;
selectedPoint.x = rect.left + (int) (-FloatMath.cos( hueInPiInterval ) * colorHsv[1] * innerCircleRadius + fullCircleRadius);
selectedPoint.y = rect.top + (int) (-FloatMath.sin( hueInPiInterval ) * colorHsv[1] * innerCircleRadius + fullCircleRadius);
canvas.drawLine( selectedPoint.x - pointerLength, selectedPoint.y, selectedPoint.x + pointerLength, selectedPoint.y, pointerPaint );
canvas.drawLine( selectedPoint.x, selectedPoint.y - pointerLength, selectedPoint.x, selectedPoint.y + pointerLength, pointerPaint );
}
}
private Rect rect;
private Bitmap bitmap;
private int[] pixels;
private float innerCircleRadius;
private float fullCircleRadius;
private int scaledWidth;
private int scaledHeight;
private int[] scaledPixels;
private float scaledInnerCircleRadius;
private float scaledFullCircleRadius;
private float scaledFadeOutSize;
private Point selectedPoint = new Point();
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
rect = new Rect( innerPadding, innerPadding, w - innerPadding, h - innerPadding );
bitmap = Bitmap.createBitmap( w - 2 * innerPadding, h - 2 * innerPadding, Config.ARGB_8888 );
fullCircleRadius = Math.min( rect.width(), rect.height() ) / 2;
innerCircleRadius = fullCircleRadius * ( 1 - FADE_OUT_FRACTION );
scaledWidth = rect.width() / scale;
scaledHeight = rect.height() / scale;
scaledFullCircleRadius = Math.min( scaledWidth, scaledHeight ) / 2;
scaledInnerCircleRadius = scaledFullCircleRadius * ( 1 - FADE_OUT_FRACTION );
scaledFadeOutSize = scaledFullCircleRadius - scaledInnerCircleRadius;
scaledPixels = new int[ scaledWidth * scaledHeight ];
pixels = new int[ rect.width() * rect.height() ];
createBitmap();
}
private void createBitmap() {
int w = rect.width();
int h = rect.height();
float[] hsv = new float[] { 0f, 0f, 1f };
int alpha = 255;
int x = (int) -scaledFullCircleRadius, y = (int) -scaledFullCircleRadius;
for ( int i = 0; i < scaledPixels.length; i++ ) {
if ( i % scaledWidth == 0 ) {
x = (int) -scaledFullCircleRadius;
y++;
} else {
x++;
}
double centerDist = Math.sqrt( x*x + y*y );
if ( centerDist <= scaledFullCircleRadius ) {
hsv[ 0 ] = (float) (Math.atan2( y, x ) / Math.PI * 180f) + 180;
hsv[ 1 ] = (float) (centerDist / scaledInnerCircleRadius);
if ( centerDist <= scaledInnerCircleRadius ) {
alpha = 255;
} else {
alpha = 255 - (int) ((centerDist - scaledInnerCircleRadius) / scaledFadeOutSize * 255);
}
scaledPixels[ i ] = Color.HSVToColor( alpha, hsv );
} else {
scaledPixels[ i ] = 0x00000000;
}
}
int scaledX, scaledY;
for( x = 0; x < w; x++ ) {
scaledX = x / scale;
if ( scaledX >= scaledWidth ) scaledX = scaledWidth - 1;
for ( y = 0; y < h; y++ ) {
scaledY = y / scale;
if ( scaledY >= scaledHeight ) scaledY = scaledHeight - 1;
pixels[ x * h + y ] = scaledPixels[ scaledX * scaledHeight + scaledY ];
}
}
bitmap.setPixels( pixels, 0, w, 0, 0, w, h );
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int maxWidth = MeasureSpec.getSize( widthMeasureSpec );
int maxHeight = MeasureSpec.getSize( heightMeasureSpec );
int width, height;
/*
* Make the view quadratic, with height and width equal and as large as possible
*/
width = height = Math.min( maxWidth, maxHeight );
setMeasuredDimension( width, height );
}
public int getColorForPoint( int x, int y, float[] hsv ) {
x -= fullCircleRadius;
y -= fullCircleRadius;
double centerDist = Math.sqrt( x*x + y*y );
hsv[ 0 ] = (float) (Math.atan2( y, x ) / Math.PI * 180f) + 180;
hsv[ 1 ] = Math.max( 0f, Math.min( 1f, (float) (centerDist / innerCircleRadius) ) );
return Color.HSVToColor( hsv );
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch ( action ) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if ( listener != null ) {
listener.colorSelected( getColorForPoint( (int)event.getX(), (int)event.getY(), colorHsv ) );
}
invalidate();
return true;
}
return super.onTouchEvent(event);
}
}
private static class HSVValueSlider extends View {
public HSVValueSlider(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public HSVValueSlider(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HSVValueSlider(Context context) {
super(context);
}
private OnColorSelectedListener listener;
public void setListener( OnColorSelectedListener listener ) {
this.listener = listener;
}
float[] colorHsv = { 0f, 0f, 1f };
public void setColor( int color, boolean keepValue ) {
float oldValue = colorHsv[2];
Color.colorToHSV(color, colorHsv);
if ( keepValue ) {
colorHsv[2] = oldValue;
}
if ( listener != null ) {
listener.colorSelected( Color.HSVToColor( colorHsv ) );
}
createBitmap();
}
@Override
protected void onDraw(Canvas canvas) {
if ( bitmap != null ) {
canvas.drawBitmap(bitmap, srcRect, dstRect, null);
}
}
private Rect srcRect;
private Rect dstRect;
private Bitmap bitmap;
private int[] pixels;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
srcRect = new Rect( 0, 0, w, 1 );
dstRect = new Rect( 0, 0, w, h );
bitmap = Bitmap.createBitmap( w, 1, Config.ARGB_8888 );
pixels = new int[ w ];
createBitmap();
}
private void createBitmap() {
if ( bitmap == null ) {
return;
}
int w = getWidth();
float[] hsv = new float[] { colorHsv[0], colorHsv[1], 1f };
int selectedX = (int) (colorHsv[ 2 ] * w);
float value = 0;
float valueStep = 1f / w;
for( int x = 0; x < w; x++ ) {
value += valueStep;
if ( x >= selectedX - 1 && x <= selectedX + 1 ) {
int intVal = 0xFF - (int)( value * 0xFF );
int color = intVal * 0x010101 + 0xFF000000;
pixels[x] = color;
} else {
hsv[2] = value;
pixels[x] = Color.HSVToColor( hsv );
}
}
bitmap.setPixels( pixels, 0, w, 0, 0, w, 1 );
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch ( action ) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = Math.max( 0, Math.min( bitmap.getWidth() - 1, (int)event.getX() ) );
float value = x / (float)bitmap.getWidth();
if ( colorHsv[2] != value ) {
colorHsv[2] = value;
if ( listener != null ) {
listener.colorSelected( Color.HSVToColor( colorHsv ) );
}
createBitmap();
invalidate();
}
return true;
}
return super.onTouchEvent(event);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment