Skip to content

Instantly share code, notes, and snippets.

@payne911
Created March 19, 2019 05:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save payne911/26d71df33449cb3b6583415034c2347e to your computer and use it in GitHub Desktop.
Save payne911/26d71df33449cb3b6583415034c2347e to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button_pick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:text="@string/pick_color"
app:layout_constraintBottom_toTopOf="@+id/picked_color_heading"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/picked_color_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="4dp"
android:text="@string/picked_color"
app:layout_constraintBottom_toTopOf="@+id/picked_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_pick" />
<View
android:id="@+id/picked_color"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/picked_color_heading" />
</android.support.constraint.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="thumb_radius">15</integer>
<color name="thumb_color">@color/colorAccent</color>
</resources>
package com.example.colorpicker.Views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.InsetDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.example.colorpicker.R;
public class AreaPicker extends View {
private OnPickedListener onPickedListener = null;
private InsetDrawable backgroundDrawable;
private static int thumbRadius, padding;
private Paint thumb_paint;
private float x, y;
public AreaPicker(Context context) {
super(context);
init();
}
public AreaPicker(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AreaPicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
setFocusable(true);
setFocusableInTouchMode(true);
thumb_paint = new Paint();
thumb_paint.setStyle(Paint.Style.FILL);
thumb_paint.setColor(getContext().getColor(R.color.thumb_color));
thumbRadius = getResources().getInteger(R.integer.thumb_radius);
padding = thumbRadius * 2;
// On utilise un InsetDrawable comme arrière-plan, pour que, lorsque l'usager fournira son
// propre Drawable à afficher dans le plan de sélection, celui-ci ne se rendent pas
// jusqu'au bord du View, mais plutôt laisse une petite marge pour que le le marqueur de
// sélection puisse déborder du plan de sélection sans déborder du View.
backgroundDrawable = new InsetDrawable(new GradientDrawable(), padding);
setBackground(backgroundDrawable);
}
public void setInsetDrawable(Drawable dr){
backgroundDrawable.setDrawable(dr);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
x = screen_to_frac_coord(event.getX(), getWidth());
y = screen_to_frac_coord(event.getY(), getHeight());
onChange(true);
return true;
}
public void setMaxX(int newMaxX){
/* IMPLÉMENTER CETTE MÉTHODE */
}
public void setMaxY(int newMaxY){
/* IMPLÉMENTER CETTE MÉTHODE */
}
public int getPickedX(){
/* IMPLÉMENTER CETTE MÉTHODE */
return 0;
}
public int getPickedY(){
/* IMPLÉMENTER CETTE MÉTHODE */
return 0;
}
public void setPickedX(int newX){
/* IMPLÉMENTER CETTE MÉTHODE */
}
public void setPickedY(int newY){
/* IMPLÉMENTER CETTE MÉTHODE */
}
// Cette fonction doit être appelée immédiatement après que la coordonnée
// représentée par cet AreaPicker a été mise à jour.
// (Bref, dès que this.x et/ou this.y a changé, il faut appeler onChange.)
private void onChange(boolean fromUser){
if(onPickedListener != null){
onPickedListener.onPicked(this, getPickedX(), getPickedY(), fromUser);
}
invalidate();
}
// S'assure que value est entre 0 et 1.
private float unitClamp(float value){
return Math.max(0, Math.min(1, value));
}
// Convertit une coordonnée exprimée en espace-écran en une coordonée
// exprimée en fraction de l'étendue (0 à 1).
private float screen_to_frac_coord(float screen_coord, float screen_range){
float frac_coord = (screen_coord - padding) / (screen_range - 2 * padding);
return unitClamp(frac_coord);
}
// Convertit une coordonnée exprimée en fraction de l'étendue (0 à 1) en espace-écran.
private float frac_to_screen_coord(float frac_coord, float screen_range){
return frac_coord * (screen_range - 2 * padding) + padding;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float ax = frac_to_screen_coord(x, getWidth());
float ay = frac_to_screen_coord(y, getHeight());
canvas.drawCircle(ax, ay, thumbRadius, thumb_paint);
}
public void setOnPickedListener(OnPickedListener onPickedListener){
this.onPickedListener = onPickedListener;
}
public interface OnPickedListener{
void onPicked(AreaPicker areaPicker, int x, int y, boolean fromUser);
}
}
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/checkers"
android:tileMode="repeat"
/>
package com.example.colorpicker;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.support.annotation.ColorInt;
import android.support.v7.widget.AppCompatSeekBar;
import android.util.AttributeSet;
public class ColoredSeekBar extends AppCompatSeekBar {
GradientDrawable gd;
public ColoredSeekBar(Context context) {
super(context);
init();
}
public ColoredSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ColoredSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init(){
gd = new GradientDrawable();
setProgressDrawable(gd);
}
public void setColor(@ColorInt int leftColor, @ColorInt int rightcolor) {
gd = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
new int[] {leftColor, rightcolor});
setProgressDrawable(gd);
}
}
package com.example.colorpicker;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.support.annotation.ColorInt;
import android.util.Log; // todo: delete before remise
import android.view.LayoutInflater;
import android.view.View;
import android.widget.SeekBar;
import com.example.colorpicker.Views.AreaPicker;
class ColorPickerDialog extends AlertDialog {
private static final String TAG = "JTAG"; // todo: delete before remise
private final static int MAX_RGB_VALUE = 255;
private final static int MAX_SV_VALUE = 100;
private final static int MAX_H_VALUE = 360;
private AreaPicker seekSV;
private SaturationValueGradient saturationValueGradient;
private OnColorPickedListener onColorPickedListener = null;
/* Seekbars. */
private ColoredSeekBar rainbowSeekBar;
public ColoredSeekBar rSeekBar;
private ColoredSeekBar gSeekBar;
private ColoredSeekBar bSeekBar;
private ColoredSeekBar aSeekBar;
// Représentation/stockage interne de la couleur présentement sélectionnée par le Dialog.
private int a, r, g, b;
// todo: for alpha, see https://stackoverflow.com/questions/15319635/manipulate-alpha-bytes-of-java-android-color-int
ColorPickerDialog(Context context) {
super(context);
init(context);
}
ColorPickerDialog(Context context, int themeResId) {
super(context, themeResId);
init(context);
}
ColorPickerDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
init(context);
}
private void init(Context context) {
/* CETTE MÉTHODE DEVRA ÊTRE MODIFIÉE */
// Initialize dialog
@SuppressLint("InflateParams")
View v = LayoutInflater.from(context).inflate(R.layout.dialog_picker, null);
setView(v);
/* Set up SeekBar variables. */
rainbowSeekBar = v.findViewById(R.id.seekH);
rSeekBar = v.findViewById(R.id.seekR);
gSeekBar = v.findViewById(R.id.seekG);
bSeekBar = v.findViewById(R.id.seekB);
aSeekBar = v.findViewById(R.id.seekA);
// Initialize SV gradient
seekSV = v.findViewById(R.id.seekSV);
saturationValueGradient = new SaturationValueGradient();
seekSV.setInsetDrawable(saturationValueGradient);
/* Setting up Gradients of SeekBars. */
rSeekBar.setColor(Color.BLACK, Color.RED);
rSeekBar.setOnSeekBarChangeListener(rListener);
rSeekBar.setMax(MAX_RGB_VALUE);
gSeekBar.setColor(Color.BLACK, Color.GREEN);
gSeekBar.setOnSeekBarChangeListener(gListener);
gSeekBar.setMax(MAX_RGB_VALUE);
bSeekBar.setColor(Color.BLACK, Color.BLUE);
bSeekBar.setOnSeekBarChangeListener(bListener);
bSeekBar.setMax(MAX_RGB_VALUE);
aSeekBar.setBackground(context.getDrawable(R.drawable.checker));
aSeekBar.setColor(Color.parseColor("#00000000"),
Color.parseColor("#FF000000"));
// aSeekBar.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.DST_IN);
// aSeekBar.setProgressDrawable(context.getDrawable(R.drawable.seekbar_layers));
aSeekBar.setMax(MAX_RGB_VALUE);
// adapted from https://stackoverflow.com/a/32990741/9768291
GradientDrawable rainbow = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT,
new int[] {Color.RED, Color.MAGENTA, Color.BLUE,
Color.CYAN, Color.GREEN, Color.YELLOW, Color.RED});
rainbowSeekBar.setProgressDrawable(rainbow);
rainbowSeekBar.setOnSeekBarChangeListener(rainbowListener);
rainbowSeekBar.setMax(MAX_H_VALUE); // todo: is that it?
// Exemple pour afficher un gradient SV centré sur du rouge pur.
saturationValueGradient.setColor(Color.RED);
// Default color
setColor(getContext().getColor(R.color.defaultColor));
}
@ColorInt int getColor() {
/* IMPLÉMENTER CETTE MÉTHODE
* Elle doit retourner la couleur présentement sélectionnée par le dialog.
* */
a = aSeekBar.getProgress();
r = rSeekBar.getProgress();
g = gSeekBar.getProgress();
b = bSeekBar.getProgress();
Log.w(TAG, "getColor() : rgb=" + Color.rgb(r,g,b));
return Color.argb(a, r, g, b);
}
public void setColor(@ColorInt int newColor) {
/* IMPLÉMENTER CETTE MÉTHODE
* Elle doit mettre à jour l'état du dialog pour que la couleur
* sélectionnée corresponde à "newColor".
* */
Log.w(TAG, "from int: " + newColor +
" | a: " + Color.alpha(newColor) +
" | r: " + Color.red (newColor) +
" | g: " + Color.green(newColor) +
" | b: " + Color.blue (newColor));
rSeekBar.setProgress(Color.red (newColor));
gSeekBar.setProgress(Color.green(newColor));
bSeekBar.setProgress(Color.blue (newColor));
// aSeekBar.setProgress(Color.red(newColor));
}
static private int[] HSVtoRGB(int h, int s, int v) {
/* IMPLÉMENTER CETTE MÉTHODE
* Elle doit convertir un trio de valeurs HSL à un trio de valeurs RGB
* */
return new int[3];
}
static private int[] RGBtoHSV(int r, int g, int b) {
/* IMPLÉMENTER CETTE MÉTHODE
* Elle doit convertir un trio de valeurs RGB à un trio de valeurs HSL
* */
return new int[3];
}
public void setOnColorPickedListener(OnColorPickedListener onColorPickedListener) {
/* IMPLÉMENTER CETTE MÉTHODE
* Elle doit enregistrer un "OnColorPickedListener", qui sera appelé,
* éventuellement, lorsque le bouton "ok" du dialog sera cliqué.
* */
this.onColorPickedListener = onColorPickedListener;
}
public interface OnColorPickedListener {
void onColorPicked(ColorPickerDialog colorPickerDialog, @ColorInt int color);
}
/* SeekBars's Listeners. */
private SeekBar.OnSeekBarChangeListener rainbowListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
saturationValueGradient.setColor(Color.rgb(progress, progress, progress)); // todo: fix
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
};
private SeekBar.OnSeekBarChangeListener rListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
r = progress;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
};
private SeekBar.OnSeekBarChangeListener gListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
g = progress;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
};
private SeekBar.OnSeekBarChangeListener bListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
b = progress;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
};
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/headerHSV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center_horizontal"
android:text="@string/hsv"
app:layout_constraintBottom_toTopOf="@+id/seekSV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.colorpicker.Views.AreaPicker
android:id="@+id/seekSV"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/seekH"
app:layout_constraintDimensionRatio="W,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/headerHSV"
app:layout_constraintVertical_weight="1" />
<com.example.colorpicker.ColoredSeekBar
android:id="@+id/seekH"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:splitTrack="false"
app:layout_constraintBottom_toTopOf="@+id/separator1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekSV" />
<View
android:id="@+id/separator1"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider"
app:layout_constraintBottom_toTopOf="@+id/headerRGB"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekH" />
<TextView
android:id="@+id/headerRGB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center_horizontal"
android:text="@string/rgb"
app:layout_constraintBottom_toTopOf="@+id/seekR"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/separator1" />
<com.example.colorpicker.ColoredSeekBar
android:id="@+id/seekR"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:splitTrack="false"
app:layout_constraintBottom_toTopOf="@+id/seekG"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/headerRGB" />
<com.example.colorpicker.ColoredSeekBar
android:id="@+id/seekG"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:splitTrack="false"
app:layout_constraintBottom_toTopOf="@+id/seekB"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekR" />
<com.example.colorpicker.ColoredSeekBar
android:id="@+id/seekB"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:splitTrack="false"
app:layout_constraintBottom_toTopOf="@+id/separator2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekG"/>
<View
android:id="@+id/separator2"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider"
app:layout_constraintBottom_toBottomOf="@+id/headerAlpha"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekB" />
<TextView
android:id="@+id/headerAlpha"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center_horizontal"
android:text="@string/alpha"
app:layout_constraintBottom_toTopOf="@+id/seekA"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/separator2" />
<com.example.colorpicker.ColoredSeekBar
android:id="@+id/seekA"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:splitTrack="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/headerAlpha" />
</android.support.constraint.ConstraintLayout>
package com.example.colorpicker;
import android.support.annotation.ColorInt;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast; // todo: delete before remise
import static android.content.DialogInterface.BUTTON_NEGATIVE;
import static android.content.DialogInterface.BUTTON_POSITIVE;
public class MainActivity extends AppCompatActivity implements ColorPickerDialog.OnColorPickedListener {
View colorPicked;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* CETTE MÉTHODE DEVRA ÊTRE MODIFIÉE */
/* Set up the ColorPicked square. */
colorPicked = findViewById(R.id.picked_color);
colorPicked.setBackground(getDrawable(R.drawable.checker));
/* todo: initial color of "colorPicked" square (Color.BLACK ?)
colorPicked.setBack */
/* Set up the Dialog. */
ColorPickerDialog dialog = new ColorPickerDialog(this);
dialog.setTitle(R.string.pick_color);
dialog.setOnColorPickedListener(this);
dialog.setButton(BUTTON_POSITIVE,
getResources().getString(android.R.string.ok),
(d,which) -> onColorPicked(dialog, dialog.getColor()));
dialog.setButton(BUTTON_NEGATIVE,
getResources().getString(android.R.string.cancel),
(d,which) -> Toast.makeText(this, "dwewd3", Toast.LENGTH_SHORT).show());
findViewById(R.id.button_pick).setOnClickListener((View v) -> dialog.show());
}
@Override
public void onColorPicked(ColorPickerDialog colorPickerDialog, @ColorInt int color) {
colorPicked.setBackgroundColor(color);
}
}
package com.example.colorpicker;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.support.annotation.ColorInt;
public class SaturationValueGradient extends LayerDrawable {
private GradientDrawable saturationGradient;
private static GradientDrawable[] prepareLayers(){
// Le dégradé 2D du AreaPicker est créé en superposant 2 dégradés linéaires. Un à
// l'horizontal, et l'autre vertical. Le dégradé du dessus est dégrade entre noir et
// transparent, de telle sorte à ce qu'il laisse progressivement transparaître le dégradé
// en dessous de lui. Seul le dégradé du dessous, qui va de blanc à la couleur
// pleinement saturée, a besoin d'être modifié. C'est "hacky", mais ça marche!
// Color layer
GradientDrawable saturationGradient = new GradientDrawable();
saturationGradient.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT);
// Black and white layer
GradientDrawable valueGradient = new GradientDrawable();
valueGradient.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);
valueGradient.setColors(new int[] {Color.TRANSPARENT, Color.BLACK});
valueGradient.setStroke(1, Color.BLACK);
return new GradientDrawable[]{saturationGradient, valueGradient};
}
public SaturationValueGradient() {
super(prepareLayers());
saturationGradient = (GradientDrawable)this.getDrawable(0);
}
public SaturationValueGradient(@ColorInt int color) {
this();
setColor(color);
}
public void setColor(@ColorInt int color) {
saturationGradient.setColors(new int[] {Color.WHITE, color});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment