Build colorpicker as module

This commit is contained in:
M66B 2023-01-19 20:04:52 +01:00
parent ea26bbb360
commit 6aa8dc98ac
33 changed files with 1929 additions and 2 deletions

View File

@ -580,7 +580,8 @@ dependencies {
implementation "io.noties.markwon:html:$markwon_version"
// // https://github.com/QuadFlask/colorpicker
implementation "com.github.QuadFlask:colorpicker:$colorpicker_version"
//implementation "com.github.QuadFlask:colorpicker:$colorpicker_version"
implementation project(':colorpicker')
// https://github.com/EverythingMe/overscroll-decor
// https://search.maven.org/artifact/io.github.everythingme/overscroll-decor-android

1
colorpicker/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

24
colorpicker/build.gradle Normal file
View File

@ -0,0 +1,24 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
buildToolsVersion "29.0.0"
defaultConfig {
minSdkVersion 14
targetSdkVersion 29
versionCode 17
versionName "0.0.15"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
}

17
colorpicker/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/flask/Documents/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,5 @@
<manifest package="com.flask.colorpicker">
<application/>
</manifest>

View File

@ -0,0 +1,54 @@
package com.flask.colorpicker;
import android.graphics.Color;
public class ColorCircle {
private float x, y;
private float[] hsv = new float[3];
private float[] hsvClone;
private int color;
public ColorCircle(float x, float y, float[] hsv) {
set(x, y, hsv);
}
public double sqDist(float x, float y) {
double dx = this.x - x;
double dy = this.y - y;
return dx * dx + dy * dy;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float[] getHsv() {
return hsv;
}
public float[] getHsvWithLightness(float lightness) {
if (hsvClone == null)
hsvClone = hsv.clone();
hsvClone[0] = hsv[0];
hsvClone[1] = hsv[1];
hsvClone[2] = lightness;
return hsvClone;
}
public void set(float x, float y, float[] hsv) {
this.x = x;
this.y = y;
this.hsv[0] = hsv[0];
this.hsv[1] = hsv[1];
this.hsv[2] = hsv[2];
this.color = Color.HSVToColor(this.hsv);
}
public int getColor() {
return color;
}
}

View File

@ -0,0 +1,39 @@
package com.flask.colorpicker;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import com.flask.colorpicker.builder.PaintBuilder;
public class ColorCircleDrawable extends ColorDrawable {
private float strokeWidth;
private Paint strokePaint = PaintBuilder.newPaint().style(Paint.Style.STROKE).stroke(strokeWidth).color(0xff9e9e9e).build();
private Paint fillPaint = PaintBuilder.newPaint().style(Paint.Style.FILL).color(0).build();
private Paint fillBackPaint = PaintBuilder.newPaint().shader(PaintBuilder.createAlphaPatternShader(26)).build();
public ColorCircleDrawable(int color) {
super(color);
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0);
int width = canvas.getWidth();
float radius = width / 2f;
strokeWidth = radius / 8f;
this.strokePaint.setStrokeWidth(strokeWidth);
this.fillPaint.setColor(getColor());
canvas.drawCircle(radius, radius, radius - strokeWidth, fillBackPaint);
canvas.drawCircle(radius, radius, radius - strokeWidth, fillPaint);
canvas.drawCircle(radius, radius, radius - strokeWidth, strokePaint);
}
@Override
public void setColor(int color) {
super.setColor(color);
invalidateSelf();
}
}

View File

@ -0,0 +1,155 @@
package com.flask.colorpicker;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.preference.Preference;
import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import com.flask.colorpicker.builder.ColorPickerClickListener;
import com.flask.colorpicker.builder.ColorPickerDialogBuilder;
public class ColorPickerPreference extends Preference {
protected boolean alphaSlider;
protected boolean lightSlider;
protected boolean border;
protected int selectedColor = 0;
protected ColorPickerView.WHEEL_TYPE wheelType;
protected int density;
private boolean pickerColorEdit;
private String pickerTitle;
private String pickerButtonCancel;
private String pickerButtonOk;
protected ImageView colorIndicator;
public ColorPickerPreference(Context context) {
super(context);
}
public ColorPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
initWith(context, attrs);
}
public ColorPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initWith(context, attrs);
}
private void initWith(Context context, AttributeSet attrs) {
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerPreference);
try {
alphaSlider = typedArray.getBoolean(R.styleable.ColorPickerPreference_alphaSlider, false);
lightSlider = typedArray.getBoolean(R.styleable.ColorPickerPreference_lightnessSlider, false);
border = typedArray.getBoolean(R.styleable.ColorPickerPreference_border, true);
density = typedArray.getInt(R.styleable.ColorPickerPreference_density, 8);
wheelType = ColorPickerView.WHEEL_TYPE.indexOf(typedArray.getInt(R.styleable.ColorPickerPreference_wheelType, 0));
selectedColor = typedArray.getInt(R.styleable.ColorPickerPreference_initialColor, 0xffffffff);
pickerColorEdit = typedArray.getBoolean(R.styleable.ColorPickerPreference_pickerColorEdit, true);
pickerTitle = typedArray.getString(R.styleable.ColorPickerPreference_pickerTitle);
if (pickerTitle==null)
pickerTitle = "Choose color";
pickerButtonCancel = typedArray.getString(R.styleable.ColorPickerPreference_pickerButtonCancel);
if (pickerButtonCancel==null)
pickerButtonCancel = "cancel";
pickerButtonOk = typedArray.getString(R.styleable.ColorPickerPreference_pickerButtonOk);
if (pickerButtonOk==null)
pickerButtonOk = "ok";
} finally {
typedArray.recycle();
}
setWidgetLayoutResource(R.layout.color_widget);
}
@Override
protected void onBindView(@NonNull View view) {
super.onBindView(view);
int tmpColor = isEnabled()
? selectedColor
: darken(selectedColor, .5f);
colorIndicator = (ImageView) view.findViewById(R.id.color_indicator);
ColorCircleDrawable colorChoiceDrawable = null;
Drawable currentDrawable = colorIndicator.getDrawable();
if (currentDrawable != null && currentDrawable instanceof ColorCircleDrawable)
colorChoiceDrawable = (ColorCircleDrawable) currentDrawable;
if (colorChoiceDrawable == null)
colorChoiceDrawable = new ColorCircleDrawable(tmpColor);
colorIndicator.setImageDrawable(colorChoiceDrawable);
}
public void setValue(int value) {
if (callChangeListener(value)) {
selectedColor = value;
persistInt(value);
notifyChanged();
}
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedInt(0) : (Integer) defaultValue);
}
@Override
protected void onClick() {
ColorPickerDialogBuilder builder = ColorPickerDialogBuilder
.with(getContext())
.setTitle(pickerTitle)
.initialColor(selectedColor)
.showBorder(border)
.wheelType(wheelType)
.density(density)
.showColorEdit(pickerColorEdit)
.setPositiveButton(pickerButtonOk, new ColorPickerClickListener() {
@Override
public void onClick(DialogInterface dialog, int selectedColorFromPicker, Integer[] allColors) {
setValue(selectedColorFromPicker);
}
})
.setNegativeButton(pickerButtonCancel, null);
if (!alphaSlider && !lightSlider) builder.noSliders();
else if (!alphaSlider) builder.lightnessSliderOnly();
else if (!lightSlider) builder.alphaSliderOnly();
builder
.build()
.show();
}
public static int darken(int color, float factor) {
int a = Color.alpha(color);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
return Color.argb(a,
Math.max((int)(r * factor), 0),
Math.max((int)(g * factor), 0),
Math.max((int)(b * factor), 0));
}
}

