mirror of https://codeberg.org/tom79/Fedilab
parent
4a8d20ed6b
commit
8ffd5b3a19
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,39 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 33
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Add a new configuration to hold your dependencies
|
||||
configurations {
|
||||
libConfig
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
//noinspection GradleCompatible
|
||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
VERSION_NAME=1.1.0
|
||||
VERSION_CODE=110
|
||||
GROUP=com.jaredrummler
|
||||
ARTIFACT_ID=colorpicker
|
||||
POM_NAME=colorpicker
|
||||
POM_ARTIFACT_ID=colorpicker
|
||||
POM_PACKAGING=aar
|
||||
POM_DESCRIPTION=A simply good looking color picker component for Android
|
||||
POM_URL=https://github.com/jaredrummler/ColorPicker
|
||||
POM_SCM_URL=https://github.com/jaredrummler/ColorPicker
|
||||
POM_SCM_CONNECTION=scm:git@github.com:jaredrummler/ColorPicker.git
|
||||
POM_SCM_DEV_CONNECTION=scm:git@github.com:jaredrummler/ColorPicker.git
|
||||
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
POM_LICENCE_DIST=repo
|
||||
POM_DEVELOPER_ID=jaredrummler
|
||||
POM_DEVELOPER_NAME=Jared Rummler
|
||||
SNAPSHOT_REPOSITORY_URL=https://oss.sonatype.org/content/repositories/snapshots
|
||||
RELEASE_REPOSITORY_URL=https://oss.sonatype.org/service/local/staging/deploy/maven2
|
@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\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 *;
|
||||
#}
|
@ -0,0 +1 @@
|
||||
<manifest package="com.jaredrummler.android.colorpicker" />
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* This drawable will draw a simple white and gray chessboard pattern.
|
||||
* It's the pattern you will often see as a background behind a partly transparent image in many applications.
|
||||
*/
|
||||
class AlphaPatternDrawable extends Drawable {
|
||||
|
||||
private final Paint paint = new Paint();
|
||||
private final Paint paintWhite = new Paint();
|
||||
private final Paint paintGray = new Paint();
|
||||
private int rectangleSize = 10;
|
||||
private int numRectanglesHorizontal;
|
||||
private int numRectanglesVertical;
|
||||
|
||||
/**
|
||||
* Bitmap in which the pattern will be cached.
|
||||
* This is so the pattern will not have to be recreated each time draw() gets called.
|
||||
* Because recreating the pattern i rather expensive. I will only be recreated if the size changes.
|
||||
*/
|
||||
private Bitmap bitmap;
|
||||
|
||||
AlphaPatternDrawable(int rectangleSize) {
|
||||
this.rectangleSize = rectangleSize;
|
||||
paintWhite.setColor(0xFFFFFFFF);
|
||||
paintGray.setColor(0xFFCBCBCB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
canvas.drawBitmap(bitmap, null, getBounds(), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
throw new UnsupportedOperationException("Alpha is not supported by this drawable.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
throw new UnsupportedOperationException("ColorFilter is not supported by this drawable.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
int height = bounds.height();
|
||||
int width = bounds.width();
|
||||
numRectanglesHorizontal = (int) Math.ceil((width / rectangleSize));
|
||||
numRectanglesVertical = (int) Math.ceil(height / rectangleSize);
|
||||
generatePatternBitmap();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will generate a bitmap with the pattern as big as the rectangle we were allow to draw on.
|
||||
* We do this to chache the bitmap so we don't need to recreate it each time draw() is called since it takes a few
|
||||
* milliseconds
|
||||
*/
|
||||
private void generatePatternBitmap() {
|
||||
if (getBounds().width() <= 0 || getBounds().height() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
Rect r = new Rect();
|
||||
boolean verticalStartWhite = true;
|
||||
for (int i = 0; i <= numRectanglesVertical; i++) {
|
||||
boolean isWhite = verticalStartWhite;
|
||||
for (int j = 0; j <= numRectanglesHorizontal; j++) {
|
||||
r.top = i * rectangleSize;
|
||||
r.left = j * rectangleSize;
|
||||
r.bottom = r.top + rectangleSize;
|
||||
r.right = r.left + rectangleSize;
|
||||
canvas.drawRect(r, isWhite ? paintWhite : paintGray);
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
verticalStartWhite = !verticalStartWhite;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
class ColorPaletteAdapter extends BaseAdapter {
|
||||
|
||||
/*package*/ final OnColorSelectedListener listener;
|
||||
/*package*/ final int[] colors;
|
||||
/*package*/ int selectedPosition;
|
||||
/*package*/ int colorShape;
|
||||
|
||||
ColorPaletteAdapter(OnColorSelectedListener listener, int[] colors, int selectedPosition,
|
||||
@ColorShape int colorShape) {
|
||||
this.listener = listener;
|
||||
this.colors = colors;
|
||||
this.selectedPosition = selectedPosition;
|
||||
this.colorShape = colorShape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return colors.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return colors[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
holder = new ViewHolder(parent.getContext());
|
||||
convertView = holder.view;
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
holder.setup(position);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
void selectNone() {
|
||||
selectedPosition = -1;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
interface OnColorSelectedListener {
|
||||
|
||||
void onColorSelected(int color);
|
||||
}
|
||||
|
||||
private final class ViewHolder {
|
||||
|
||||
View view;
|
||||
ColorPanelView colorPanelView;
|
||||
ImageView imageView;
|
||||
int originalBorderColor;
|
||||
|
||||
ViewHolder(Context context) {
|
||||
int layoutResId;
|
||||
if (colorShape == ColorShape.SQUARE) {
|
||||
layoutResId = R.layout.cpv_color_item_square;
|
||||
} else {
|
||||
layoutResId = R.layout.cpv_color_item_circle;
|
||||
}
|
||||
view = View.inflate(context, layoutResId, null);
|
||||
colorPanelView = view.findViewById(R.id.cpv_color_panel_view);
|
||||
imageView = view.findViewById(R.id.cpv_color_image_view);
|
||||
originalBorderColor = colorPanelView.getBorderColor();
|
||||
view.setTag(this);
|
||||
}
|
||||
|
||||
void setup(int position) {
|
||||
int color = colors[position];
|
||||
int alpha = Color.alpha(color);
|
||||
colorPanelView.setColor(color);
|
||||
imageView.setImageResource(selectedPosition == position ? R.drawable.cpv_preset_checked : 0);
|
||||
if (alpha != 255) {
|
||||
if (alpha <= ColorPickerDialog.ALPHA_THRESHOLD) {
|
||||
colorPanelView.setBorderColor(color | 0xFF000000);
|
||||
imageView.setColorFilter(/*color | 0xFF000000*/Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
colorPanelView.setBorderColor(originalBorderColor);
|
||||
imageView.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
} else {
|
||||
setColorFilter(position);
|
||||
}
|
||||
setOnClickListener(position);
|
||||
}
|
||||
|
||||
private void setOnClickListener(final int position) {
|
||||
colorPanelView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (selectedPosition != position) {
|
||||
selectedPosition = position;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
listener.onColorSelected(colors[position]);
|
||||
}
|
||||
});
|
||||
colorPanelView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
colorPanelView.showHint();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setColorFilter(int position) {
|
||||
if (position == selectedPosition && ColorUtils.calculateLuminance(colors[position]) >= 0.65) {
|
||||
imageView.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
imageView.setColorFilter(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class draws a panel which which will be filled with a color which can be set. It can be used to show the
|
||||
* currently selected color which you will get from the {@link ColorPickerView}.
|
||||
*/
|
||||
public class ColorPanelView extends View {
|
||||
|
||||
private final static int DEFAULT_BORDER_COLOR = 0xFF6E6E6E;
|
||||
|
||||
private Drawable alphaPattern;
|
||||
private Paint borderPaint;
|
||||
private Paint colorPaint;
|
||||
private Paint alphaPaint;
|
||||
private Paint originalPaint;
|
||||
private Rect drawingRect;
|
||||
private Rect colorRect;
|
||||
private RectF centerRect = new RectF();
|
||||
private boolean showOldColor;
|
||||
|
||||
/* The width in pixels of the border surrounding the color panel. */
|
||||
private int borderWidthPx;
|
||||
private int borderColor = DEFAULT_BORDER_COLOR;
|
||||
private int color = Color.BLACK;
|
||||
private int shape;
|
||||
|
||||
public ColorPanelView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ColorPanelView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ColorPanelView(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("color", color);
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if (state instanceof Bundle) {
|
||||
Bundle bundle = (Bundle) state;
|
||||
color = bundle.getInt("color");
|
||||
state = bundle.getParcelable("instanceState");
|
||||
}
|
||||
super.onRestoreInstanceState(state);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPanelView);
|
||||
shape = a.getInt(R.styleable.ColorPanelView_cpv_colorShape, ColorShape.CIRCLE);
|
||||
showOldColor = a.getBoolean(R.styleable.ColorPanelView_cpv_showOldColor, false);
|
||||
if (showOldColor && shape != ColorShape.CIRCLE) {
|
||||
throw new IllegalStateException("Color preview is only available in circle mode");
|
||||
}
|
||||
borderColor = a.getColor(R.styleable.ColorPanelView_cpv_borderColor, DEFAULT_BORDER_COLOR);
|
||||
a.recycle();
|
||||
if (borderColor == DEFAULT_BORDER_COLOR) {
|
||||
// If no specific border 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 typedArray =
|
||||
context.obtainStyledAttributes(value.data, new int[]{android.R.attr.textColorSecondary});
|
||||
borderColor = typedArray.getColor(0, borderColor);
|
||||
typedArray.recycle();
|
||||
}
|
||||
borderWidthPx = DrawingUtils.dpToPx(context, 1);
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
colorPaint = new Paint();
|
||||
colorPaint.setAntiAlias(true);
|
||||
if (showOldColor) {
|
||||
originalPaint = new Paint();
|
||||
}
|
||||
if (shape == ColorShape.CIRCLE) {
|
||||
Bitmap bitmap = ((BitmapDrawable) context.getResources().getDrawable(R.drawable.cpv_alpha)).getBitmap();
|
||||
alphaPaint = new Paint();
|
||||
alphaPaint.setAntiAlias(true);
|
||||
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
|
||||
alphaPaint.setShader(shader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
borderPaint.setColor(borderColor);
|
||||
colorPaint.setColor(color);
|
||||
if (shape == ColorShape.SQUARE) {
|
||||
if (borderWidthPx > 0) {
|
||||
canvas.drawRect(drawingRect, borderPaint);
|
||||
}
|
||||
if (alphaPattern != null) {
|
||||
alphaPattern.draw(canvas);
|
||||
}
|
||||
canvas.drawRect(colorRect, colorPaint);
|
||||
} else if (shape == ColorShape.CIRCLE) {
|
||||
final int outerRadius = getMeasuredWidth() / 2;
|
||||
if (borderWidthPx > 0) {
|
||||
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, outerRadius, borderPaint);
|
||||
}
|
||||
if (Color.alpha(color) < 255) {
|
||||
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, outerRadius - borderWidthPx, alphaPaint);
|
||||
}
|
||||
if (showOldColor) {
|
||||
canvas.drawArc(centerRect, 90, 180, true, originalPaint);
|
||||
canvas.drawArc(centerRect, 270, 180, true, colorPaint);
|
||||
} else {
|
||||
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, outerRadius - borderWidthPx, colorPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (shape == ColorShape.SQUARE) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
setMeasuredDimension(width, height);
|
||||
} else if (shape == ColorShape.CIRCLE) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
|
||||
} else {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
if (shape == ColorShape.SQUARE || showOldColor) {
|
||||
drawingRect = new Rect();
|
||||
drawingRect.left = getPaddingLeft();
|
||||
drawingRect.right = w - getPaddingRight();
|
||||
drawingRect.top = getPaddingTop();
|
||||
drawingRect.bottom = h - getPaddingBottom();
|
||||
if (showOldColor) {
|
||||
setUpCenterRect();
|
||||
} else {
|
||||
setUpColorRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpCenterRect() {
|
||||
final Rect dRect = drawingRect;
|
||||
int left = dRect.left + borderWidthPx;
|
||||
int top = dRect.top + borderWidthPx;
|
||||
int bottom = dRect.bottom - borderWidthPx;
|
||||
int right = dRect.right - borderWidthPx;
|
||||
centerRect = new RectF(left, top, right, bottom);
|
||||
}
|
||||
|
||||
private void setUpColorRect() {
|
||||
final Rect dRect = drawingRect;
|
||||
int left = dRect.left + borderWidthPx;
|
||||
int top = dRect.top + borderWidthPx;
|
||||
int bottom = dRect.bottom - borderWidthPx;
|
||||
int right = dRect.right - borderWidthPx;
|
||||
colorRect = new Rect(left, top, right, bottom);
|
||||
alphaPattern = new AlphaPatternDrawable(DrawingUtils.dpToPx(getContext(), 4));
|
||||
alphaPattern.setBounds(Math.round(colorRect.left), Math.round(colorRect.top), Math.round(colorRect.right),
|
||||
Math.round(colorRect.bottom));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color currently show by this view.
|
||||
*
|
||||
* @return the color value
|
||||
*/
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color that should be shown by this view.
|
||||
*
|
||||
* @param color the color value
|
||||
*/
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the original color. This is only used for previewing colors.
|
||||
*
|
||||
* @param color The original color
|
||||
*/
|
||||
public void setOriginalColor(@ColorInt int color) {
|
||||
if (originalPaint != null) {
|
||||
originalPaint.setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the color of the border surrounding the panel.
|
||||
*/
|
||||
public int getBorderColor() {
|
||||
return borderColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color of the border surrounding the panel.
|
||||
*
|
||||
* @param color the color value
|
||||
*/
|
||||
public void setBorderColor(int color) {
|
||||
borderColor = color;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shape
|
||||
*
|
||||
* @return Either {@link ColorShape#SQUARE} or {@link ColorShape#CIRCLE}.
|
||||
*/
|
||||
@ColorShape
|
||||
public int getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shape.
|
||||
*
|
||||
* @param shape Either {@link ColorShape#SQUARE} or {@link ColorShape#CIRCLE}.
|
||||
*/
|
||||
public void setShape(@ColorShape int shape) {
|
||||
this.shape = shape;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a toast message with the hex color code below the view.
|
||||
*/
|
||||
public void showHint() {
|
||||
final int[] screenPos = new int[2];
|
||||
final Rect displayFrame = new Rect();
|
||||
getLocationOnScreen(screenPos);
|
||||
getWindowVisibleDisplayFrame(displayFrame);
|
||||
final Context context = getContext();
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
final int midy = screenPos[1] + height / 2;
|
||||
int referenceX = screenPos[0] + width / 2;
|
||||
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||
referenceX = screenWidth - referenceX; // mirror
|
||||
}
|
||||
StringBuilder hint = new StringBuilder("#");
|
||||
if (Color.alpha(color) != 255) {
|
||||
hint.append(Integer.toHexString(color).toUpperCase(Locale.ENGLISH));
|
||||
} else {
|
||||
hint.append(String.format("%06X", 0xFFFFFF & color).toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
Toast cheatSheet = Toast.makeText(context, hint.toString(), Toast.LENGTH_SHORT);
|
||||
if (midy < displayFrame.height()) {
|
||||
// Show along the top; follow action buttons
|
||||
cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX, screenPos[1] + height - displayFrame.top);
|
||||
} else {
|
||||
// Show along the bottom center
|
||||
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
|
||||
}
|
||||
cheatSheet.show();
|
||||
}
|
||||
}
|
@ -0,0 +1,968 @@
|
||||
/*
|
||||
* 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.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* <p>A dialog to pick a color.</p>
|
||||
*
|
||||
* <p>The {@link Activity activity} that shows this dialog should implement {@link ColorPickerDialogListener}</p>
|
||||
*
|
||||
* <p>Example usage:</p>
|
||||
*
|
||||
* <pre>
|
||||
* ColorPickerDialog.newBuilder().show(activity);
|
||||
* </pre>
|
||||
*/
|
||||
public class ColorPickerDialog extends DialogFragment implements ColorPickerView.OnColorChangedListener, TextWatcher {
|
||||
|
||||
public static final int TYPE_CUSTOM = 0;
|
||||
public static final int TYPE_PRESETS = 1;
|
||||
/**
|
||||
* Material design colors used as the default color presets
|
||||
*/
|
||||
public static final int[] MATERIAL_COLORS = {
|
||||
0xFFF44336, // RED 500
|
||||
0xFFE91E63, // PINK 500
|
||||
0xFFFF2C93, // LIGHT PINK 500
|
||||
0xFF9C27B0, // PURPLE 500
|
||||
0xFF673AB7, // DEEP PURPLE 500
|
||||
0xFF3F51B5, // INDIGO 500
|
||||
0xFF2196F3, // BLUE 500
|
||||
0xFF03A9F4, // LIGHT BLUE 500
|
||||
0xFF00BCD4, // CYAN 500
|
||||
0xFF009688, // TEAL 500
|
||||
0xFF4CAF50, // GREEN 500
|
||||
0xFF8BC34A, // LIGHT GREEN 500
|
||||
0xFFCDDC39, // LIME 500
|
||||
0xFFFFEB3B, // YELLOW 500
|
||||
0xFFFFC107, // AMBER 500
|
||||
0xFFFF9800, // ORANGE 500
|
||||
0xFF795548, // BROWN 500
|
||||
0xFF607D8B, // BLUE GREY 500
|
||||
0xFF9E9E9E, // GREY 500
|
||||
};
|
||||
static final int ALPHA_THRESHOLD = 165;
|
||||
private static final String TAG = "ColorPickerDialog";
|
||||
private static final String ARG_ID = "id";
|
||||
private static final String ARG_TYPE = "dialogType";
|
||||
private static final String ARG_COLOR = "color";
|
||||
private static final String ARG_ALPHA = "alpha";
|
||||
private static final String ARG_PRESETS = "presets";
|
||||
private static final String ARG_ALLOW_PRESETS = "allowPresets";
|
||||
private static final String ARG_ALLOW_CUSTOM = "allowCustom";
|
||||
private static final String ARG_DIALOG_TITLE = "dialogTitle";
|
||||
private static final String ARG_SHOW_COLOR_SHADES = "showColorShades";
|
||||
private static final String ARG_COLOR_SHAPE = "colorShape";
|
||||
private static final String ARG_PRESETS_BUTTON_TEXT = "presetsButtonText";
|
||||
private static final String ARG_CUSTOM_BUTTON_TEXT = "customButtonText";
|
||||
private static final String ARG_SELECTED_BUTTON_TEXT = "selectedButtonText";
|
||||
|
||||
ColorPickerDialogListener colorPickerDialogListener;
|
||||
FrameLayout rootView;
|
||||
int[] presets;
|
||||
@ColorInt
|
||||
int color;
|
||||
int dialogType;
|
||||
int dialogId;
|
||||
boolean showColorShades;
|
||||
int colorShape;
|
||||
|
||||
// -- PRESETS --------------------------
|
||||
ColorPaletteAdapter adapter;
|
||||
LinearLayout shadesLayout;
|
||||
SeekBar transparencySeekBar;
|
||||
TextView transparencyPercText;
|
||||
|
||||
// -- CUSTOM ---------------------------
|
||||
ColorPickerView colorPicker;
|
||||
ColorPanelView newColorPanel;
|
||||
EditText hexEditText;
|
||||
private final OnTouchListener onPickerTouchListener = new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (v != hexEditText && hexEditText.hasFocus()) {
|
||||
hexEditText.clearFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(hexEditText.getWindowToken(), 0);
|
||||
hexEditText.clearFocus();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
boolean showAlphaSlider;
|
||||
private int presetsButtonStringRes;
|
||||
private boolean fromEditText;
|
||||
private int customButtonStringRes;
|
||||
|
||||
/**
|
||||
* Create a new Builder for creating a {@link ColorPickerDialog} instance
|
||||
*
|
||||
* @return The {@link Builder builder} to create the {@link ColorPickerDialog}.
|
||||
*/
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
dialogId = getArguments().getInt(ARG_ID);
|
||||
showAlphaSlider = getArguments().getBoolean(ARG_ALPHA);
|
||||
showColorShades = getArguments().getBoolean(ARG_SHOW_COLOR_SHADES);
|
||||
colorShape = getArguments().getInt(ARG_COLOR_SHAPE);
|
||||
if (savedInstanceState == null) {
|
||||
color = getArguments().getInt(ARG_COLOR);
|
||||
dialogType = getArguments().getInt(ARG_TYPE);
|
||||
} else {
|
||||
color = savedInstanceState.getInt(ARG_COLOR);
|
||||
dialogType = savedInstanceState.getInt(ARG_TYPE);
|
||||
}
|
||||
|
||||
rootView = new FrameLayout(requireActivity());
|
||||
if (dialogType == TYPE_CUSTOM) {
|
||||
rootView.addView(createPickerView());
|
||||
} else if (dialogType == TYPE_PRESETS) {
|
||||
rootView.addView(createPresetsView());
|
||||
}
|
||||
|
||||
int selectedButtonStringRes = getArguments().getInt(ARG_SELECTED_BUTTON_TEXT);
|
||||
if (selectedButtonStringRes == 0) {
|
||||
selectedButtonStringRes = R.string.cpv_select;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()).setView(rootView)
|
||||
.setPositiveButton(selectedButtonStringRes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
onColorSelected(color);
|
||||
}
|
||||
});
|
||||
|
||||
int dialogTitleStringRes = getArguments().getInt(ARG_DIALOG_TITLE);
|
||||
if (dialogTitleStringRes != 0) {
|
||||
builder.setTitle(dialogTitleStringRes);
|
||||
}
|
||||
|
||||
presetsButtonStringRes = getArguments().getInt(ARG_PRESETS_BUTTON_TEXT);
|
||||
customButtonStringRes = getArguments().getInt(ARG_CUSTOM_BUTTON_TEXT);
|
||||
|
||||
int neutralButtonStringRes;
|
||||
if (dialogType == TYPE_CUSTOM && getArguments().getBoolean(ARG_ALLOW_PRESETS)) {
|
||||
neutralButtonStringRes = (presetsButtonStringRes != 0 ? presetsButtonStringRes : R.string.cpv_presets);
|
||||
} else if (dialogType == TYPE_PRESETS && getArguments().getBoolean(ARG_ALLOW_CUSTOM)) {
|
||||
neutralButtonStringRes = (customButtonStringRes != 0 ? customButtonStringRes : R.string.cpv_custom);
|
||||
} else {
|
||||
neutralButtonStringRes = 0;
|
||||
}
|
||||
|
||||
if (neutralButtonStringRes != 0) {
|
||||
builder.setNeutralButton(neutralButtonStringRes, null);
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
AlertDialog dialog = (AlertDialog) getDialog();
|
||||
|
||||
// http://stackoverflow.com/a/16972670/1048340
|
||||
//noinspection ConstantConditions
|
||||
dialog.getWindow()
|
||||
.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||
|
||||
// Do not dismiss the dialog when clicking the neutral button.
|
||||
Button neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||
if (neutralButton != null) {
|
||||
neutralButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
rootView.removeAllViews();
|
||||
switch (dialogType) {
|
||||
case TYPE_CUSTOM:
|
||||
dialogType = TYPE_PRESETS;
|
||||
((Button) v).setText(customButtonStringRes != 0 ? customButtonStringRes : R.string.cpv_custom);
|
||||
rootView.addView(createPresetsView());
|
||||
break;
|
||||
case TYPE_PRESETS:
|
||||
dialogType = TYPE_CUSTOM;
|
||||
((Button) v).setText(presetsButtonStringRes != 0 ? presetsButtonStringRes : R.string.cpv_presets);
|
||||
rootView.addView(createPickerView());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
onDialogDismissed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(ARG_COLOR, color);
|
||||
outState.putInt(ARG_TYPE, dialogType);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback.
|
||||
* <p/>
|
||||
* Note: The preferred way to handle the callback is to have the calling Activity implement
|
||||
* {@link ColorPickerDialogListener} as this will not survive an orientation change.
|
||||
*
|
||||
* @param colorPickerDialogListener The callback invoked when a color is selected or the dialog is dismissed.
|
||||
*/
|
||||
public void setColorPickerDialogListener(ColorPickerDialogListener colorPickerDialogListener) {
|
||||
this.colorPickerDialogListener = colorPickerDialogListener;
|
||||
}
|
||||
|
||||
// region Custom Picker
|
||||
|
||||
View createPickerView() {
|
||||
View contentView = View.inflate(getActivity(), R.layout.cpv_dialog_color_picker, null);
|
||||
colorPicker = contentView.findViewById(R.id.cpv_color_picker_view);
|
||||
ColorPanelView oldColorPanel = contentView.findViewById(R.id.cpv_color_panel_old);
|
||||
newColorPanel = contentView.findViewById(R.id.cpv_color_panel_new);
|
||||
ImageView arrowRight = contentView.findViewById(R.id.cpv_arrow_right);
|
||||
hexEditText = contentView.findViewById(R.id.cpv_hex);
|
||||
|
||||
try {
|
||||
final TypedValue value = new TypedValue();
|
||||
TypedArray typedArray =
|
||||
getActivity().obtainStyledAttributes(value.data, new int[]{android.R.attr.textColorPrimary});
|
||||
int arrowColor = typedArray.getColor(0, Color.BLACK);
|
||||
typedArray.recycle();
|
||||
arrowRight.setColorFilter(arrowColor);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
colorPicker.setAlphaSliderVisible(showAlphaSlider);
|
||||
oldColorPanel.setColor(getArguments().getInt(ARG_COLOR));
|
||||
colorPicker.setColor(color, true);
|
||||
newColorPanel.setColor(color);
|
||||
setHex(color);
|
||||
|
||||
if (!showAlphaSlider) {
|
||||
hexEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(6)});
|
||||
}
|
||||
|
||||
newColorPanel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (newColorPanel.getColor() == color) {
|
||||
onColorSelected(color);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
contentView.setOnTouchListener(onPickerTouchListener);
|
||||
colorPicker.setOnColorChangedListener(this);
|
||||
hexEditText.addTextChangedListener(this);
|
||||
|
||||
hexEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(hexEditText, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return contentView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColorChanged(int newColor) {
|
||||
color = newColor;
|
||||
if (newColorPanel != null) {
|
||||
newColorPanel.setColor(newColor);
|
||||
}
|
||||
if (!fromEditText && hexEditText != null) {
|
||||
setHex(newColor);
|
||||
if (hexEditText.hasFocus()) {
|
||||
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(hexEditText.getWindowToken(), 0);
|
||||
hexEditText.clearFocus();
|
||||
}
|
||||
}
|
||||
fromEditText = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (hexEditText.isFocused()) {
|
||||
int color = parseColorString(s.toString());
|
||||
if (color != colorPicker.getColor()) {
|
||||
fromEditText = true;
|
||||
colorPicker.setColor(color, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setHex(int color) {
|
||||
if (showAlphaSlider) {
|
||||
hexEditText.setText(String.format("%08X", (color)));
|
||||
} else {
|
||||
hexEditText.setText(String.format("%06X", (0xFFFFFF & color)));
|
||||
}
|
||||
}
|
||||
|
||||
private int parseColorString(String colorString) throws NumberFormatException {
|
||||
int a, r, g, b = 0;
|
||||
if (colorString.startsWith("#")) {
|
||||
colorString = colorString.substring(1);
|
||||
}
|
||||
if (colorString.length() == 0) {
|
||||
r = 0;
|
||||
a = 255;
|
||||
g = 0;
|
||||
} else if (colorString.length() <= 2) {
|
||||
a = 255;
|
||||
r = 0;
|
||||
b = Integer.parseInt(colorString, 16);
|
||||
g = 0;
|
||||
} else if (colorString.length() == 3) {
|
||||
a = 255;
|
||||
r = Integer.parseInt(colorString.substring(0, 1), 16);
|
||||
g = Integer.parseInt(colorString.substring(1, 2), 16);
|
||||
b = Integer.parseInt(colorString.substring(2, 3), 16);
|
||||
} else if (colorString.length() == 4) {
|
||||
a = 255;
|
||||
r = Integer.parseInt(colorString.substring(0, 2), 16);
|
||||
g = r;
|
||||
r = 0;
|
||||
b = Integer.parseInt(colorString.substring(2, 4), 16);
|
||||
} else if (colorString.length() == 5) {
|
||||
a = 255;
|
||||
r = Integer.parseInt(colorString.substring(0, 1), 16);
|
||||
g = Integer.parseInt(colorString.substring(1, 3), 16);
|
||||
b = Integer.parseInt(colorString.substring(3, 5), 16);
|
||||
} else if (colorString.length() == 6) {
|
||||
a = 255;
|
||||
r = Integer.parseInt(colorString.substring(0, 2), 16);
|
||||
g = Integer.parseInt(colorString.substring(2, 4), 16);
|
||||
b = Integer.parseInt(colorString.substring(4, 6), 16);
|
||||
} else if (colorString.length() == 7) {
|
||||
a = Integer.parseInt(colorString.substring(0, 1), 16);
|
||||
r = Integer.parseInt(colorString.substring(1, 3), 16);
|
||||
g = Integer.parseInt(colorString.substring(3, 5), 16);
|
||||
b = Integer.parseInt(colorString.substring(5, 7), 16);
|
||||
} else if (colorString.length() == 8) {
|
||||
a = Integer.parseInt(colorString.substring(0, 2), 16);
|
||||
r = Integer.parseInt(colorString.substring(2, 4), 16);
|
||||
g = Integer.parseInt(colorString.substring(4, 6), 16);
|
||||
b = Integer.parseInt(colorString.substring(6, 8), 16);
|
||||
} else {
|
||||
b = -1;
|
||||
g = -1;
|
||||
r = -1;
|
||||
a = -1;
|
||||
}
|
||||
return Color.argb(a, r, g, b);
|
||||
}
|
||||
|
||||
// -- endregion --
|
||||
|
||||
// region Presets Picker
|
||||
|
||||
View createPresetsView() {
|
||||
View contentView = View.inflate(getActivity(), R.layout.cpv_dialog_presets, null);
|
||||
shadesLayout = contentView.findViewById(R.id.shades_layout);
|
||||
transparencySeekBar = contentView.findViewById(R.id.transparency_seekbar);
|
||||
transparencyPercText = contentView.findViewById(R.id.transparency_text);
|
||||
GridView gridView = contentView.findViewById(R.id.gridView);
|
||||
|
||||
loadPresets();
|
||||
|
||||
if (showColorShades) {
|
||||
createColorShades(color);
|
||||
} else {
|
||||
shadesLayout.setVisibility(View.GONE);
|
||||
contentView.findViewById(R.id.shades_divider).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
adapter = new ColorPaletteAdapter(new ColorPaletteAdapter.OnColorSelectedListener() {
|
||||
@Override
|
||||
public void onColorSelected(int newColor) {
|
||||
if (color == newColor) {
|
||||
// Double tab selects the color
|
||||
ColorPickerDialog.this.onColorSelected(color);
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
color = newColor;
|
||||
if (showColorShades) {
|
||||
createColorShades(color);
|
||||
}
|
||||
}
|
||||
}, presets, getSelectedItemPosition(), colorShape);
|
||||
|
||||
gridView.setAdapter(adapter);
|
||||
|
||||
if (showAlphaSlider) {
|
||||
setupTransparency();
|
||||
} else {
|
||||
contentView.findViewById(R.id.transparency_layout).setVisibility(View.GONE);
|
||||
contentView.findViewById(R.id.transparency_title).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return contentView;
|
||||
}
|
||||
|
||||
private void loadPresets() {
|
||||
int alpha = Color.alpha(color);
|
||||
presets = getArguments().getIntArray(ARG_PRESETS);
|
||||
if (presets == null) presets = MATERIAL_COLORS;
|
||||
boolean isMaterialColors = presets == MATERIAL_COLORS;
|
||||
presets = Arrays.copyOf(presets, presets.length); // don't update the original array when modifying alpha
|
||||
if (alpha != 255) {
|
||||
// add alpha to the presets
|
||||
for (int i = 0; i < presets.length; i++) {
|
||||
int color = presets[i];
|
||||
int red = Color.red(color);
|
||||
int green = Color.green(color);
|
||||
int blue = Color.blue(color);
|
||||
presets[i] = Color.argb(alpha, red, green, blue);
|
||||
}
|
||||
}
|
||||
presets = unshiftIfNotExists(presets, color);
|
||||
int initialColor = getArguments().getInt(ARG_COLOR);
|
||||
if (initialColor != color) {
|
||||
// The user clicked a color and a configuration change occurred. Make sure the initial color is in the presets
|
||||
presets = unshiftIfNotExists(presets, initialColor);
|
||||
}
|
||||
if (isMaterialColors && presets.length == 19) {
|
||||
// Add black to have a total of 20 colors if the current color is in the material color palette
|
||||
presets = pushIfNotExists(presets, Color.argb(alpha, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void createColorShades(@ColorInt final int color) {
|
||||
final int[] colorShades = getColorShades(color);
|
||||
|
||||
if (shadesLayout.getChildCount() != 0) {
|
||||
for (int i = 0; i < shadesLayout.getChildCount(); i++) {
|
||||
FrameLayout layout = (FrameLayout) shadesLayout.getChildAt(i);
|
||||
final ColorPanelView cpv = layout.findViewById(R.id.cpv_color_panel_view);
|
||||
ImageView iv = layout.findViewById(R.id.cpv_color_image_view);
|
||||
cpv.setColor(colorShades[i]);
|
||||
cpv.setTag(false);
|
||||
iv.setImageDrawable(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int horizontalPadding = getResources().getDimensionPixelSize(R.dimen.cpv_item_horizontal_padding);
|
||||
|
||||
for (final int colorShade : colorShades) {
|
||||
int layoutResId;
|
||||
if (colorShape == ColorShape.SQUARE) {
|
||||
layoutResId = R.layout.cpv_color_item_square;
|
||||
} else {
|
||||
layoutResId = R.layout.cpv_color_item_circle;
|
||||
}
|
||||
|
||||
final View view = View.inflate(getActivity(), layoutResId, null);
|
||||
final ColorPanelView colorPanelView = view.findViewById(R.id.cpv_color_panel_view);
|
||||
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) colorPanelView.getLayoutParams();
|
||||
params.leftMargin = params.rightMargin = horizontalPadding;
|
||||
colorPanelView.setLayoutParams(params);
|
||||
colorPanelView.setColor(colorShade);
|
||||
shadesLayout.addView(view);
|
||||
|
||||
colorPanelView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// The color is black when rotating the dialog. This is a dirty fix. WTF!?
|
||||
colorPanelView.setColor(colorShade);
|
||||
}
|
||||
});
|
||||
|
||||
colorPanelView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getTag() instanceof Boolean && (Boolean) v.getTag()) {
|
||||
onColorSelected(ColorPickerDialog.this.color);
|
||||
dismiss();
|
||||
return; // already selected
|
||||
}
|
||||
ColorPickerDialog.this.color = colorPanelView.getColor();
|
||||
adapter.selectNone();
|
||||
for (int i = 0; i < shadesLayout.getChildCount(); i++) {
|
||||
FrameLayout layout = (FrameLayout) shadesLayout.getChildAt(i);
|
||||
ColorPanelView cpv = layout.findViewById(R.id.cpv_color_panel_view);
|
||||
ImageView iv = layout.findViewById(R.id.cpv_color_image_view);
|
||||
iv.setImageResource(cpv == v ? R.drawable.cpv_preset_checked : 0);
|
||||
if (cpv == v && ColorUtils.calculateLuminance(cpv.getColor()) >= 0.65
|
||||
|| Color.alpha(cpv.getColor()) <= ALPHA_THRESHOLD) {
|
||||
iv.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
iv.setColorFilter(null);
|
||||
}
|
||||
cpv.setTag(cpv == v);
|
||||
}
|
||||
}
|
||||
});
|
||||
colorPanelView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
colorPanelView.showHint();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void onColorSelected(int color) {
|
||||
if (colorPickerDialogListener != null) {
|
||||
Log.w(TAG, "Using deprecated listener which may be remove in future releases");
|
||||
colorPickerDialogListener.onColorSelected(dialogId, color);
|
||||
return;
|
||||
}
|
||||
Activity activity = getActivity();
|
||||
if (activity instanceof ColorPickerDialogListener) {
|
||||
((ColorPickerDialogListener) activity).onColorSelected(dialogId, color);
|
||||
} else {
|
||||
throw new IllegalStateException("The activity must implement ColorPickerDialogListener");
|
||||
}
|
||||
}
|
||||
|
||||
private void onDialogDismissed() {
|
||||
if (colorPickerDialogListener != null) {
|
||||
Log.w(TAG, "Using deprecated listener which may be remove in future releases");
|
||||
colorPickerDialogListener.onDialogDismissed(dialogId);
|
||||
return;
|
||||
}
|
||||
Activity activity = getActivity();
|
||||
if (activity instanceof ColorPickerDialogListener) {
|
||||
((ColorPickerDialogListener) activity).onDialogDismissed(dialogId);
|
||||
}
|
||||
}
|
||||
|
||||
private int shadeColor(@ColorInt int color, double percent) {
|
||||
String hex = String.format("#%06X", (0xFFFFFF & color));
|
||||
long f = Long.parseLong(hex.substring(1), 16);
|
||||
double t = percent < 0 ? 0 : 255;
|
||||
double p = percent < 0 ? percent * -1 : percent;
|
||||
long R = f >> 16;
|
||||
long G = f >> 8 & 0x00FF;
|
||||
long B = f & 0x0000FF;
|
||||
int alpha = Color.alpha(color);
|
||||
int red = (int) (Math.round((t - R) * p) + R);
|
||||
int green = (int) (Math.round((t - G) * p) + G);
|
||||
int blue = (int) (Math.round((t - B) * p) + B);
|
||||
return Color.argb(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
private int[] getColorShades(@ColorInt int color) {
|
||||
return new int[]{
|
||||
shadeColor(color, 0.9), shadeColor(color, 0.7), shadeColor(color, 0.5), shadeColor(color, 0.333),
|
||||
shadeColor(color, 0.166), shadeColor(color, -0.125), shadeColor(color, -0.25), shadeColor(color, -0.375),
|
||||
shadeColor(color, -0.5), shadeColor(color, -0.675), shadeColor(color, -0.7), shadeColor(color, -0.775),
|
||||
};
|
||||
}
|
||||
|
||||
private void setupTransparency() {
|
||||
int progress = 255 - Color.alpha(color);
|
||||
transparencySeekBar.setMax(255);
|
||||
transparencySeekBar.setProgress(progress);
|
||||
int percentage = (int) ((double) progress * 100 / 255);
|
||||
transparencyPercText.setText(String.format(Locale.ENGLISH, "%d%%", percentage));
|
||||
transparencySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
int percentage = (int) ((double) progress * 100 / 255);
|
||||
transparencyPercText.setText(String.format(Locale.ENGLISH, "%d%%", percentage));
|
||||
int alpha = 255 - progress;
|
||||
// update items in GridView:
|
||||
for (int i = 0; i < adapter.colors.length; i++) {
|
||||
int color = adapter.colors[i];
|
||||
int red = Color.red(color);
|
||||
int green = Color.green(color);
|
||||
int blue = Color.blue(color);
|
||||
adapter.colors[i] = Color.argb(alpha, red, green, blue);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
// update shades:
|
||||
for (int i = 0; i < shadesLayout.getChildCount(); i++) {
|
||||
FrameLayout layout = (FrameLayout) shadesLayout.getChildAt(i);
|
||||
ColorPanelView cpv = layout.findViewById(R.id.cpv_color_panel_view);
|
||||
ImageView iv = layout.findViewById(R.id.cpv_color_image_view);
|
||||
if (layout.getTag() == null) {
|
||||
// save the original border color
|
||||
layout.setTag(cpv.getBorderColor());
|
||||
}
|
||||
int color = cpv.getColor();
|
||||
color = Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||
if (alpha <= ALPHA_THRESHOLD) {
|
||||
cpv.setBorderColor(color | 0xFF000000);
|
||||
} else {
|
||||
cpv.setBorderColor((int) layout.getTag());
|
||||
}
|
||||
if (cpv.getTag() != null && (Boolean) cpv.getTag()) {
|
||||
// The alpha changed on the selected shaded color. Update the checkmark color filter.
|
||||
if (alpha <= ALPHA_THRESHOLD) {
|
||||
iv.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
if (ColorUtils.calculateLuminance(color) >= 0.65) {
|
||||
iv.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
iv.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
cpv.setColor(color);
|
||||
}
|
||||
// update color:
|
||||
int red = Color.red(color);
|
||||
int green = Color.green(color);
|
||||
int blue = Color.blue(color);
|
||||
color = Color.argb(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int[] unshiftIfNotExists(int[] array, int value) {
|
||||
boolean present = false;
|
||||
for (int i : array) {
|
||||
if (i == value) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!present) {
|
||||
int[] newArray = new int[array.length + 1];
|
||||
newArray[0] = value;
|
||||
System.arraycopy(array, 0, newArray, 1, newArray.length - 1);
|
||||
return newArray;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private int[] pushIfNotExists(int[] array, int value) {
|
||||
boolean present = false;
|
||||
for (int i : array) {
|
||||
if (i == value) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!present) {
|
||||
int[] newArray = new int[array.length + 1];
|
||||
newArray[newArray.length - 1] = value;
|
||||
System.arraycopy(array, 0, newArray, 0, newArray.length - 1);
|
||||
return newArray;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private int getSelectedItemPosition() {
|
||||
for (int i = 0; i < presets.length; i++) {
|
||||
if (presets[i] == color) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Builder
|
||||
|
||||
@IntDef({TYPE_CUSTOM, TYPE_PRESETS})
|
||||
public @interface DialogType {
|
||||
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
ColorPickerDialogListener colorPickerDialogListener;
|
||||
@StringRes
|
||||
int dialogTitle = R.string.cpv_default_title;
|
||||
@StringRes
|
||||
int presetsButtonText = R.string.cpv_presets;
|
||||
@StringRes
|
||||
int customButtonText = R.string.cpv_custom;
|
||||
@StringRes
|
||||
int selectedButtonText = R.string.cpv_select;
|
||||
@DialogType
|
||||
int dialogType = TYPE_PRESETS;
|
||||
int[] presets = MATERIAL_COLORS;
|
||||
@ColorInt
|
||||
int color = Color.BLACK;
|
||||
int dialogId = 0;
|
||||
boolean showAlphaSlider = false;
|
||||
boolean allowPresets = true;
|
||||
boolean allowCustom = true;
|
||||
boolean showColorShades = true;
|
||||
@ColorShape
|
||||
int colorShape = ColorShape.CIRCLE;
|
||||
|
||||
/*package*/ Builder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dialog title string resource id
|
||||
*
|
||||
* @param dialogTitle The string resource used for the dialog title
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setDialogTitle(@StringRes int dialogTitle) {
|
||||
this.dialogTitle = dialogTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected button text string resource id
|
||||
*
|
||||
* @param selectedButtonText The string resource used for the selected button text
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setSelectedButtonText(@StringRes int selectedButtonText) {
|
||||
this.selectedButtonText = selectedButtonText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the presets button text string resource id
|
||||
*
|
||||
* @param presetsButtonText The string resource used for the presets button text
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setPresetsButtonText(@StringRes int presetsButtonText) {
|
||||
this.presetsButtonText = presetsButtonText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom button text string resource id
|
||||
*
|
||||
* @param customButtonText The string resource used for the custom button text
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setCustomButtonText(@StringRes int customButtonText) {
|
||||
this.customButtonText = customButtonText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which dialog view to show.
|
||||
*
|
||||
* @param dialogType Either {@link ColorPickerDialog#TYPE_CUSTOM} or {@link ColorPickerDialog#TYPE_PRESETS}.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setDialogType(@DialogType int dialogType) {
|
||||
this.dialogType = dialogType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors used for the presets
|
||||
*
|
||||
* @param presets An array of color ints.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setPresets(@NonNull int[] presets) {
|
||||
this.presets = presets;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the original color
|
||||
*
|
||||
* @param color The default color for the color picker
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setColor(int color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dialog id used for callbacks
|
||||
*
|
||||
* @param dialogId The id that is sent back to the {@link ColorPickerDialogListener}.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setDialogId(int dialogId) {
|
||||
this.dialogId = dialogId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the alpha slider
|
||||
*
|
||||
* @param showAlphaSlider {@code true} to show the alpha slider. Currently only supported with the {@link
|
||||
* ColorPickerView}.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setShowAlphaSlider(boolean showAlphaSlider) {
|
||||
this.showAlphaSlider = showAlphaSlider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/Hide a neutral button to select preset colors.
|
||||
*
|
||||
* @param allowPresets {@code false} to disable showing the presets button.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setAllowPresets(boolean allowPresets) {
|
||||
this.allowPresets = allowPresets;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/Hide the neutral button to select a custom color.
|
||||
*
|
||||
* @param allowCustom {@code false} to disable showing the custom button.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setAllowCustom(boolean allowCustom) {
|
||||
this.allowCustom = allowCustom;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/Hide the color shades in the presets picker
|
||||
*
|
||||
* @param showColorShades {@code false} to hide the color shades.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setShowColorShades(boolean showColorShades) {
|
||||
this.showColorShades = showColorShades;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shape of the color panel view.
|
||||
*
|
||||
* @param colorShape Either {@link ColorShape#CIRCLE} or {@link ColorShape#SQUARE}.
|
||||
* @return This builder object for chaining method calls
|
||||
*/
|
||||
public Builder setColorShape(int colorShape) {
|
||||
this.colorShape = colorShape;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link ColorPickerDialog} instance.
|
||||
*
|
||||
* @return A new {@link ColorPickerDialog}.
|
||||
* @see #show(FragmentActivity)
|
||||
*/
|
||||
public ColorPickerDialog create() {
|
||||
ColorPickerDialog dialog = new ColorPickerDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_ID, dialogId);
|
||||
args.putInt(ARG_TYPE, dialogType);
|
||||
args.putInt(ARG_COLOR, color);
|
||||
args.putIntArray(ARG_PRESETS, presets);
|
||||
args.putBoolean(ARG_ALPHA, showAlphaSlider);
|
||||
args.putBoolean(ARG_ALLOW_CUSTOM, allowCustom);
|
||||
args.putBoolean(ARG_ALLOW_PRESETS, allowPresets);
|
||||
args.putInt(ARG_DIALOG_TITLE, dialogTitle);
|
||||
args.putBoolean(ARG_SHOW_COLOR_SHADES, showColorShades);
|
||||
args.putInt(ARG_COLOR_SHAPE, colorShape);
|
||||
args.putInt(ARG_PRESETS_BUTTON_TEXT, presetsButtonText);
|
||||
args.putInt(ARG_CUSTOM_BUTTON_TEXT, customButtonText);
|
||||
args.putInt(ARG_SELECTED_BUTTON_TEXT, selectedButtonText);
|
||||
dialog.setArguments(args);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and show the {@link ColorPickerDialog} created with this builder.
|
||||
*
|
||||
* @param activity The current activity.
|
||||
*/
|
||||
public void show(FragmentActivity activity) {
|
||||
create().show(activity.getSupportFragmentManager(), "color-picker-dialog");
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 androidx.annotation.ColorInt;
|
||||
|
||||
/**
|
||||
* Callback used for getting the selected color from a color picker dialog.
|
||||
*/
|
||||
public interface ColorPickerDialogListener {
|
||||
|
||||
/**
|
||||
* Callback that is invoked when a color is selected from the color picker dialog.
|
||||
*
|
||||
* @param dialogId The dialog id used to create the dialog instance.
|
||||
* @param color The selected color
|
||||
*/
|
||||
void onColorSelected(int dialogId, @ColorInt int color);
|
||||
|
||||
/**
|
||||
* Callback that is invoked when the color picker dialog was dismissed.
|
||||
*
|
||||
* @param dialogId The dialog id used to create the dialog instance.
|
||||
*/
|
||||
void onDialogDismissed(int dialogId);
|
||||
}
|
@ -0,0 +1,972 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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.Color;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* A Preference to select a color
|
||||
*/
|
||||
public class ColorPreference extends Preference implements ColorPickerDialogListener {
|
||||
|
||||
private static final int SIZE_NORMAL = 0;
|
||||
private static final int SIZE_LARGE = 1;
|
||||
|
||||
private OnShowDialogListener onShowDialogListener;
|
||||
private int color = Color.BLACK;
|
||||
private boolean showDialog;
|
||||
@ColorPickerDialog.DialogType
|
||||
private int dialogType;
|
||||
private int colorShape;
|
||||
private boolean allowPresets;
|
||||
private boolean allowCustom;
|
||||
private boolean showAlphaSlider;
|
||||
private boolean showColorShades;
|
||||
private int previewSize;
|
||||
private int[] presets;
|
||||
private int dialogTitle;
|
||||
|
||||
public ColorPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public ColorPreference(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(AttributeSet attrs) {
|
||||
setPersistent(true);
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPreference);
|
||||
showDialog = a.getBoolean(R.styleable.ColorPreference_cpv_showDialog, true);
|
||||
//noinspection WrongConstant
|
||||
dialogType = a.getInt(R.styleable.ColorPreference_cpv_dialogType, ColorPickerDialog.TYPE_PRESETS);
|
||||
colorShape = a.getInt(R.styleable.ColorPreference_cpv_colorShape, ColorShape.CIRCLE);
|
||||
allowPresets = a.getBoolean(R.styleable.ColorPreference_cpv_allowPresets, true);
|
||||
allowCustom = a.getBoolean(R.styleable.ColorPreference_cpv_allowCustom, true);
|
||||
showAlphaSlider = a.getBoolean(R.styleable.ColorPreference_cpv_showAlphaSlider, false);
|
||||
showColorShades = a.getBoolean(R.styleable.ColorPreference_cpv_showColorShades, true);
|
||||
previewSize = a.getInt(R.styleable.ColorPreference_cpv_previewSize, SIZE_NORMAL);
|
||||
final int presetsResId = a.getResourceId(R.styleable.ColorPreference_cpv_colorPresets, 0);
|
||||
dialogTitle = a.getResourceId(R.styleable.ColorPreference_cpv_dialogTitle, R.string.cpv_default_title);
|
||||
if (presetsResId != 0) {
|
||||
presets = getContext().getResources().getIntArray(presetsResId);
|
||||
} else {
|
||||
presets = ColorPickerDialog.MATERIAL_COLORS;
|
||||
}
|
||||
if (colorShape == ColorShape.CIRCLE) {
|
||||
setWidgetLayoutResource(
|
||||
previewSize == SIZE_LARGE ? R.layout.cpv_preference_circle_large : R.layout.cpv_preference_circle);
|
||||
} else {
|
||||
setWidgetLayoutResource(
|
||||
previewSize == SIZE_LARGE ? R.layout.cpv_preference_square_large : R.layout.cpv_preference_square);
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
if (onShowDialogListener != null) {
|
||||
onShowDialogListener.onShowColorPickerDialog((String) getTitle(), color);
|
||||
} else if (showDialog) {
|
||||
ColorPickerDialog dialog = ColorPickerDialog.newBuilder()
|
||||
.setDialogType(dialogType)
|
||||
.setDialogTitle(dialogTitle)
|
||||
.setColorShape(colorShape)
|
||||
.setPresets(presets)
|
||||
.setAllowPresets(allowPresets)
|
||||
.setAllowCustom(allowCustom)
|
||||
.setShowAlphaSlider(showAlphaSlider)
|
||||
.setShowColorShades(showColorShades)
|
||||
.setColor(color)
|
||||
.create();
|
||||
dialog.setColorPickerDialogListener(this);
|
||||
FragmentActivity activity = (FragmentActivity) getContext();
|
||||
activity.getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.add(dialog, getFragmentTag())
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToActivity() {
|
||||
super.onAttachedToActivity();
|
||||
|
||||
if (showDialog) {
|
||||
FragmentActivity activity = (FragmentActivity) getContext();
|
||||
ColorPickerDialog fragment =
|
||||
(ColorPickerDialog) activity.getSupportFragmentManager().findFragmentByTag(getFragmentTag());
|
||||
if (fragment != null) {
|
||||
// re-bind preference to fragment
|
||||
fragment.setColorPickerDialogListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
ColorPanelView preview = view.findViewById(R.id.cpv_preference_preview_color_panel);
|
||||
if (preview != null) {
|
||||
preview.setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
|
||||
if (restorePersistedValue) {
|
||||
color = getPersistedInt(0xFF000000);
|
||||
} else {
|
||||
color = (Integer) defaultValue;
|
||||
persistInt(color);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int index) {
|
||||
return a.getInteger(index, Color.BLACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColorSelected(int dialogId, @ColorInt int color) {
|
||||
saveValue(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogDismissed(int dialogId) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new color
|
||||
*
|
||||
* @param color The newly selected color
|
||||
*/
|
||||
public void saveValue(@ColorInt int color) {
|
||||
this.color = color;
|
||||
persistInt(this.color);
|
||||
notifyChanged();
|
||||
callChangeListener(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color of the key. This should be one of the entries in {@link #getPresets()}.
|
||||
*
|
||||
* @return The color of the key
|
||||
*/
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color selected. This should be one of the entries in {@link #getPresets()}.
|
||||
*
|
||||
* @param color The color to set for the key
|
||||
*/
|
||||
public void setColor(@ColorInt int color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the colors that will be shown in the {@link ColorPickerDialog}.
|
||||
*
|
||||
* @return An array of color ints
|
||||
*/
|
||||
public int[] getPresets() {
|
||||
return presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors shown in the {@link ColorPickerDialog}.
|
||||
*
|
||||
* @param presets An array of color ints
|
||||
*/
|
||||
public void setPresets(@NonNull int[] presets) {
|
||||
this.presets = presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener used for showing the {@link ColorPickerDialog}.
|
||||
* Call {@link #saveValue(int)} after the user chooses a color.
|
||||
* If this is set then it is up to you to show the dialog.
|
||||
*
|
||||
* @param listener The listener to show the dialog
|
||||
*/
|
||||
public void setOnShowDialogListener(OnShowDialogListener listener) {
|
||||
onShowDialogListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tag used for the {@link ColorPickerDialog}.
|
||||
*
|
||||
* @return The tag
|
||||
*/
|
||||
public String getFragmentTag() {
|
||||
return "color_" + getKey();
|
||||
}
|
||||
|
||||
public interface OnShowDialogListener {
|
||||
|
||||
void onShowColorPickerDialog(String title, int currentColor);
|
||||
}
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
package com.jaredrummler.android.colorpicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
/**
|
||||
* A Preference to select a color
|
||||
*/
|
||||
public class ColorPreferenceCompat extends Preference implements ColorPickerDialogListener {
|
||||
|
||||
private static final int SIZE_NORMAL = 0;
|
||||
private static final int SIZE_LARGE = 1;
|
||||
|
||||
private OnShowDialogListener onShowDialogListener;
|
||||
private int color = Color.BLACK;
|
||||
private boolean showDialog;
|
||||
@ColorPickerDialog.DialogType
|
||||
private int dialogType;
|
||||
private int colorShape;
|
||||
private boolean allowPresets;
|
||||
private boolean allowCustom;
|
||||
private boolean showAlphaSlider;
|
||||
private boolean showColorShades;
|
||||
private int previewSize;
|
||||
private int[] presets;
|
||||
private int dialogTitle;
|
||||
|
||||
public ColorPreferenceCompat(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public ColorPreferenceCompat(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(AttributeSet attrs) {
|
||||
setPersistent(true);
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ColorPreference);
|
||||
showDialog = a.getBoolean(R.styleable.ColorPreference_cpv_showDialog, true);
|
||||
//noinspection WrongConstant
|
||||
dialogType = a.getInt(R.styleable.ColorPreference_cpv_dialogType, ColorPickerDialog.TYPE_PRESETS);
|
||||
colorShape = a.getInt(R.styleable.ColorPreference_cpv_colorShape, ColorShape.CIRCLE);
|
||||
allowPresets = a.getBoolean(R.styleable.ColorPreference_cpv_allowPresets, true);
|
||||
allowCustom = a.getBoolean(R.styleable.ColorPreference_cpv_allowCustom, true);
|
||||
showAlphaSlider = a.getBoolean(R.styleable.ColorPreference_cpv_showAlphaSlider, false);
|
||||
showColorShades = a.getBoolean(R.styleable.ColorPreference_cpv_showColorShades, true);
|
||||
previewSize = a.getInt(R.styleable.ColorPreference_cpv_previewSize, SIZE_NORMAL);
|
||||
final int presetsResId = a.getResourceId(R.styleable.ColorPreference_cpv_colorPresets, 0);
|
||||
dialogTitle = a.getResourceId(R.styleable.ColorPreference_cpv_dialogTitle, R.string.cpv_default_title);
|
||||
if (presetsResId != 0) {
|
||||
presets = getContext().getResources().getIntArray(presetsResId);
|
||||
} else {
|
||||
presets = ColorPickerDialog.MATERIAL_COLORS;
|
||||
}
|
||||
if (colorShape == ColorShape.CIRCLE) {
|
||||
setWidgetLayoutResource(
|
||||
previewSize == SIZE_LARGE ? R.layout.cpv_preference_circle_large : R.layout.cpv_preference_circle);
|
||||
} else {
|
||||
setWidgetLayoutResource(
|
||||
previewSize == SIZE_LARGE ? R.layout.cpv_preference_square_large : R.layout.cpv_preference_square);
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
if (onShowDialogListener != null) {
|
||||
onShowDialogListener.onShowColorPickerDialog((String) getTitle(), color);
|
||||
} else if (showDialog) {
|
||||
ColorPickerDialog dialog = ColorPickerDialog.newBuilder()
|
||||
.setDialogType(dialogType)
|
||||
.setDialogTitle(dialogTitle)
|
||||
.setColorShape(colorShape)
|
||||
.setPresets(presets)
|
||||
.setAllowPresets(allowPresets)
|
||||
.setAllowCustom(allowCustom)
|
||||
.setShowAlphaSlider(showAlphaSlider)
|
||||
.setShowColorShades(showColorShades)
|
||||
.setColor(color)
|
||||
.create();
|
||||
dialog.setColorPickerDialogListener(this);
|
||||
getActivity().getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.add(dialog, getFragmentTag())
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
public FragmentActivity getActivity() {
|
||||
Context context = getContext();
|
||||
if (context instanceof FragmentActivity) {
|
||||
return (FragmentActivity) context;
|
||||
} else if (context instanceof ContextWrapper) {
|
||||
Context baseContext = ((ContextWrapper) context).getBaseContext();
|
||||
if (baseContext instanceof FragmentActivity) {
|
||||
return (FragmentActivity) baseContext;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Error getting activity from context");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttached() {
|
||||
super.onAttached();
|
||||
if (showDialog) {
|
||||
ColorPickerDialog fragment =
|
||||
(ColorPickerDialog) getActivity().getSupportFragmentManager().findFragmentByTag(getFragmentTag());
|
||||
if (fragment != null) {
|
||||
// re-bind preference to fragment
|
||||
fragment.setColorPickerDialogListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
ColorPanelView preview = holder.itemView.findViewById(R.id.cpv_preference_preview_color_panel);
|
||||
if (preview != null) {
|
||||
preview.setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(Object defaultValue) {
|
||||
super.onSetInitialValue(defaultValue);
|
||||
if (defaultValue instanceof Integer) {
|
||||
color = (Integer) defaultValue;
|
||||
persistInt(color);
|
||||
} else {
|
||||
color = getPersistedInt(0xFF000000);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int index) {
|
||||
return a.getInteger(index, Color.BLACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColorSelected(int dialogId, @ColorInt int color) {
|
||||
saveValue(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogDismissed(int dialogId) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new color
|
||||
*
|
||||
* @param color The newly selected color
|
||||
*/
|
||||
public void saveValue(@ColorInt int color) {
|
||||
this.color = color;
|
||||
persistInt(this.color);
|
||||
notifyChanged();
|
||||
callChangeListener(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the colors that will be shown in the {@link ColorPickerDialog}.
|
||||
*
|
||||
* @return An array of color ints
|
||||
*/
|
||||
public int[] getPresets() {
|
||||
return presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors shown in the {@link ColorPickerDialog}.
|
||||
*
|
||||
* @param presets An array of color ints
|
||||
*/
|
||||
public void setPresets(@NonNull int[] presets) {
|
||||
this.presets = presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener used for showing the {@link ColorPickerDialog}.
|
||||
* Call {@link #saveValue(int)} after the user chooses a color.
|
||||
* If this is set then it is up to you to show the dialog.
|
||||
*
|
||||
* @param listener The listener to show the dialog
|
||||
*/
|
||||
public void setOnShowDialogListener(OnShowDialogListener listener) {
|
||||
onShowDialogListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color of the key. This should be one of the entries in {@link #getPresets()}.
|
||||
*
|
||||
* @return The color of the key
|
||||
*/
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color selected. This should be one of the entries in {@link #getPresets()}.
|
||||
*
|
||||
* @param color The color to set for the key
|
||||
*/
|
||||
public void setColor(@ColorInt int color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tag used for the {@link ColorPickerDialog}.
|
||||
*
|
||||
* @return The tag
|
||||
*/
|
||||
public String getFragmentTag() {
|
||||
return "color_" + getKey();
|
||||
}
|
||||
|
||||
public interface OnShowDialogListener {
|
||||
|
||||
void onShowColorPickerDialog(String title, int currentColor);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 androidx.annotation.IntDef;
|
||||
|
||||
/**
|
||||
* The shape of the color preview
|
||||
*/
|
||||
@IntDef({ColorShape.SQUARE, ColorShape.CIRCLE})
|
||||
public @interface ColorShape {
|
||||
|
||||
int SQUARE = 0;
|
||||
|
||||
int CIRCLE = 1;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
|
||||
final class DrawingUtils {
|
||||
|
||||
static int dpToPx(Context c, float dipValue) {
|
||||
DisplayMetrics metrics = c.getResources().getDisplayMetrics();
|
||||
float val = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics);
|
||||
int res = (int) (val + 0.5); // Round
|
||||
// Ensure at least 1 pixel if val was > 0
|
||||
return res == 0 && val > 0 ? 1 : res;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.util.AttributeSet;
|
||||
import android.widget.GridView;
|
||||
|
||||
import androidx.annotation.RestrictTo;
|
||||
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
public class NestedGridView extends GridView {
|
||||
|
||||
public NestedGridView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public NestedGridView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public NestedGridView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
|
||||
super.onMeasure(widthMeasureSpec, expandSpec);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 81 B |
After Width: | Height: | Size: 93 B |
After Width: | Height: | Size: 101 B |
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:drawable="@drawable/cpv_btn_background_pressed" android:state_focused="true" android:state_pressed="false" />
|
||||
<item android:drawable="@drawable/cpv_btn_background_pressed" android:state_focused="true" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/cpv_btn_background_pressed" android:state_focused="false" android:state_pressed="true" />
|
||||
</selector>
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:alpha="0.1"
|
||||
android:bottom="6dp"
|
||||
android:top="6dp">
|
||||
<shape>
|
||||
<solid android:color="#20666666" />
|
||||
<corners android:radius="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M22,12l-4,-4v3H3v2h15v3z" />
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
||||
</vector>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView
|
||||
android:id="@+id/cpv_color_panel_view"
|
||||
android:layout_width="@dimen/cpv_item_size"
|
||||
android:layout_height="@dimen/cpv_item_size"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="true"
|
||||
app:cpv_colorShape="circle" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cpv_color_image_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="false" />
|
||||
|
||||
</FrameLayout>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView
|
||||
android:id="@+id/cpv_color_panel_view"
|
||||
android:layout_width="@dimen/cpv_item_size"
|
||||
android:layout_height="@dimen/cpv_item_size"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="true"
|
||||
app:cpv_colorShape="square" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cpv_color_image_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="false" />
|
||||
|
||||
</FrameLayout>
|
@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPickerView
|
||||
android:id="@id/cpv_color_picker_view"
|
||||
style="@style/cpv_ColorPickerViewStyle"
|
||||
android:padding="16dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView
|
||||
android:id="@id/cpv_color_panel_old"
|
||||
android:layout_width="@dimen/cpv_dialog_preview_width"
|
||||
android:layout_height="@dimen/cpv_dialog_preview_height"
|
||||
app:cpv_colorShape="square" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cpv_arrow_right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:src="@drawable/cpv_ic_arrow_right_black_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView
|
||||
android:id="@id/cpv_color_panel_new"
|
||||
android:layout_width="@dimen/cpv_dialog_preview_width"
|
||||
android:layout_height="@dimen/cpv_dialog_preview_height"
|
||||
app:cpv_colorShape="square" />
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:descendantFocusability="beforeDescendants"
|
||||
android:focusableInTouchMode="true"
|
||||
android:gravity="right"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="#"
|
||||
android:typeface="monospace"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/cpv_hex"
|
||||
android:layout_width="110dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:digits="0123456789ABCDEFabcdef"
|
||||
android:focusable="true"
|
||||
android:imeOptions="actionGo"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:maxLength="8"
|
||||
android:maxLines="1"
|
||||
android:typeface="monospace" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.jaredrummler.android.colorpicker.NestedGridView
|
||||
android:id="@+id/gridView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:cacheColorHint="@android:color/transparent"
|
||||
android:clickable="false"
|
||||
android:columnWidth="@dimen/cpv_column_width"
|
||||
android:horizontalSpacing="4dp"
|
||||
android:listSelector="@android:color/transparent"
|
||||
android:numColumns="auto_fit"
|
||||
android:stretchMode="spacingWidthUniform"
|
||||
android:verticalSpacing="8dp" />
|
||||
|
||||
<View
|
||||
android:id="@+id/shades_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="?android:attr/dividerVertical" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shades_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- views added dynamically -->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transparency_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:text="@string/cpv_transparency" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/transparency_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="100">
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/transparency_seekbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_weight="85" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transparency_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="15"
|
||||
android:textStyle="bold"
|
||||
android:typeface="monospace"
|
||||
tools:text="100%" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -0,0 +1,6 @@
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@id/cpv_preference_preview_color_panel"
|
||||
android:layout_width="@dimen/cpv_color_preference_normal"
|
||||
android:layout_height="@dimen/cpv_color_preference_normal"
|
||||
app:cpv_colorShape="circle" />
|
@ -0,0 +1,6 @@
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@id/cpv_preference_preview_color_panel"
|
||||
android:layout_width="@dimen/cpv_color_preference_large"
|
||||
android:layout_height="@dimen/cpv_color_preference_large"
|
||||
app:cpv_colorShape="circle" />
|
@ -0,0 +1,6 @@
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@id/cpv_preference_preview_color_panel"
|
||||
android:layout_width="@dimen/cpv_color_preference_normal"
|
||||
android:layout_height="@dimen/cpv_color_preference_normal"
|
||||
app:cpv_colorShape="square" />
|
@ -0,0 +1,6 @@
|
||||
<com.jaredrummler.android.colorpicker.ColorPanelView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@id/cpv_preference_preview_color_panel"
|
||||
android:layout_width="@dimen/cpv_color_preference_large"
|
||||
android:layout_height="@dimen/cpv_color_preference_large"
|
||||
app:cpv_colorShape="square" />
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">اختر لونًا</string>
|
||||
<string name="cpv_presets">ألوان جاهزة</string>
|
||||
<string name="cpv_custom">ألوان معدلة</string>
|
||||
<string name="cpv_select">اختر</string>
|
||||
<string name="cpv_transparency">الشفافية</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Вылучыце колер</string>
|
||||
<string name="cpv_presets">Перадусталёўкі</string>
|
||||
<string name="cpv_custom">Асаблівы</string>
|
||||
<string name="cpv_select">Выбраць</string>
|
||||
<string name="cpv_transparency">Празрыстасць</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Vyberte barvu</string>
|
||||
<string name="cpv_presets">Přednastavené</string>
|
||||
<string name="cpv_custom">Vlastní</string>
|
||||
<string name="cpv_select">Vybrat</string>
|
||||
<string name="cpv_transparency">Průhlednost</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Farbe auswählen</string>
|
||||
<string name="cpv_presets">Voreinstellungen</string>
|
||||
<string name="cpv_custom">Benutzerdefiniert</string>
|
||||
<string name="cpv_select">Auswählen</string>
|
||||
<string name="cpv_transparency">Transparenz</string>
|
||||
</resources>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Selecciona un color</string>
|
||||
<string name="cpv_presets">Predeterminados</string>
|
||||
<string name="cpv_custom">Personalizar</string>
|
||||
<string name="cpv_select">Seleccionar</string>
|
||||
<string name="cpv_transparency">Transparencia</string>
|
||||
</resources>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Sélectionner une couleur</string>
|
||||
<string name="cpv_presets">Présélections</string>
|
||||
<string name="cpv_custom">Personnalisée</string>
|
||||
<string name="cpv_select">Valider</string>
|
||||
<string name="cpv_transparency">Transparence</string>
|
||||
</resources>
|
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<string name="cpv_custom">Personalizzato</string>
|
||||
<string name="cpv_default_title">Seleziona un Colore</string>
|
||||
<string name="cpv_presets">Predefiniti</string>
|
||||
<string name="cpv_select">Seleziona</string>
|
||||
<string name="cpv_transparency">Trasparenza</string>
|
||||
</resources>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cpv_default_title">בחירת צבע</string>
|
||||
<string name="cpv_presets">קבוע מראש</string>
|
||||
<string name="cpv_custom">מותאם אישית</string>
|
||||
<string name="cpv_select">בחירה</string>
|
||||
<string name="cpv_transparency">שקיפות</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">色を選択</string>
|
||||
<string name="cpv_presets">プリセット</string>
|
||||
<string name="cpv_custom">カスタム</string>
|
||||
<string name="cpv_select">選択</string>
|
||||
<string name="cpv_transparency">透明度</string>
|
||||
</resources>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Selecteer een kleur</string>
|
||||
<string name="cpv_presets">Vooraf ingesteld</string>
|
||||
<string name="cpv_custom">Aangepast</string>
|
||||
<string name="cpv_select">Kiezen</string>
|
||||
<string name="cpv_transparency">Transparantie</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Wybierz kolor</string>
|
||||
<string name="cpv_presets">Kolory domyślne</string>
|
||||
<string name="cpv_custom">Dostosuj</string>
|
||||
<string name="cpv_select">Wybierz</string>
|
||||
<string name="cpv_transparency">Przezroczystość</string>
|
||||
</resources>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Selecionar uma cor</string>
|
||||
<string name="cpv_presets">Predefinidos</string>
|
||||
<string name="cpv_custom">Personalizar</string>
|
||||
<string name="cpv_select">Selecionar</string>
|
||||
<string name="cpv_transparency">Transparência</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Выберите цвет</string>
|
||||
<string name="cpv_presets">Предустановки</string>
|
||||
<string name="cpv_custom">Особый</string>
|
||||
<string name="cpv_select">Выбрать</string>
|
||||
<string name="cpv_transparency">Прозрачность</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Vyberte farbu</string>
|
||||
<string name="cpv_presets">Prednastavené</string>
|
||||
<string name="cpv_custom">Vlastné</string>
|
||||
<string name="cpv_select">Vybrať</string>
|
||||
<string name="cpv_transparency">Priehľadnosť</string>
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">选择颜色</string>
|
||||
<string name="cpv_presets">预置颜色</string>
|
||||
<string name="cpv_custom">自定义颜色</string>
|
||||
<string name="cpv_select">确认</string>
|
||||
<string name="cpv_transparency">透明度</string>
|
||||
</resources>
|
@ -0,0 +1,38 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="ResourceName">
|
||||
|
||||
<declare-styleable name="ColorPanelView">
|
||||
<attr name="cpv_showOldColor" format="boolean" />
|
||||
<attr name="cpv_colorShape" format="enum">
|
||||
<enum name="square" value="0" />
|
||||
<enum name="circle" value="1" />
|
||||
</attr>
|
||||
<attr name="cpv_borderColor" format="color|reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ColorPickerView" parent="ColorPanelView">
|
||||
<attr name="cpv_alphaChannelVisible" format="boolean|reference" />
|
||||
<attr name="cpv_alphaChannelText" format="string|reference" />
|
||||
<attr name="cpv_sliderColor" format="color|reference" />
|
||||
<attr name="cpv_borderColor" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ColorPreference" parent="ColorPickerDialog">
|
||||
<attr name="cpv_showAlphaSlider" format="boolean|reference" />
|
||||
<attr name="cpv_previewSize" format="enum">
|
||||
<enum name="regular" value="0" />
|
||||
<enum name="large" value="1" />
|
||||
</attr>
|
||||
<attr name="cpv_colorShape" />
|
||||
<attr name="cpv_dialogTitle" format="reference" />
|
||||
<attr name="cpv_colorPresets" format="reference" />
|
||||
<attr name="cpv_dialogType" format="enum">
|
||||
<enum name="custom" value="0" />
|
||||
<enum name="preset" value="1" />
|
||||
</attr>
|
||||
<attr name="cpv_showColorShades" format="boolean|reference" />
|
||||
<attr name="cpv_allowPresets" format="boolean|reference" />
|
||||
<attr name="cpv_allowCustom" format="boolean|reference" />
|
||||
<attr name="cpv_showDialog" format="boolean|reference" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="cpv_required_padding">6dp</dimen>
|
||||
<dimen name="cpv_dialog_preview_width">66dp</dimen>
|
||||
<dimen name="cpv_dialog_preview_height">34dp</dimen>
|
||||
<dimen name="cpv_column_width">58dp</dimen>
|
||||
<dimen name="cpv_item_size">50dp</dimen>
|
||||
<dimen name="cpv_item_horizontal_padding">8dp</dimen>
|
||||
<dimen name="cpv_color_preference_normal">28dp</dimen>
|
||||
<dimen name="cpv_color_preference_large">40dp</dimen>
|
||||
</resources>
|
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<item name="cpv_color_picker_view" type="id" />
|
||||
<item name="cpv_color_panel_old" type="id" />
|
||||
<item name="cpv_color_panel_new" type="id" />
|
||||
<item name="cpv_preference_preview_color_panel" type="id" />
|
||||
|
||||
</resources>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Default title for color picker dialog [CHAR LIMIT=30] -->
|
||||
<string name="cpv_default_title">Select a Color</string>
|
||||
<string name="cpv_presets">Presets</string>
|
||||
<string name="cpv_custom">Custom</string>
|
||||
<string name="cpv_select">Select</string>
|
||||
<string name="cpv_transparency">Transparency</string>
|
||||
</resources>
|
@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2016 Jared Rummler <jared.rummler@gmail.com>
|
||||
~
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="cpv_ColorPickerViewStyle">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">315dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
Loading…
Reference in new issue