View File

@ -0,0 +1,572 @@
package com.flask.colorpicker;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.flask.colorpicker.builder.ColorWheelRendererBuilder;
import com.flask.colorpicker.builder.PaintBuilder;
import com.flask.colorpicker.renderer.ColorWheelRenderOption;
import com.flask.colorpicker.renderer.ColorWheelRenderer;
import com.flask.colorpicker.slider.AlphaSlider;
import com.flask.colorpicker.slider.LightnessSlider;
import java.util.ArrayList;
public class ColorPickerView extends View {
private static final float STROKE_RATIO = 1.5f;
private Bitmap colorWheel;
private Canvas colorWheelCanvas;
private Bitmap currentColor;
private Canvas currentColorCanvas;
private boolean showBorder;
private int density = 8;
private float lightness = 1;
private float alpha = 1;
private int backgroundColor = 0x00000000;
private Integer initialColors[] = new Integer[]{null, null, null, null, null};
private int colorSelection = 0;
private Integer initialColor;
private Integer pickerColorEditTextColor;
private Paint colorWheelFill = PaintBuilder.newPaint().color(0).build();
private Paint selectorStroke = PaintBuilder.newPaint().color(0).build();
private Paint alphaPatternPaint = PaintBuilder.newPaint().build();
private ColorCircle currentColorCircle;
private ArrayList<OnColorChangedListener> colorChangedListeners = new ArrayList<>();
private ArrayList<OnColorSelectedListener> listeners = new ArrayList<>();
private LightnessSlider lightnessSlider;
private AlphaSlider alphaSlider;
private EditText colorEdit;
private TextWatcher colorTextChange = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
int color = Color.parseColor(s.toString());
// set the color without changing the edit text preventing stack overflow
setColor(color, false);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void afterTextChanged(Editable s) {
}
};
private LinearLayout colorPreview;
private ColorWheelRenderer renderer;
private int alphaSliderViewId, lightnessSliderViewId;
public ColorPickerView(Context context) {
super(context);
initWith(context, null);
}
public ColorPickerView(Context context, AttributeSet attrs) {
super(context, attrs);
initWith(context, attrs);
}
public ColorPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initWith(context, attrs);
}
@TargetApi(21)
public ColorPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initWith(context, attrs);
}
private void initWith(Context context, AttributeSet attrs) {
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerPreference);
density = typedArray.getInt(R.styleable.ColorPickerPreference_density, 10);
initialColor = typedArray.getInt(R.styleable.ColorPickerPreference_initialColor, 0xffffffff);
pickerColorEditTextColor = typedArray.getInt(R.styleable.ColorPickerPreference_pickerColorEditTextColor, 0xffffffff);
WHEEL_TYPE wheelType = WHEEL_TYPE.indexOf(typedArray.getInt(R.styleable.ColorPickerPreference_wheelType, 0));
ColorWheelRenderer renderer = ColorWheelRendererBuilder.getRenderer(wheelType);
alphaSliderViewId = typedArray.getResourceId(R.styleable.ColorPickerPreference_alphaSliderView, 0);
lightnessSliderViewId = typedArray.getResourceId(R.styleable.ColorPickerPreference_lightnessSliderView, 0);
setRenderer(renderer);
setDensity(density);
setInitialColor(initialColor, true);
typedArray.recycle();
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
updateColorWheel();
currentColorCircle = findNearestByColor(initialColor);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (alphaSliderViewId != 0)
setAlphaSlider((AlphaSlider) getRootView().findViewById(alphaSliderViewId));
if (lightnessSliderViewId != 0)
setLightnessSlider((LightnessSlider) getRootView().findViewById(lightnessSliderViewId));
updateColorWheel();
currentColorCircle = findNearestByColor(initialColor);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateColorWheel();
}
private void updateColorWheel() {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (height < width)
width = height;
if (width <= 0)
return;
if (colorWheel == null || colorWheel.getWidth() != width) {
colorWheel = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
colorWheelCanvas = new Canvas(colorWheel);
alphaPatternPaint.setShader(PaintBuilder.createAlphaPatternShader(26));
}
if (currentColor == null || currentColor.getWidth() != width) {
currentColor = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
currentColorCanvas = new Canvas(currentColor);
}
drawColorWheel();
invalidate();
}
private void drawColorWheel() {
colorWheelCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
currentColorCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
if (renderer == null) return;
float half = colorWheelCanvas.getWidth() / 2f;
float strokeWidth = STROKE_RATIO * (1f + ColorWheelRenderer.GAP_PERCENTAGE);
float maxRadius = half - strokeWidth - half / density;
float cSize = maxRadius / (density - 1) / 2;
ColorWheelRenderOption colorWheelRenderOption = renderer.getRenderOption();
colorWheelRenderOption.density = this.density;
colorWheelRenderOption.maxRadius = maxRadius;
colorWheelRenderOption.cSize = cSize;
colorWheelRenderOption.strokeWidth = strokeWidth;
colorWheelRenderOption.alpha = alpha;
colorWheelRenderOption.lightness = lightness;
colorWheelRenderOption.targetCanvas = colorWheelCanvas;
renderer.initWith(colorWheelRenderOption);
renderer.draw();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = 0;
if (widthMode == MeasureSpec.UNSPECIFIED)
width = widthMeasureSpec;
else if (widthMode == MeasureSpec.AT_MOST)
width = MeasureSpec.getSize(widthMeasureSpec);
else if (widthMode == MeasureSpec.EXACTLY)
width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = 0;
if (heightMode == MeasureSpec.UNSPECIFIED)
height = heightMeasureSpec;
else if (heightMode == MeasureSpec.AT_MOST)
height = MeasureSpec.getSize(heightMeasureSpec);
else if (heightMode == MeasureSpec.EXACTLY)
height = MeasureSpec.getSize(heightMeasureSpec);
int squareDimen = width;
if (height < width)
squareDimen = height;
setMeasuredDimension(squareDimen, squareDimen);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: {
int lastSelectedColor = getSelectedColor();
currentColorCircle = findNearestByPosition(event.getX(), event.getY());
int selectedColor = getSelectedColor();
callOnColorChangedListeners(lastSelectedColor, selectedColor);
initialColor = selectedColor;
setColorToSliders(selectedColor);
updateColorWheel();
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
int selectedColor = getSelectedColor();
if (listeners != null) {
for (OnColorSelectedListener listener : listeners) {
try {
listener.onColorSelected(selectedColor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
setColorToSliders(selectedColor);
setColorText(selectedColor);
setColorPreviewColor(selectedColor);
invalidate();
break;
}
}
return true;
}
protected void callOnColorChangedListeners(int oldColor, int newColor) {
if (colorChangedListeners != null && oldColor != newColor) {
for (OnColorChangedListener listener : colorChangedListeners) {
try {
listener.onColorChanged(newColor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(backgroundColor);
float maxRadius = canvas.getWidth() / (1f + ColorWheelRenderer.GAP_PERCENTAGE);
float size = maxRadius / density / 2;
if (colorWheel != null && currentColorCircle != null) {
colorWheelFill.setColor(Color.HSVToColor(currentColorCircle.getHsvWithLightness(this.lightness)));
colorWheelFill.setAlpha((int) (alpha * 0xff));
// a separate canvas is used to erase an issue with the alpha pattern around the edges
// draw circle slightly larger than it needs to be, then erase edges to proper dimensions
currentColorCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + 4, alphaPatternPaint);
currentColorCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + 4, colorWheelFill);
selectorStroke = PaintBuilder.newPaint().color(0xffffffff).style(Paint.Style.STROKE).stroke(size * (STROKE_RATIO - 1)).xPerMode(PorterDuff.Mode.CLEAR).build();
if (showBorder) colorWheelCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + (selectorStroke.getStrokeWidth() / 2f), selectorStroke);
canvas.drawBitmap(colorWheel, 0, 0, null);
currentColorCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + (selectorStroke.getStrokeWidth() / 2f), selectorStroke);
canvas.drawBitmap(currentColor, 0, 0, null);
}
}
private ColorCircle findNearestByPosition(float x, float y) {
ColorCircle near = null;
double minDist = Double.MAX_VALUE;
for (ColorCircle colorCircle : renderer.getColorCircleList()) {
double dist = colorCircle.sqDist(x, y);
if (minDist > dist) {
minDist = dist;
near = colorCircle;
}
}
return near;
}
private ColorCircle findNearestByColor(int color) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
ColorCircle near = null;
double minDiff = Double.MAX_VALUE;
double x = hsv[1] * Math.cos(hsv[0] * Math.PI / 180);
double y = hsv[1] * Math.sin(hsv[0] * Math.PI / 180);
for (ColorCircle colorCircle : renderer.getColorCircleList()) {
float[] hsv1 = colorCircle.getHsv();
double x1 = hsv1[1] * Math.cos(hsv1[0] * Math.PI / 180);
double y1 = hsv1[1] * Math.sin(hsv1[0] * Math.PI / 180);
double dx = x - x1;
double dy = y - y1;
double dist = dx * dx + dy * dy;
if (dist < minDiff) {
minDiff = dist;
near = colorCircle;
}
}
return near;
}
public int getSelectedColor() {
int color = 0;
if (currentColorCircle != null)
color = Utils.colorAtLightness(currentColorCircle.getColor(), this.lightness);
return Utils.adjustAlpha(this.alpha, color);
}
public Integer[] getAllColors() {
return initialColors;
}
public void setInitialColors(Integer[] colors, int selectedColor) {
this.initialColors = colors;
this.colorSelection = selectedColor;
Integer initialColor = this.initialColors[this.colorSelection];
if (initialColor == null) initialColor = 0xffffffff;
setInitialColor(initialColor, true);
}
public void setInitialColor(int color, boolean updateText) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
this.alpha = Utils.getAlphaPercent(color);
this.lightness = hsv[2];
this.initialColors[this.colorSelection] = color;
this.initialColor = color;
setColorPreviewColor(color);
setColorToSliders(color);
if (this.colorEdit != null && updateText)
setColorText(color);
currentColorCircle = findNearestByColor(color);
}
public void setLightness(float lightness) {
int lastSelectedColor = getSelectedColor();
this.lightness = lightness;
if (currentColorCircle != null) {
this.initialColor = Color.HSVToColor(Utils.alphaValueAsInt(this.alpha), currentColorCircle.getHsvWithLightness(lightness));
if (this.colorEdit != null)
this.colorEdit.setText(Utils.getHexString(this.initialColor, this.alphaSlider != null));
if (this.alphaSlider != null && this.initialColor != null)
this.alphaSlider.setColor(this.initialColor);
callOnColorChangedListeners(lastSelectedColor, this.initialColor);
updateColorWheel();
invalidate();
}
}
public void setColor(int color, boolean updateText) {
setInitialColor(color, updateText);
updateColorWheel();
invalidate();
}
public void setAlphaValue(float alpha) {
int lastSelectedColor = getSelectedColor();
this.alpha = alpha;
this.initialColor = Color.HSVToColor(Utils.alphaValueAsInt(this.alpha), currentColorCircle.getHsvWithLightness(this.lightness));
if (this.colorEdit != null)
this.colorEdit.setText(Utils.getHexString(this.initialColor, this.alphaSlider != null));
if (this.lightnessSlider != null && this.initialColor != null)
this.lightnessSlider.setColor(this.initialColor);
callOnColorChangedListeners(lastSelectedColor, this.initialColor);
updateColorWheel();
invalidate();
}
public void addOnColorChangedListener(OnColorChangedListener listener) {
this.colorChangedListeners.add(listener);
}
public void addOnColorSelectedListener(OnColorSelectedListener listener) {
this.listeners.add(listener);
}
public void setLightnessSlider(LightnessSlider lightnessSlider) {
this.lightnessSlider = lightnessSlider;
if (lightnessSlider != null) {
this.lightnessSlider.setColorPicker(this);
this.lightnessSlider.setColor(getSelectedColor());
}
}
public void setAlphaSlider(AlphaSlider alphaSlider) {
this.alphaSlider = alphaSlider;
if (alphaSlider != null) {
this.alphaSlider.setColorPicker(this);
this.alphaSlider.setColor(getSelectedColor());
}
}
public void setColorEdit(EditText colorEdit) {
this.colorEdit = colorEdit;
if (this.colorEdit != null) {
this.colorEdit.setVisibility(View.VISIBLE);
this.colorEdit.addTextChangedListener(colorTextChange);
setColorEditTextColor(pickerColorEditTextColor);
}
}
public void setColorEditTextColor(int argb) {
this.pickerColorEditTextColor = argb;
if (colorEdit != null)
colorEdit.setTextColor(argb);
}
public void setDensity(int density) {
this.density = Math.max(2, density);
invalidate();
}
public void setRenderer(ColorWheelRenderer renderer) {
this.renderer = renderer;
invalidate();
}
public void setColorPreview(LinearLayout colorPreview, Integer selectedColor) {
if (colorPreview == null)
return;
this.colorPreview = colorPreview;
if (selectedColor == null)
selectedColor = 0;
int children = colorPreview.getChildCount();
if (children == 0 || colorPreview.getVisibility() != View.VISIBLE)
return;
for (int i = 0; i < children; i++) {
View childView = colorPreview.getChildAt(i);
if (!(childView instanceof LinearLayout))
continue;
LinearLayout childLayout = (LinearLayout) childView;
if (i == selectedColor) {
childLayout.setBackgroundColor(Color.WHITE);
}
ImageView childImage = (ImageView) childLayout.findViewById(R.id.image_preview);
childImage.setClickable(true);
childImage.setTag(i);
childImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (v == null)
return;
Object tag = v.getTag();
if (tag == null || !(tag instanceof Integer))
return;
setSelectedColor((int) tag);
}
});
}
}
public void setSelectedColor(int previewNumber) {
if (initialColors == null || initialColors.length < previewNumber)
return;
this.colorSelection = previewNumber;
setHighlightedColor(previewNumber);
Integer color = initialColors[previewNumber];
if (color == null)
return;
setColor(color, true);
}
public void setShowBorder(boolean showBorder) {
this.showBorder = showBorder;
}
private void setHighlightedColor(int previewNumber) {
int children = colorPreview.getChildCount();
if (children == 0 || colorPreview.getVisibility() != View.VISIBLE)
return;
for (int i = 0; i < children; i++) {
View childView = colorPreview.getChildAt(i);
if (!(childView instanceof LinearLayout))
continue;
LinearLayout childLayout = (LinearLayout) childView;
if (i == previewNumber) {
childLayout.setBackgroundColor(Color.WHITE);
} else {
childLayout.setBackgroundColor(Color.TRANSPARENT);
}
}
}
private void setColorPreviewColor(int newColor) {
if (colorPreview == null || initialColors == null || colorSelection > initialColors.length || initialColors[colorSelection] == null)
return;
int children = colorPreview.getChildCount();
if (children == 0 || colorPreview.getVisibility() != View.VISIBLE)
return;
View childView = colorPreview.getChildAt(colorSelection);
if (!(childView instanceof LinearLayout))
return;
LinearLayout childLayout = (LinearLayout) childView;
ImageView childImage = (ImageView) childLayout.findViewById(R.id.image_preview);
childImage.setImageDrawable(new ColorCircleDrawable(newColor));
}
private void setColorText(int argb) {
if (colorEdit == null)
return;
colorEdit.setText(Utils.getHexString(argb, this.alphaSlider != null));
}
private void setColorToSliders(int selectedColor) {
if (lightnessSlider != null)
lightnessSlider.setColor(selectedColor);
if (alphaSlider != null)
alphaSlider.setColor(selectedColor);
}
public enum WHEEL_TYPE {
FLOWER, CIRCLE;
public static WHEEL_TYPE indexOf(int index) {
switch (index) {
case 0:
return FLOWER;
case 1:
return CIRCLE;
}
return FLOWER;
}
}
}

View File

@ -0,0 +1,5 @@
package com.flask.colorpicker;
public interface OnColorChangedListener {
void onColorChanged(int selectedColor);
}

View File

@ -0,0 +1,5 @@
package com.flask.colorpicker;
public interface OnColorSelectedListener {
void onColorSelected(int selectedColor);
}

View File

@ -0,0 +1,40 @@
package com.flask.colorpicker;
import android.graphics.Color;
/**
* Created by Charles Andersons on 4/17/15.
*/
public class Utils {
public static float getAlphaPercent(int argb) {
return Color.alpha(argb) / 255f;
}
public static int alphaValueAsInt(float alpha) {
return Math.round(alpha * 255);
}
public static int adjustAlpha(float alpha, int color) {
return alphaValueAsInt(alpha) << 24 | (0x00ffffff & color);
}
public static int colorAtLightness(int color, float lightness) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] = lightness;
return Color.HSVToColor(hsv);
}
public static float lightnessOfColor(int color) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
return hsv[2];
}
public static String getHexString(int color, boolean showAlpha) {
int base = showAlpha ? 0xFFFFFFFF : 0xFFFFFF;
String format = showAlpha ? "#%08X" : "#%06X";
return String.format(format, (base & color)).toUpperCase();
}
}

View File

@ -0,0 +1,10 @@
package com.flask.colorpicker.builder;
import android.content.DialogInterface;
/**
* Created by Charles Anderson on 4/17/15.
*/
public interface ColorPickerClickListener {
void onClick(DialogInterface d, int lastSelectedColor, Integer[] allColors);
}

View File

@ -0,0 +1,296 @@
package com.flask.colorpicker.builder;
import androidx.appcompat.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.text.InputFilter;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.flask.colorpicker.ColorPickerView;
import com.flask.colorpicker.OnColorChangedListener;
import com.flask.colorpicker.OnColorSelectedListener;
import com.flask.colorpicker.R;
import com.flask.colorpicker.Utils;
import com.flask.colorpicker.renderer.ColorWheelRenderer;
import com.flask.colorpicker.slider.AlphaSlider;
import com.flask.colorpicker.slider.LightnessSlider;
public class ColorPickerDialogBuilder {
private AlertDialog.Builder builder;
private LinearLayout pickerContainer;
private ColorPickerView colorPickerView;
private LightnessSlider lightnessSlider;
private AlphaSlider alphaSlider;
private EditText colorEdit;
private LinearLayout colorPreview;
private boolean isLightnessSliderEnabled = true;
private boolean isAlphaSliderEnabled = true;
private boolean isBorderEnabled = true;
private boolean isColorEditEnabled = false;
private boolean isPreviewEnabled = false;
private int pickerCount = 1;
private int defaultMargin = 0;
private int defaultMarginTop = 0;
private Integer[] initialColor = new Integer[]{null, null, null, null, null};
private ColorPickerDialogBuilder(Context context) {
this(context, 0);
}
private ColorPickerDialogBuilder(Context context, int theme) {
defaultMargin = getDimensionAsPx(context, R.dimen.default_slider_margin);
defaultMarginTop = getDimensionAsPx(context, R.dimen.default_margin_top);
builder = new AlertDialog.Builder(context, theme);
pickerContainer = new LinearLayout(context);
pickerContainer.setOrientation(LinearLayout.VERTICAL);
pickerContainer.setGravity(Gravity.CENTER_HORIZONTAL);
pickerContainer.setPadding(defaultMargin, defaultMarginTop, defaultMargin, 0);
LinearLayout.LayoutParams layoutParamsForColorPickerView = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
layoutParamsForColorPickerView.weight = 1;
colorPickerView = new ColorPickerView(context);
pickerContainer.addView(colorPickerView, layoutParamsForColorPickerView);
builder.setView(pickerContainer);
}
public static ColorPickerDialogBuilder with(Context context) {
return new ColorPickerDialogBuilder(context);
}
public static ColorPickerDialogBuilder with(Context context, int theme) {
return new ColorPickerDialogBuilder(context, theme);
}
public ColorPickerDialogBuilder setTitle(String title) {
builder.setTitle(title);
return this;
}
public ColorPickerDialogBuilder setTitle(int titleId) {
builder.setTitle(titleId);
return this;
}
public ColorPickerDialogBuilder initialColor(int initialColor) {
this.initialColor[0] = initialColor;
return this;
}
public ColorPickerDialogBuilder initialColors(int[] initialColor) {
for (int i = 0; i < initialColor.length && i < this.initialColor.length; i++) {
this.initialColor[i] = initialColor[i];
}
return this;
}
public ColorPickerDialogBuilder wheelType(ColorPickerView.WHEEL_TYPE wheelType) {
ColorWheelRenderer renderer = ColorWheelRendererBuilder.getRenderer(wheelType);
colorPickerView.setRenderer(renderer);
return this;
}
public ColorPickerDialogBuilder density(int density) {
colorPickerView.setDensity(density);
return this;
}
public ColorPickerDialogBuilder setOnColorChangedListener(OnColorChangedListener onColorChangedListener) {
colorPickerView.addOnColorChangedListener(onColorChangedListener);
return this;
}
public ColorPickerDialogBuilder setOnColorSelectedListener(OnColorSelectedListener onColorSelectedListener) {
colorPickerView.addOnColorSelectedListener(onColorSelectedListener);
return this;
}
public ColorPickerDialogBuilder setPositiveButton(CharSequence text, final ColorPickerClickListener onClickListener) {
builder.setPositiveButton(text, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
positiveButtonOnClick(dialog, onClickListener);
}
});
return this;
}
public ColorPickerDialogBuilder setPositiveButton(int textId, final ColorPickerClickListener onClickListener) {
builder.setPositiveButton(textId, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
positiveButtonOnClick(dialog, onClickListener);
}
});
return this;
}
public ColorPickerDialogBuilder setNegativeButton(CharSequence text, DialogInterface.OnClickListener onClickListener) {
builder.setNegativeButton(text, onClickListener);
return this;
}
public ColorPickerDialogBuilder setNegativeButton(int textId, DialogInterface.OnClickListener onClickListener) {
builder.setNegativeButton(textId, onClickListener);
return this;
}
public ColorPickerDialogBuilder noSliders() {
isLightnessSliderEnabled = false;
isAlphaSliderEnabled = false;
return this;
}
public ColorPickerDialogBuilder alphaSliderOnly() {
isLightnessSliderEnabled = false;
isAlphaSliderEnabled = true;
return this;
}
public ColorPickerDialogBuilder lightnessSliderOnly() {
isLightnessSliderEnabled = true;
isAlphaSliderEnabled = false;
return this;
}
public ColorPickerDialogBuilder showAlphaSlider(boolean showAlpha) {
isAlphaSliderEnabled = showAlpha;
return this;
}
public ColorPickerDialogBuilder showLightnessSlider(boolean showLightness) {
isLightnessSliderEnabled = showLightness;
return this;
}
public ColorPickerDialogBuilder showBorder(boolean showBorder) {
isBorderEnabled = showBorder;
return this;
}
public ColorPickerDialogBuilder showColorEdit(boolean showEdit) {
isColorEditEnabled = showEdit;
return this;
}
public ColorPickerDialogBuilder setColorEditTextColor(int argb) {
colorPickerView.setColorEditTextColor(argb);
return this;
}
public ColorPickerDialogBuilder showColorPreview(boolean showPreview) {
isPreviewEnabled = showPreview;
if (!showPreview)
pickerCount = 1;
return this;
}
public ColorPickerDialogBuilder setPickerCount(int pickerCount) throws IndexOutOfBoundsException {
if (pickerCount < 1 || pickerCount > 5)
throw new IndexOutOfBoundsException("Picker Can Only Support 1-5 Colors");
this.pickerCount = pickerCount;
if (this.pickerCount > 1)
this.isPreviewEnabled = true;
return this;
}
public AlertDialog build() {
Context context = builder.getContext();
colorPickerView.setInitialColors(initialColor, getStartOffset(initialColor));
colorPickerView.setShowBorder(isBorderEnabled);
if (isLightnessSliderEnabled) {
LinearLayout.LayoutParams layoutParamsForLightnessBar = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getDimensionAsPx(context, R.dimen.default_slider_height));
lightnessSlider = new LightnessSlider(context);
lightnessSlider.setLayoutParams(layoutParamsForLightnessBar);
pickerContainer.addView(lightnessSlider);
colorPickerView.setLightnessSlider(lightnessSlider);
lightnessSlider.setColor(getStartColor(initialColor));
lightnessSlider.setShowBorder(isBorderEnabled);
}
if (isAlphaSliderEnabled) {
LinearLayout.LayoutParams layoutParamsForAlphaBar = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getDimensionAsPx(context, R.dimen.default_slider_height));
alphaSlider = new AlphaSlider(context);
alphaSlider.setLayoutParams(layoutParamsForAlphaBar);
pickerContainer.addView(alphaSlider);
colorPickerView.setAlphaSlider(alphaSlider);
alphaSlider.setColor(getStartColor(initialColor));
alphaSlider.setShowBorder(isBorderEnabled);
}
if (isColorEditEnabled) {
LinearLayout.LayoutParams layoutParamsForColorEdit = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
colorEdit = (EditText) View.inflate(context, R.layout.color_edit, null);
colorEdit.setFilters(new InputFilter[]{new InputFilter.AllCaps()});
colorEdit.setSingleLine();
colorEdit.setVisibility(View.GONE);
// limit number of characters to hexColors
int maxLength = isAlphaSliderEnabled ? 9 : 7;
colorEdit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
pickerContainer.addView(colorEdit, layoutParamsForColorEdit);
colorEdit.setText(Utils.getHexString(getStartColor(initialColor), isAlphaSliderEnabled));
colorPickerView.setColorEdit(colorEdit);
}
if (isPreviewEnabled) {
colorPreview = (LinearLayout) View.inflate(context, R.layout.color_preview, null);
colorPreview.setVisibility(View.GONE);
pickerContainer.addView(colorPreview);
if (initialColor.length == 0) {
ImageView colorImage = (ImageView) View.inflate(context, R.layout.color_selector, null);
colorImage.setImageDrawable(new ColorDrawable(Color.WHITE));
} else {
for (int i = 0; i < initialColor.length && i < this.pickerCount; i++) {
if (initialColor[i] == null)
break;
LinearLayout colorLayout = (LinearLayout) View.inflate(context, R.layout.color_selector, null);
ImageView colorImage = (ImageView) colorLayout.findViewById(R.id.image_preview);
colorImage.setImageDrawable(new ColorDrawable(initialColor[i]));
colorPreview.addView(colorLayout);
}
}
colorPreview.setVisibility(View.VISIBLE);
colorPickerView.setColorPreview(colorPreview, getStartOffset(initialColor));
}
return builder.create();
}
private Integer getStartOffset(Integer[] colors) {
Integer start = 0;
for (int i = 0; i < colors.length; i++) {
if (colors[i] == null) {
return start;
}
start = (i + 1) / 2;
}
return start;
}
private int getStartColor(Integer[] colors) {
Integer startColor = getStartOffset(colors);
return startColor == null ? Color.WHITE : colors[startColor];
}
private static int getDimensionAsPx(Context context, int rid) {
return (int) (context.getResources().getDimension(rid) + .5f);
}
private void positiveButtonOnClick(DialogInterface dialog, ColorPickerClickListener onClickListener) {
int selectedColor = colorPickerView.getSelectedColor();
Integer[] allColors = colorPickerView.getAllColors();
onClickListener.onClick(dialog, selectedColor, allColors);
}
}

View File

@ -0,0 +1,18 @@
package com.flask.colorpicker.builder;
import com.flask.colorpicker.ColorPickerView;
import com.flask.colorpicker.renderer.ColorWheelRenderer;
import com.flask.colorpicker.renderer.FlowerColorWheelRenderer;
import com.flask.colorpicker.renderer.SimpleColorWheelRenderer;
public class ColorWheelRendererBuilder {
public static ColorWheelRenderer getRenderer(ColorPickerView.WHEEL_TYPE wheelType) {
switch (wheelType) {
case CIRCLE:
return new SimpleColorWheelRenderer();
case FLOWER:
return new FlowerColorWheelRenderer();
}
throw new IllegalArgumentException("wrong WHEEL_TYPE");
}
}

View File

@ -0,0 +1,82 @@
package com.flask.colorpicker.builder;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
public class PaintBuilder {
public static PaintHolder newPaint() {
return new PaintHolder();
}
public static class PaintHolder {
private Paint paint;
private PaintHolder() {
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
public PaintHolder color(int color) {
this.paint.setColor(color);
return this;
}
public PaintHolder antiAlias(boolean flag) {
this.paint.setAntiAlias(flag);
return this;
}
public PaintHolder style(Paint.Style style) {
this.paint.setStyle(style);
return this;
}
public PaintHolder mode(PorterDuff.Mode mode) {
this.paint.setXfermode(new PorterDuffXfermode(mode));
return this;
}
public PaintHolder stroke(float width) {
this.paint.setStrokeWidth(width);
return this;
}
public PaintHolder xPerMode(PorterDuff.Mode mode) {
this.paint.setXfermode(new PorterDuffXfermode(mode));
return this;
}
public PaintHolder shader(Shader shader) {
this.paint.setShader(shader);
return this;
}
public Paint build() {
return this.paint;
}
}
public static Shader createAlphaPatternShader(int size) {
size /= 2;
size = Math.max(8, size * 2);
return new BitmapShader(createAlphaBackgroundPattern(size), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
}
private static Bitmap createAlphaBackgroundPattern(int size) {
Paint alphaPatternPaint = PaintBuilder.newPaint().build();
Bitmap bm = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
int s = Math.round(size / 2f);
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++) {
if ((i + j) % 2 == 0) alphaPatternPaint.setColor(0xffffffff);
else alphaPatternPaint.setColor(0xffd0d0d0);
c.drawRect(i * s, j * s, (i + 1) * s, (j + 1) * s, alphaPatternPaint);
}
return bm;
}
}

View File

@ -0,0 +1,34 @@
package com.flask.colorpicker.renderer;
import com.flask.colorpicker.ColorCircle;
import java.util.ArrayList;
import java.util.List;
public abstract class AbsColorWheelRenderer implements ColorWheelRenderer {
protected ColorWheelRenderOption colorWheelRenderOption;
protected List<ColorCircle> colorCircleList = new ArrayList<>();
public void initWith(ColorWheelRenderOption colorWheelRenderOption) {
this.colorWheelRenderOption = colorWheelRenderOption;
this.colorCircleList.clear();
}
@Override
public ColorWheelRenderOption getRenderOption() {
if (colorWheelRenderOption == null) colorWheelRenderOption = new ColorWheelRenderOption();
return colorWheelRenderOption;
}
public List<ColorCircle> getColorCircleList() {
return colorCircleList;
}
protected int getAlphaValueAsInt() {
return Math.round(colorWheelRenderOption.alpha * 255);
}
protected int calcTotalCount(float radius, float size) {
return Math.max(1, (int) ((1f - GAP_PERCENTAGE) * Math.PI / (Math.asin(size / radius)) + 0.5f));
}
}

View File

@ -0,0 +1,10 @@
package com.flask.colorpicker.renderer;
import android.graphics.Canvas;
public class ColorWheelRenderOption {
public int density;
public float maxRadius;
public float cSize, strokeWidth, alpha, lightness;
public Canvas targetCanvas;
}

View File

@ -0,0 +1,17 @@
package com.flask.colorpicker.renderer;
import com.flask.colorpicker.ColorCircle;
import java.util.List;
public interface ColorWheelRenderer {
float GAP_PERCENTAGE = 0.025f;
void draw();
ColorWheelRenderOption getRenderOption();
void initWith(ColorWheelRenderOption colorWheelRenderOption);
List<ColorCircle> getColorCircleList();
}

View File

@ -0,0 +1,50 @@
package com.flask.colorpicker.renderer;
import android.graphics.Color;
import android.graphics.Paint;
import com.flask.colorpicker.ColorCircle;
import com.flask.colorpicker.builder.PaintBuilder;
public class FlowerColorWheelRenderer extends AbsColorWheelRenderer {
private Paint selectorFill = PaintBuilder.newPaint().build();
private float[] hsv = new float[3];
private float sizeJitter = 1.2f;
@Override
public void draw() {
final int setSize = colorCircleList.size();
int currentCount = 0;
float half = colorWheelRenderOption.targetCanvas.getWidth() / 2f;
int density = colorWheelRenderOption.density;
float strokeWidth = colorWheelRenderOption.strokeWidth;
float maxRadius = colorWheelRenderOption.maxRadius;
float cSize = colorWheelRenderOption.cSize;
for (int i = 0; i < density; i++) {
float p = (float) i / (density - 1); // 0~1
float jitter = (i - density / 2f) / density; // -0.5 ~ 0.5
float radius = maxRadius * p;
float size = Math.max(1.5f + strokeWidth, cSize + (i == 0 ? 0 : cSize * sizeJitter * jitter));
int total = Math.min(calcTotalCount(radius, size), density * 2);
for (int j = 0; j < total; j++) {
double angle = Math.PI * 2 * j / total + (Math.PI / total) * ((i + 1) % 2);
float x = half + (float) (radius * Math.cos(angle));
float y = half + (float) (radius * Math.sin(angle));
hsv[0] = (float) (angle * 180 / Math.PI);
hsv[1] = radius / maxRadius;
hsv[2] = colorWheelRenderOption.lightness;
selectorFill.setColor(Color.HSVToColor(hsv));
selectorFill.setAlpha(getAlphaValueAsInt());
colorWheelRenderOption.targetCanvas.drawCircle(x, y, size - strokeWidth, selectorFill);
if (currentCount >= setSize) {
colorCircleList.add(new ColorCircle(x, y, hsv));
} else colorCircleList.get(currentCount).set(x, y, hsv);
currentCount++;
}
}
}
}

View File

@ -0,0 +1,46 @@
package com.flask.colorpicker.renderer;
import android.graphics.Color;
import android.graphics.Paint;
import com.flask.colorpicker.ColorCircle;
import com.flask.colorpicker.builder.PaintBuilder;
public class SimpleColorWheelRenderer extends AbsColorWheelRenderer {
private Paint selectorFill = PaintBuilder.newPaint().build();
private float[] hsv = new float[3];
@Override
public void draw() {
final int setSize = colorCircleList.size();
int currentCount = 0;
float half = colorWheelRenderOption.targetCanvas.getWidth() / 2f;
int density = colorWheelRenderOption.density;
float maxRadius = colorWheelRenderOption.maxRadius;
for (int i = 0; i < density; i++) {
float p = (float) i / (density - 1); // 0~1
float radius = maxRadius * p;
float size = colorWheelRenderOption.cSize;
int total = calcTotalCount(radius, size);
for (int j = 0; j < total; j++) {
double angle = Math.PI * 2 * j / total + (Math.PI / total) * ((i + 1) % 2);
float x = half + (float) (radius * Math.cos(angle));
float y = half + (float) (radius * Math.sin(angle));
hsv[0] = (float) (angle * 180 / Math.PI);
hsv[1] = radius / maxRadius;
hsv[2] = colorWheelRenderOption.lightness;
selectorFill.setColor(Color.HSVToColor(hsv));
selectorFill.setAlpha(getAlphaValueAsInt());
colorWheelRenderOption.targetCanvas.drawCircle(x, y, size - colorWheelRenderOption.strokeWidth, selectorFill);
if (currentCount >= setSize)
colorCircleList.add(new ColorCircle(x, y, hsv));
else colorCircleList.get(currentCount).set(x, y, hsv);
currentCount++;
}
}
}
}

View File

@ -0,0 +1,189 @@
package com.flask.colorpicker.slider;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import androidx.annotation.DimenRes;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.flask.colorpicker.R;
public abstract class AbsCustomSlider extends View {
protected Bitmap bitmap;
protected Canvas bitmapCanvas;
protected Bitmap bar;
protected Canvas barCanvas;
protected OnValueChangedListener onValueChangedListener;
protected int barOffsetX;
protected int handleRadius = 20;
protected int barHeight = 5;
protected float value = 1;
protected boolean showBorder = false;
private boolean inVerticalOrientation = false;
public AbsCustomSlider(Context context) {
super(context);
init(context, null);
}
public AbsCustomSlider(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public AbsCustomSlider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray styledAttrs = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.AbsCustomSlider, 0, 0);
try {
inVerticalOrientation = styledAttrs.getBoolean(
R.styleable.AbsCustomSlider_inVerticalOrientation, inVerticalOrientation);
} finally {
styledAttrs.recycle();
}
}
protected void updateBar() {
handleRadius = getDimension(R.dimen.default_slider_handler_radius);
barHeight = getDimension(R.dimen.default_slider_bar_height);
barOffsetX = handleRadius;
if (bar == null)
createBitmaps();
drawBar(barCanvas);
invalidate();
}
protected void createBitmaps() {
int width;
int height;
if (inVerticalOrientation) {
width = getHeight();
height = getWidth();
} else {
width = getWidth();
height = getHeight();
}
bar = Bitmap.createBitmap(Math.max(width - barOffsetX * 2, 1), barHeight, Bitmap.Config.ARGB_8888);
barCanvas = new Canvas(bar);
if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
if (bitmap != null) bitmap.recycle();
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width;
int height;
if (inVerticalOrientation) {
width = getHeight();
height = getWidth();
canvas.rotate(-90);
canvas.translate(-width, 0);
} else {
width = getWidth();
height = getHeight();
}
if (bar != null && bitmapCanvas != null) {
bitmapCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
bitmapCanvas.drawBitmap(bar, barOffsetX, (height - bar.getHeight()) / 2, null);
float x = handleRadius + value * (width - handleRadius * 2);
float y = height / 2f;
drawHandle(bitmapCanvas, x, y);
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
protected abstract void drawBar(Canvas barCanvas);
protected abstract void onValueChanged(float value);
protected abstract void drawHandle(Canvas canvas, float x, float y);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateBar();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = 0;
if (widthMode == MeasureSpec.UNSPECIFIED)
width = widthMeasureSpec;
else if (widthMode == MeasureSpec.AT_MOST)
width = MeasureSpec.getSize(widthMeasureSpec);
else if (widthMode == MeasureSpec.EXACTLY)
width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = 0;
if (heightMode == MeasureSpec.UNSPECIFIED)
height = heightMeasureSpec;
else if (heightMode == MeasureSpec.AT_MOST)
height = MeasureSpec.getSize(heightMeasureSpec);
else if (heightMode == MeasureSpec.EXACTLY)
height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: {
if (bar != null) {
if (inVerticalOrientation) {
value = 1 - (event.getY() - barOffsetX) / bar.getWidth();
} else {
value = (event.getX() - barOffsetX) / bar.getWidth();
}
value = Math.max(0, Math.min(value, 1));
onValueChanged(value);
invalidate();
}
break;
}
case MotionEvent.ACTION_UP: {
onValueChanged(value);
if (onValueChangedListener != null)
onValueChangedListener.onValueChanged(value);
invalidate();
}
}
return true;
}
protected int getDimension(@DimenRes int id) {
return getResources().getDimensionPixelSize(id);
}
public void setShowBorder(boolean showBorder) {
this.showBorder = showBorder;
}
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
this.onValueChangedListener = onValueChangedListener;
}
}

View File

@ -0,0 +1,100 @@
package com.flask.colorpicker.slider;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import com.flask.colorpicker.ColorPickerView;
import com.flask.colorpicker.Utils;
import com.flask.colorpicker.builder.PaintBuilder;
public class AlphaSlider extends AbsCustomSlider {
public int color;
private Paint alphaPatternPaint = PaintBuilder.newPaint().build();
private Paint barPaint = PaintBuilder.newPaint().build();
private Paint solid = PaintBuilder.newPaint().build();
private Paint clearingStroke = PaintBuilder.newPaint().color(0xffffffff).xPerMode(PorterDuff.Mode.CLEAR).build();
private Paint clearStroke = PaintBuilder.newPaint().build();
private Bitmap clearBitmap;
private Canvas clearBitmapCanvas;
private ColorPickerView colorPicker;
public AlphaSlider(Context context) {
super(context);
}
public AlphaSlider(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlphaSlider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void createBitmaps() {
super.createBitmaps();
alphaPatternPaint.setShader(PaintBuilder.createAlphaPatternShader(barHeight * 2));
clearBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
clearBitmapCanvas = new Canvas(clearBitmap);
}
@Override
protected void drawBar(Canvas barCanvas) {
int width = barCanvas.getWidth();
int height = barCanvas.getHeight();
barCanvas.drawRect(0, 0, width, height, alphaPatternPaint);
int l = Math.max(2, width / 256);
for (int x = 0; x <= width; x += l) {
float alpha = (float) x / (width - 1);
barPaint.setColor(color);
barPaint.setAlpha(Math.round(alpha * 255));
barCanvas.drawRect(x, 0, x + l, height, barPaint);
}
}
@Override
protected void onValueChanged(float value) {
if (colorPicker != null)
colorPicker.setAlphaValue(value);
}
@Override
protected void drawHandle(Canvas canvas, float x, float y) {
solid.setColor(color);
solid.setAlpha(Math.round(value * 255));
if (showBorder) canvas.drawCircle(x, y, handleRadius, clearingStroke);
if (value < 1) {
// this fixes the same artifact issue from ColorPickerView
// happens when alpha pattern is drawn underneath a circle with the same size
clearBitmapCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
clearBitmapCanvas.drawCircle(x, y, handleRadius * 0.75f + 4, alphaPatternPaint);
clearBitmapCanvas.drawCircle(x, y, handleRadius * 0.75f + 4, solid);
clearStroke = PaintBuilder.newPaint().color(0xffffffff).style(Paint.Style.STROKE).stroke(6).xPerMode(PorterDuff.Mode.CLEAR).build();
clearBitmapCanvas.drawCircle(x, y, handleRadius * 0.75f + (clearStroke.getStrokeWidth() / 2), clearStroke);
canvas.drawBitmap(clearBitmap, 0, 0, null);
} else {
canvas.drawCircle(x, y, handleRadius * 0.75f, solid);
}
}
public void setColorPicker(ColorPickerView colorPicker) {
this.colorPicker = colorPicker;
}
public void setColor(int color) {
this.color = color;
this.value = Utils.getAlphaPercent(color);
if (bar != null) {
updateBar();
invalidate();
}
}
}

View File

@ -0,0 +1,74 @@
package com.flask.colorpicker.slider;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import com.flask.colorpicker.ColorPickerView;
import com.flask.colorpicker.Utils;
import com.flask.colorpicker.builder.PaintBuilder;
public class LightnessSlider extends AbsCustomSlider {
private int color;
private Paint barPaint = PaintBuilder.newPaint().build();
private Paint solid = PaintBuilder.newPaint().build();
private Paint clearingStroke = PaintBuilder.newPaint().color(0xffffffff).xPerMode(PorterDuff.Mode.CLEAR).build();
private ColorPickerView colorPicker;
public LightnessSlider(Context context) {
super(context);
}
public LightnessSlider(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LightnessSlider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void drawBar(Canvas barCanvas) {
int width = barCanvas.getWidth();
int height = barCanvas.getHeight();
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
int l = Math.max(2, width / 256);
for (int x = 0; x <= width; x += l) {
hsv[2] = (float) x / (width - 1);
barPaint.setColor(Color.HSVToColor(hsv));
barCanvas.drawRect(x, 0, x + l, height, barPaint);
}
}
@Override
protected void onValueChanged(float value) {
if (colorPicker != null)
colorPicker.setLightness(value);
}
@Override
protected void drawHandle(Canvas canvas, float x, float y) {
solid.setColor(Utils.colorAtLightness(color, value));
if (showBorder) canvas.drawCircle(x, y, handleRadius, clearingStroke);
canvas.drawCircle(x, y, handleRadius * 0.75f, solid);
}
public void setColorPicker(ColorPickerView colorPicker) {
this.colorPicker = colorPicker;
}
public void setColor(int color) {
this.color = color;
this.value = Utils.lightnessOfColor(color);
if (bar != null) {
updateBar();
invalidate();
}
}
}

View File

@ -0,0 +1,5 @@
package com.flask.colorpicker.slider;
public interface OnValueChangedListener {
void onValueChanged(float value);
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/PickerEditText"
android:hint="Color Value"
android:inputType="textNoSuggestions" />

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="@dimen/default_preview_height"
android:background="@android:color/transparent"
android:gravity="center"
android:orientation="horizontal" />

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/default_preview_height"
android:layout_height="@dimen/default_preview_height"
android:background="@android:color/transparent"
android:padding="2dp">
<ImageView
android:layout_width="@dimen/default_preview_image_height"
android:layout_height="@dimen/default_preview_image_height"
android:src="@android:color/transparent"
android:id="@+id/image_preview"/>
</LinearLayout>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/color_indicator"
android:layout_width="32dp"
android:layout_height="32dp"
tools:ignore="ContentDescription" />

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ColorPickerPreference">
<attr name="alphaSlider" format="boolean"/>
<attr name="lightnessSlider" format="boolean"/>
<attr name="border" format="boolean"/>
<attr name="density" format="integer"/>
<attr name="initialColor" format="integer"/>
<attr name="wheelType" format="enum">
<enum name="FLOWER" value="0"/>
<enum name="CIRCLE" value="1"/>
</attr>
<attr name="lightnessSliderView" format="reference"/>
<attr name="alphaSliderView" format="reference"/>
<attr name="pickerColorEdit" format="boolean"/>
<attr name="pickerColorEditTextColor" format="integer"/>
<attr name="pickerTitle" format="reference|string"/>
<attr name="pickerButtonOk" format="reference|string"/>
<attr name="pickerButtonCancel" format="reference|string"/>
</declare-styleable>
<declare-styleable name="AbsCustomSlider">
<attr name="inVerticalOrientation" format="boolean"/>
</declare-styleable>
</resources>

View File

@ -0,0 +1,10 @@
<resources>
<dimen name="default_slider_height">36dp</dimen>
<dimen name="default_slider_margin">24dp</dimen>
<dimen name="default_slider_bar_height">4dp</dimen>
<dimen name="default_slider_handler_radius">10dp</dimen>
<dimen name="default_padding_side">24dp</dimen>
<dimen name="default_preview_height">40dp</dimen>
<dimen name="default_preview_image_height">36dp</dimen>
<dimen name="default_margin_top">20dp</dimen>
</resources>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="PickerEditText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_margin">4dp</item>
<item name="android:imeOptions">actionNext</item>
<item name="android:singleLine">true</item>
<item name="android:textSize">22sp</item>
</style>
</resources>

View File

@ -1,2 +1,3 @@
include ':app', ':openpgp-api'
include ':app', ':colorpicker', ':openpgp-api'
project(':colorpicker').projectDir = new File('colorpicker')
project(':openpgp-api').projectDir = new File('openpgp-api')