diff --git a/.github/images/darktheme.png b/.github/images/darktheme.png new file mode 100644 index 0000000..21486fb Binary files /dev/null and b/.github/images/darktheme.png differ diff --git a/.github/images/dialog.png b/.github/images/dialog.png deleted file mode 100644 index 059b4ec..0000000 Binary files a/.github/images/dialog.png and /dev/null differ diff --git a/.github/images/noalpha.png b/.github/images/noalpha.png new file mode 100644 index 0000000..b16e4d0 Binary files /dev/null and b/.github/images/noalpha.png differ diff --git a/.github/images/picker.png b/.github/images/picker.png new file mode 100644 index 0000000..39853f6 Binary files /dev/null and b/.github/images/picker.png differ diff --git a/README.md b/README.md index c52e699..49c974f 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,76 @@ ColorPickerDialog is a simple dialog making it quick and easy to add a color picker functionality to any app. +[![](https://jitpack.io/v/me.jfenn/ColorPickerDialog.svg)](https://jitpack.io/#me.jfenn/ColorPickerDialog) + For testing and experimentation purposes, a sample apk can be downloaded [here](https://jfenn.me/projects/colorpickerdialog). -|Color Picker|Image Color Picker| -|--------|--------| -|![img](./.github/images/dialog.png?raw=true)|![img](./.github/images/image.png?raw=true)| +|Color Picker|No Alpha|Dark Theme| +|--------|--------|--------| +|![img](./.github/images/picker.png?raw=true)|![img](./.github/images/noalpha.png?raw=true)|![img](./.github/images/darktheme.png?raw=true)| ## Usage ### Setup -The Gradle dependency is available through jCenter, which is used by default in Android Studio. To add the module to your project, copy this line into the dependencies section of your build.gradle file. -``` gradle -compile 'james.colorpickerdialog:colorpickerdialog:0.0.4' +This project is published on [JitPack](https://jitpack.io), which you can add to your project by copying the following to your root build.gradle at the end of "repositories". + +```gradle +allprojects { + repositories { + ... + maven { url 'https://jitpack.io' } + } +} ``` -### Creating a Dialog +To add the dependency, copy this line into your app module's build.gradle file. -The basic requirements for the dialog are a context, color, and listener. You must handle storage of the color yourself, as the library will not do it for you. +```gradle +implementation 'me.jfenn:ColorPickerDialog:0.1.0' +``` -``` java -new ColorPickerDialog(this) //context - .setPreference(color) //the current stored color value - .setListener(new PreferenceDialog.OnPreferenceListener() { - @Override - public void onPreference(PreferenceDialog dialog, Integer preference) { - //called when a color is chosen - this is where you would update a stored value - } +### Creating a Dialog + +The basic requirements for the dialog are a context, color, and listener, though none of them _have_ to be specified. If you don't specify a listener, though, you can't do anything with the color picked by the dialog, which is not very good. +```java +new ColorPickerDialog(this) // context + .withColor(color) // the default / initial color + .withListener(new ColorPickerDialog.OnColorPickedListener() { @Override - public void onCancel(PreferenceDialog dialog) { - //called if the dialog is dismissed + public void onColorPicked(ColorPickerDialog dialog, int color) { + // a color has been picked; use it } }) .show(); ``` -### Image Picker +### Alpha -By default, the dialog will allow the user to pick a color from an image. To enable or disable this feature, use the `ColorPickerDialog.setImagePickerEnabled(boolean)` method. +You can also call `.withAlpha(boolean)` to specify whether you want the colors' alpha to be configurable by the user (if not, all output colors will be fully opaque). This option is enabled by default. A somewhat unnecessary example: -### Default Colors - -To add a default color, use the `ColorPickerDialog.setDefaultPreference(Integer)` method. This will cause a reset button to display when the current preference isn't equal to the default one. If a current preference isn't specified, the dialog will use the default one, but if neither are specified it will cause a `NullPointerException`. +```java +new ColorPickerDialog(this) + .withAlphaEnabled(false) // disable the alpha + .withListener(...) + .show(); +``` -### Using The Image Picker Dialog Separately +### Theming -The image picker function included in this library is currently limited to getting images from the gallery on the device. To use the dialog with a different image, you can create the dialog manually like below. +You can theme this dialog the same as any other: by passing a second parameter (a style resource) to its constructor. Full "runtime" theming will come later, but now is not later, so you can't do that yet. Here's an example of a `ColorPickerDialog` with a basic dark theme, demonstrating all of the options you can specify. -``` java -new ImageColorPickerDialog(getContext(), bitmap) //context, image - .setDefaultPreference(Color.BLACK) //default color in case the user doesn't pick a value - .setListener(new PreferenceDialog.OnPreferenceListener() { - @Override - public void onPreference(PreferenceDialog dialog, Integer preference) { - //called when a color is chosen - } +```java +new ColorPickerDialog(this, R.style.ColorPickerTheme).show(); +``` - @Override - public void onCancel(PreferenceDialog dialog) { - //called if the dialog is dismissed - } - }) - .show(); +```xml + ``` + +The `redColor`, `greenColor`, and `blueColor` attributes affect the RGB sliders, and the `neutralColor` attribute changes the "neutral" colors of the others, including the alpha slider and the handles of the sliders in the HSL picker. diff --git a/app/build.gradle b/app/build.gradle index f743e88..c4b2e5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { - applicationId "james.colorpickerdialogsample" + applicationId "me.jfenn.colorpickerdialogsample" minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 28 versionCode 2 versionName "1.1" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -20,11 +20,7 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile 'com.android.support:appcompat-v7:25.1.0' - compile project(path: ':colorpickerdialog') - testCompile 'junit:junit:4.12' -} + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation project(path: ':colorpickerdialog') +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 690bae0..5ae28ed 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="me.jfenn.colorpickerdialogsample"> - + diff --git a/app/src/main/java/james/colorpickerdialogsample/MainActivity.java b/app/src/main/java/james/colorpickerdialogsample/MainActivity.java deleted file mode 100644 index 266b247..0000000 --- a/app/src/main/java/james/colorpickerdialogsample/MainActivity.java +++ /dev/null @@ -1,59 +0,0 @@ -package james.colorpickerdialogsample; - -import android.graphics.Color; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.widget.Toast; - -import james.colorpickerdialog.dialogs.ColorPickerDialog; -import james.colorpickerdialog.dialogs.PreferenceDialog; - -public class MainActivity extends AppCompatActivity implements View.OnClickListener, PreferenceDialog.OnPreferenceListener { - - private int preference = Color.BLUE; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - findViewById(R.id.defaultAndImage).setOnClickListener(this); - findViewById(R.id.noDefaultAndImage).setOnClickListener(this); - findViewById(R.id.defaultAndNoImage).setOnClickListener(this); - findViewById(R.id.noDefaultAndNoImage).setOnClickListener(this); - } - - @Override - public void onClick(View v) { - ColorPickerDialog dialog = new ColorPickerDialog(this); - dialog.setPreference(preference); - dialog.setListener(this); - - switch (v.getId()) { - case R.id.defaultAndImage: - dialog.setDefaultPreference(Color.BLACK); - case R.id.noDefaultAndImage: - dialog.setImagePickerEnabled(true); - break; - case R.id.defaultAndNoImage: - dialog.setDefaultPreference(Color.BLACK); - case R.id.noDefaultAndNoImage: - dialog.setImagePickerEnabled(false); - break; - } - - dialog.show(); - } - - @Override - public void onPreference(PreferenceDialog dialog, Integer preference) { - Toast.makeText(MainActivity.this, String.format("#%06X", (0xFFFFFF & preference)), Toast.LENGTH_SHORT).show(); - this.preference = preference; - } - - @Override - public void onCancel(PreferenceDialog dialog) { - Toast.makeText(MainActivity.this, "Cancelled", Toast.LENGTH_SHORT).show(); - } -} diff --git a/app/src/main/java/me/jfenn/colorpickerdialogsample/MainActivity.java b/app/src/main/java/me/jfenn/colorpickerdialogsample/MainActivity.java new file mode 100644 index 0000000..dcaa556 --- /dev/null +++ b/app/src/main/java/me/jfenn/colorpickerdialogsample/MainActivity.java @@ -0,0 +1,39 @@ +package me.jfenn.colorpickerdialogsample; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.View; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import me.jfenn.colorpickerdialog.dialogs.ColorPickerDialog; + +public class MainActivity extends AppCompatActivity implements View.OnClickListener { + + private int color = Color.BLUE; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + findViewById(R.id.normal).setOnClickListener(this); + findViewById(R.id.normalAlpha).setOnClickListener(this); + findViewById(R.id.dark).setOnClickListener(this); + } + + @Override + public void onClick(View v) { + new ColorPickerDialog(this, v.getId() == R.id.dark ? R.style.ColorDialog_Dark : 0) + .withColor(color) + .withAlphaEnabled(v.getId() != R.id.normal) + .withListener(new ColorPickerDialog.OnColorPickedListener() { + @Override + public void onColorPicked(ColorPickerDialog dialog, int color) { + MainActivity.this.color = color; + Toast.makeText(MainActivity.this, String.format("#%08X", color), Toast.LENGTH_SHORT).show(); + } + }) + .show(); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 85264d2..a345e2f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,38 +1,26 @@ - + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center"> - + android:id="@+id/normal" /> - + android:id="@+id/normalAlpha" /> - + android:id="@+id/dark" /> - - - + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index db4a141..f99b9a3 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -7,4 +7,10 @@ @color/colorAccent + + diff --git a/build.gradle b/build.gradle index 4e3be99..c9af7f1 100644 --- a/build.gradle +++ b/build.gradle @@ -3,19 +3,16 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath 'com.android.tools.build:gradle:3.2.1' } } allprojects { repositories { + google() jcenter() } } diff --git a/colorpickerdialog/build.gradle b/colorpickerdialog/build.gradle index cb9b917..000a9ff 100644 --- a/colorpickerdialog/build.gradle +++ b/colorpickerdialog/build.gradle @@ -1,40 +1,16 @@ apply plugin: 'com.android.library' -ext { - bintrayRepo = 'maven' - bintrayName = 'colorpickerdialog' - - publishedGroupId = 'james.colorpickerdialog' - libraryName = 'ColorPickerDialog' - artifact = 'colorpickerdialog' - - libraryDescription = 'A simple dialog making it quick and easy to add a color picker function.' - - siteUrl = 'https://github.com/TheAndroidMaster/ColorPickerDialog' - gitUrl = 'https://github.com/TheAndroidMaster/ColorPickerDialog.git' - - libraryVersion = '0.0.4' - - developerId = 'TheAndroidMaster' - developerName = 'James Fenn' - developerEmail = '18jafenn90@gmail.com' - - licenseName = 'The Apache Software License, Version 2.0' - licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - allLicenses = ["Apache-2.0"] -} - android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { minSdkVersion 16 - targetSdkVersion 25 - versionCode 4 - versionName "0.0.4" + targetSdkVersion 28 + versionCode 5 + versionName "0.1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } @@ -47,10 +23,7 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:25.1.0' - testCompile 'junit:junit:4.12' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'com.google.android.material:material:1.0.0' } - -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' -apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' diff --git a/colorpickerdialog/src/main/AndroidManifest.xml b/colorpickerdialog/src/main/AndroidManifest.xml index 0433579..246aea6 100644 --- a/colorpickerdialog/src/main/AndroidManifest.xml +++ b/colorpickerdialog/src/main/AndroidManifest.xml @@ -1,13 +1,12 @@ - + - + diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/ColorPickerDialog.java b/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/ColorPickerDialog.java deleted file mode 100644 index 49f3379..0000000 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/ColorPickerDialog.java +++ /dev/null @@ -1,297 +0,0 @@ -package james.colorpickerdialog.dialogs; - -import android.animation.Animator; -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.provider.MediaStore; -import android.support.annotation.ColorInt; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.AppCompatEditText; -import android.support.v7.widget.AppCompatSeekBar; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.View; -import android.widget.ImageView; -import android.widget.SeekBar; -import android.widget.TextView; - -import java.io.IOException; - -import james.colorpickerdialog.ColorPicker; -import james.colorpickerdialog.R; -import james.colorpickerdialog.activities.ImagePickerActivity; -import james.colorpickerdialog.utils.ColorUtils; - -public class ColorPickerDialog extends PreferenceDialog implements ColorPicker - .OnActivityResultListener { - - private ColorPicker picker; - private TextWatcher textWatcher; - - private ImageView colorImage; - private AppCompatEditText colorHex; - private TextView redInt, greenInt, blueInt; - private AppCompatSeekBar red, green, blue; - private View reset, imagePicker; - - private boolean isTrackingTouch, isImagePickerEnabled = true; - - public ColorPickerDialog(Context context) { - super(context); - setTitle(R.string.color_picker_name); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.dialog_color_picker); - - picker = (ColorPicker) getContext().getApplicationContext(); - picker.addListener(this); - - colorImage = (ImageView) findViewById(R.id.color); - colorHex = (AppCompatEditText) findViewById(R.id.colorHex); - red = (AppCompatSeekBar) findViewById(R.id.red); - ColorUtils.setProgressBarColor(red, ContextCompat.getColor(getContext(), R.color.red)); - redInt = (TextView) findViewById(R.id.redInt); - green = (AppCompatSeekBar) findViewById(R.id.green); - ColorUtils.setProgressBarColor(green, ContextCompat.getColor(getContext(), R.color.green)); - greenInt = (TextView) findViewById(R.id.greenInt); - blue = (AppCompatSeekBar) findViewById(R.id.blue); - ColorUtils.setProgressBarColor(blue, ContextCompat.getColor(getContext(), R.color.blue)); - blueInt = (TextView) findViewById(R.id.blueInt); - imagePicker = findViewById(R.id.image); - reset = findViewById(R.id.reset); - - textWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - try { - int color = Color.parseColor(colorHex.getText().toString()); - setColor(color, true); - setPreference(color); - } catch (Exception ignored) { - } - } - - @Override - public void afterTextChanged(Editable s) { - } - }; - - colorHex.addTextChangedListener(textWatcher); - - red.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int i, boolean b) { - int color = getPreference(); - color = Color.rgb(i, Color.green(color), Color.blue(color)); - setColor(color, false); - setPreference(color); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - isTrackingTouch = true; - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - isTrackingTouch = false; - } - }); - - green.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int i, boolean b) { - int color = getPreference(); - color = Color.rgb(Color.red(color), i, Color.blue(color)); - setColor(color, false); - setPreference(color); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - isTrackingTouch = true; - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - isTrackingTouch = false; - } - }); - - blue.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int i, boolean b) { - int color = getPreference(); - color = Color.rgb(Color.red(color), Color.green(color), i); - setColor(color, false); - setPreference(color); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - isTrackingTouch = true; - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - isTrackingTouch = false; - } - }); - - setColor(getPreference(), false); - - imagePicker.setVisibility(isImagePickerEnabled ? View.VISIBLE : View.GONE); - imagePicker.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getContext().startActivity(new Intent(getContext(), ImagePickerActivity.class)); - } - }); - - Integer preference = getPreference(); - Integer defaultPreference = getDefaultPreference(); - reset.setVisibility(defaultPreference != null && !preference.equals(defaultPreference) ? View.VISIBLE : View.GONE); - - reset.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int color = getDefaultPreference(); - setColor(color, true); - setPreference(color); - } - }); - - findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - cancel(); - } - }); - - findViewById(R.id.confirm).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - confirm(); - } - }); - } - - private void setColor(@ColorInt int color, boolean animate) { - if (!isTrackingTouch && animate) { - ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), getPreference(), color); - animator.setDuration(250); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - int color = (int) animation.getAnimatedValue(); - red.setProgress(Color.red(color)); - green.setProgress(Color.green(color)); - blue.setProgress(Color.blue(color)); - } - }); - animator.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - isTrackingTouch = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - isTrackingTouch = false; - } - - @Override - public void onAnimationCancel(Animator animation) { - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - }); - animator.start(); - } else { - colorImage.setImageDrawable(new ColorDrawable(color)); - colorHex.removeTextChangedListener(textWatcher); - colorHex.setText(String.format("#%06X", (0xFFFFFF & color))); - colorHex.setTextColor(ContextCompat.getColor(getContext(), ColorUtils.isColorDark(color) ? R.color.textColorPrimaryInverse : R.color.textColorPrimary)); - colorHex.addTextChangedListener(textWatcher); - redInt.setText(String.valueOf(Color.red(color))); - greenInt.setText(String.valueOf(Color.green(color))); - blueInt.setText(String.valueOf(Color.blue(color))); - - if (red.getProgress() != Color.red(color)) red.setProgress(Color.red(color)); - if (green.getProgress() != Color.green(color)) green.setProgress(Color.green(color)); - if (blue.getProgress() != Color.blue(color)) blue.setProgress(Color.blue(color)); - } - } - - @Override - public ColorPickerDialog setDefaultPreference(Integer preference) { - return (ColorPickerDialog) super.setDefaultPreference(preference); - } - - @Override - public ColorPickerDialog setPreference(@ColorInt Integer preference) { - Integer defaultPreference = getDefaultPreference(); - if (reset != null) - reset.setVisibility(!preference.equals(defaultPreference) ? View.VISIBLE : View.GONE); - - return (ColorPickerDialog) super.setPreference(preference); - } - - public ColorPickerDialog setImagePickerEnabled(boolean isImagePickerEnabled) { - this.isImagePickerEnabled = isImagePickerEnabled; - - if (imagePicker != null) - imagePicker.setVisibility(isImagePickerEnabled ? View.VISIBLE : View.GONE); - - return this; - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - Bitmap bitmap = null; - - if (requestCode == ImagePickerActivity.ACTION_PICK_IMAGE && resultCode == - ImagePickerActivity.RESULT_OK) { - try { - bitmap = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), data.getData()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - if (bitmap != null) { - new ImageColorPickerDialog(getContext(), bitmap).setDefaultPreference(Color.BLACK).setListener(new PreferenceDialog.OnPreferenceListener() { - @Override - public void onPreference(PreferenceDialog dialog, Integer preference) { - setColor(preference, false); - setPreference(preference); - } - - @Override - public void onCancel(PreferenceDialog dialog) { - } - }).show(); - } - } - - @Override - public void dismiss() { - picker.removeListener(this); - super.dismiss(); - } -} diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/PreferenceDialog.java b/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/PreferenceDialog.java deleted file mode 100644 index 935d8de..0000000 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/PreferenceDialog.java +++ /dev/null @@ -1,90 +0,0 @@ -package james.colorpickerdialog.dialogs; - -import android.content.Context; -import android.content.DialogInterface; -import android.support.v7.app.AppCompatDialog; - -import james.colorpickerdialog.R; - -public abstract class PreferenceDialog extends AppCompatDialog { - - private T preference, defaultPreference; - private OnPreferenceListener listener; - - public PreferenceDialog(Context context) { - super(context, R.style.DialogTheme); - - setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialogInterface) { - cancel(); - } - }); - } - - public PreferenceDialog(Context context, int theme) { - super(context, theme); - - setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialogInterface) { - cancel(); - } - }); - } - - public void confirm() { - if (hasListener()) { - getListener().onPreference(this, getPreference()); - setListener(null); - } - - if (isShowing()) dismiss(); - } - - public void cancel() { - if (hasListener()) { - getListener().onCancel(this); - setListener(null); - } - - if (isShowing()) dismiss(); - } - - public PreferenceDialog setPreference(T preference) { - this.preference = preference; - return this; - } - - public T getPreference() { - return preference != null ? preference : getDefaultPreference(); - } - - public PreferenceDialog setDefaultPreference(T preference) { - defaultPreference = preference; - return this; - } - - public T getDefaultPreference() { - return defaultPreference; - } - - public PreferenceDialog setListener(OnPreferenceListener listener) { - this.listener = listener; - return this; - } - - public boolean hasListener() { - return listener != null; - } - - public OnPreferenceListener getListener() { - return listener; - } - - public interface OnPreferenceListener { - void onPreference(PreferenceDialog dialog, T preference); - - void onCancel(PreferenceDialog dialog); - } -} diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/utils/ColorUtils.java b/colorpickerdialog/src/main/java/james/colorpickerdialog/utils/ColorUtils.java deleted file mode 100644 index ab1cc75..0000000 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/utils/ColorUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package james.colorpickerdialog.utils; - -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.support.annotation.ColorInt; -import android.support.v7.widget.AppCompatSeekBar; - -public class ColorUtils { - - public static boolean isColorDark(@ColorInt int color) { - return getColorDarkness(color) > 0.4; - } - - private static double getColorDarkness(@ColorInt int color) { - if (color == Color.BLACK) return 1.0; - else if (color == Color.WHITE || color == Color.TRANSPARENT) return 0.0; - return (1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue - (color)) / 255); - } - - public static void setProgressBarColor(AppCompatSeekBar seekbar, @ColorInt int color) { - seekbar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN); - seekbar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - -} \ No newline at end of file diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/ColorPicker.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/ColorPicker.java similarity index 96% rename from colorpickerdialog/src/main/java/james/colorpickerdialog/ColorPicker.java rename to colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/ColorPicker.java index 7e2a20a..9eb39bb 100644 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/ColorPicker.java +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/ColorPicker.java @@ -1,4 +1,4 @@ -package james.colorpickerdialog; +package me.jfenn.colorpickerdialog; import android.app.Application; import android.content.Intent; diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/activities/ImagePickerActivity.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/activities/ImagePickerActivity.java similarity index 85% rename from colorpickerdialog/src/main/java/james/colorpickerdialog/activities/ImagePickerActivity.java rename to colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/activities/ImagePickerActivity.java index 30e2164..233d30b 100644 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/activities/ImagePickerActivity.java +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/activities/ImagePickerActivity.java @@ -1,11 +1,11 @@ -package james.colorpickerdialog.activities; +package me.jfenn.colorpickerdialog.activities; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import android.widget.FrameLayout; -import james.colorpickerdialog.ColorPicker; +import androidx.appcompat.app.AppCompatActivity; +import me.jfenn.colorpickerdialog.ColorPicker; public class ImagePickerActivity extends AppCompatActivity { diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/adapters/ColorPickerPagerAdapter.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/adapters/ColorPickerPagerAdapter.java new file mode 100644 index 0000000..268ffd1 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/adapters/ColorPickerPagerAdapter.java @@ -0,0 +1,117 @@ +package me.jfenn.colorpickerdialog.adapters; + +import android.content.Context; +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; +import me.jfenn.colorpickerdialog.R; +import me.jfenn.colorpickerdialog.views.ColorPickerView; +import me.jfenn.colorpickerdialog.views.HSVPickerView; +import me.jfenn.colorpickerdialog.views.RGBPickerView; + +public class ColorPickerPagerAdapter extends PagerAdapter implements ColorPickerView.OnColorPickedListener, ViewPager.OnPageChangeListener { + + private Context context; + private ColorPickerView.OnColorPickedListener listener; + + @ColorInt + private int color = Color.BLACK; + private boolean isAlphaEnabled = true; + private int position; + + private RGBPickerView rgbPicker; + private HSVPickerView hsbPicker; + + public ColorPickerPagerAdapter(Context context, ColorPickerView.OnColorPickedListener listener) { + this.context = context; + this.listener = listener; + } + + public void setColor(@ColorInt int color) { + this.color = color; + } + + public void setAlphaEnabled(boolean isAlphaEnabled) { + this.isAlphaEnabled = isAlphaEnabled; + } + + public void updateColor(@ColorInt int color, boolean animate) { + if (position == 0 && rgbPicker != null) + rgbPicker.setColor(color, animate); + if (position == 1 && hsbPicker != null) + hsbPicker.setColor(color, animate); + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + ColorPickerView view; + switch (position) { + case 0: + view = rgbPicker = new RGBPickerView(context); + break; + case 1: + view = hsbPicker = new HSVPickerView(context); + break; + default: + return new View(context); + } + + view.setListener(this); + view.setAlphaEnabled(isAlphaEnabled); + view.setColor(color); + container.addView(view); + return view; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + container.removeView((View) object); + } + + @Override + public int getCount() { + return 2; + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + + @Nullable + @Override + public CharSequence getPageTitle(int position) { + return context.getString(new int[]{R.string.colorPickerDialog_rgb, R.string.colorPickerDialog_hsv}[position]); + } + + @Override + public void onColorPicked(ColorPickerView pickerView, @ColorInt int color) { + this.color = color; + if (listener != null) + listener.onColorPicked(pickerView, color); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + this.position = position; + if (position == 0 && rgbPicker != null) + rgbPicker.setColor(color); + if (position == 1 && hsbPicker != null) + hsbPicker.setColor(color); + } + + @Override + public void onPageScrollStateChanged(int state) { + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/dialogs/ColorPickerDialog.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/dialogs/ColorPickerDialog.java new file mode 100644 index 0000000..09d15a9 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/dialogs/ColorPickerDialog.java @@ -0,0 +1,188 @@ +package me.jfenn.colorpickerdialog.dialogs; + +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.provider.MediaStore; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.View; + +import com.google.android.material.tabs.TabLayout; + +import java.io.IOException; + +import androidx.annotation.ColorInt; +import androidx.annotation.StyleRes; +import androidx.appcompat.app.AppCompatDialog; +import androidx.appcompat.widget.AppCompatEditText; +import androidx.viewpager.widget.ViewPager; +import me.jfenn.colorpickerdialog.ColorPicker; +import me.jfenn.colorpickerdialog.R; +import me.jfenn.colorpickerdialog.activities.ImagePickerActivity; +import me.jfenn.colorpickerdialog.adapters.ColorPickerPagerAdapter; +import me.jfenn.colorpickerdialog.utils.AlphaColorDrawable; +import me.jfenn.colorpickerdialog.utils.ColorUtils; +import me.jfenn.colorpickerdialog.views.ColorPickerView; +import me.jfenn.colorpickerdialog.views.SmoothColorView; + +public class ColorPickerDialog extends AppCompatDialog implements ColorPicker.OnActivityResultListener, ColorPickerView.OnColorPickedListener { + + private SmoothColorView colorView; + private AppCompatEditText colorHex; + private TabLayout tabLayout; + private ViewPager slidersPager; + private ColorPickerPagerAdapter slidersAdapter; + + @ColorInt + private int color = Color.BLACK; + private boolean isAlphaEnabled = true; + private boolean shouldIgnoreNextHex = false; + + private OnColorPickedListener listener; + + public ColorPickerDialog(Context context) { + super(context); + setTitle(R.string.colorPickerDialog_dialogName); + } + + public ColorPickerDialog(Context context, @StyleRes int style) { + super(context, style); + setTitle(R.string.colorPickerDialog_dialogName); + } + + public ColorPickerDialog withListener(OnColorPickedListener listener) { + this.listener = listener; + return this; + } + + public ColorPickerDialog withColor(@ColorInt int color) { + this.color = color; + return this; + } + + public ColorPickerDialog withAlphaEnabled(boolean isAlphaEnabled) { + this.isAlphaEnabled = isAlphaEnabled; + return this; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dialog_color_picker); + + colorView = findViewById(R.id.color); + colorHex = findViewById(R.id.colorHex); + tabLayout = findViewById(R.id.tabLayout); + slidersPager = findViewById(R.id.slidersPager); + + slidersAdapter = new ColorPickerPagerAdapter(getContext(), this); + slidersAdapter.setAlphaEnabled(isAlphaEnabled); + slidersAdapter.setColor(color); + + slidersPager.setAdapter(slidersAdapter); + slidersPager.addOnPageChangeListener(slidersAdapter); + tabLayout.setupWithViewPager(slidersPager); + + colorHex.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + Editable editable = colorHex.getText(); + if (editable != null && !shouldIgnoreNextHex) { + String str = editable.toString(); + Log.d("TextChange", str); + if (str.length() == (isAlphaEnabled ? 9 : 7)) { + try { + slidersAdapter.updateColor(Color.parseColor(str), true); + } catch (Exception ignored) { + } + } + } else shouldIgnoreNextHex = false; + } + }); + + findViewById(R.id.confirm).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) + listener.onColorPicked(ColorPickerDialog.this, color); + + dismiss(); + } + }); + + findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + + onColorPicked(null, color); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + AlphaColorDrawable.tile.recycle(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + Bitmap bitmap = null; + + if (requestCode == ImagePickerActivity.ACTION_PICK_IMAGE && resultCode == + ImagePickerActivity.RESULT_OK) { + try { + bitmap = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), data.getData()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (bitmap != null) { + /*new ImageColorPickerDialog(getContext(), bitmap).setDefaultPreference(Color.BLACK).setListener(new OnColorListener() { + @Override + public void onColorPicked(ColorPickerDialog dialog, @ColorInt int preference) { + //TODO: set color + } + + @Override + public void onCancel(ColorPickerDialog dialog) { + } + }).show();*/ + } + } + + @Override + public void onColorPicked(ColorPickerView pickerView, @ColorInt int color) { + this.color = color; + colorView.setColor(color); + + shouldIgnoreNextHex = true; + colorHex.setText(String.format(isAlphaEnabled ? "#%08X" : "#%06X", isAlphaEnabled ? color : (0xFFFFFF & color))); + colorHex.clearFocus(); + + int textColor = ColorUtils.isColorDark(ColorUtils.withBackground(color, Color.WHITE)) ? Color.WHITE : Color.BLACK; + colorHex.setTextColor(textColor); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + colorHex.setBackgroundTintList(ColorStateList.valueOf(textColor)); + } + + public interface OnColorPickedListener { + void onColorPicked(ColorPickerDialog dialog, @ColorInt int color); + } +} diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/ImageColorPickerDialog.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/dialogs/ImageColorPickerDialog.java similarity index 73% rename from colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/ImageColorPickerDialog.java rename to colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/dialogs/ImageColorPickerDialog.java index 96690ad..ae8ee99 100644 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/dialogs/ImageColorPickerDialog.java +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/dialogs/ImageColorPickerDialog.java @@ -1,15 +1,13 @@ -package james.colorpickerdialog.dialogs; +package me.jfenn.colorpickerdialog.dialogs; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.view.View; -import james.colorpickerdialog.R; -import james.colorpickerdialog.views.ColorPickerImageView; +import androidx.appcompat.app.AppCompatDialog; +import me.jfenn.colorpickerdialog.R; -public class ImageColorPickerDialog extends PreferenceDialog { +public class ImageColorPickerDialog extends AppCompatDialog { private Bitmap bitmap; @@ -17,7 +15,7 @@ public ImageColorPickerDialog(Context context, Bitmap bitmap) { super(context); this.bitmap = bitmap; - setTitle(R.string.action_pick_image_color); + setTitle(R.string.colorPickerDialog_imageColorPicker); } @Override @@ -25,7 +23,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.dialog_image_color_picker); - ColorPickerImageView imageView = (ColorPickerImageView) findViewById(R.id.image); + /*ColorPickerImageView imageView = (ColorPickerImageView) findViewById(R.id.image); imageView.setOnColorChangedListener(new ColorPickerImageView.OnColorChangedListener() { @Override public void onColorChanged(@ColorInt int color) { @@ -47,11 +45,11 @@ public void onClick(View view) { public void onClick(View view) { confirm(); } - }); + });*/ } - @Override + /*@Override public ImageColorPickerDialog setDefaultPreference(Integer preference) { return (ImageColorPickerDialog) super.setDefaultPreference(preference); - } + }*/ } diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/AlphaColorDrawable.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/AlphaColorDrawable.java new file mode 100644 index 0000000..4aa8ff4 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/AlphaColorDrawable.java @@ -0,0 +1,73 @@ +package me.jfenn.colorpickerdialog.utils; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; + +public class AlphaColorDrawable extends Drawable { + + private Paint bitmapPaint; + private Paint paint; + public static Bitmap tile; + + public AlphaColorDrawable(@ColorInt int color) { + int size = (int) ConversionUtils.getPixelsFromDp(8); + bitmapPaint = new Paint(); + bitmapPaint.setColor(Color.LTGRAY); + + if (tile == null || tile.isRecycled()) { + tile = Bitmap.createBitmap(size * 4, size * 4, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(tile); + canvas.drawColor(Color.WHITE); + for (int x = 0; x < canvas.getWidth(); x += size) { + for (int y = x % (size * 2) == 0 ? 0 : size; y < canvas.getWidth(); y += size * 2) { + canvas.drawRect(x, y, x + size, y + size, bitmapPaint); + } + } + } + + paint = new Paint(); + paint.setColor(color); + } + + + + @Override + public void draw(@NonNull Canvas canvas) { + Rect b = getBounds(); + + if (paint.getAlpha() < 255) { + for (int x = b.left; x < b.right; x += tile.getWidth()) { + for (int y = b.top; y < b.bottom; y += tile.getHeight()) { + canvas.drawBitmap(tile, x, y, bitmapPaint); + } + } + } + + canvas.drawRect(b.left, b.top, b.right, b.bottom, paint); + } + + @Override + public void setAlpha(int alpha) { + bitmapPaint.setColor(ColorUtils.setAlphaComponent(bitmapPaint.getColor(), alpha)); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/CanvasRenderTask.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/CanvasRenderTask.java new file mode 100644 index 0000000..dec431c --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/CanvasRenderTask.java @@ -0,0 +1,45 @@ +package me.jfenn.colorpickerdialog.utils; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.os.AsyncTask; + +import java.lang.ref.WeakReference; + +import androidx.annotation.Nullable; + +public class CanvasRenderTask extends AsyncTask { + + private final WeakReference reference; + + public CanvasRenderTask(Renderable renderable) { + reference = new WeakReference<>(renderable); + } + + @Override + protected Bitmap doInBackground(Integer... integers) { + if (integers[0] == null || integers[0] < 1 || integers[1] == null || integers[1] < 1) + return null; + + Bitmap bitmap = Bitmap.createBitmap(integers[0], integers[1], Bitmap.Config.ARGB_8888); + Renderable renderable = reference.get(); + if (renderable != null) + renderable.render(new Canvas(bitmap)); + + return bitmap; + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + Renderable renderable = reference.get(); + if (renderable != null) + renderable.onRendered(bitmap); + } + + public interface Renderable { + void render(Canvas canvas); + + void onRendered(@Nullable Bitmap bitmap); + } + +} \ No newline at end of file diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ColorUtils.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ColorUtils.java new file mode 100644 index 0000000..17a552d --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ColorUtils.java @@ -0,0 +1,99 @@ +package me.jfenn.colorpickerdialog.utils; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.util.TypedValue; + +import androidx.annotation.AttrRes; +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatSeekBar; +import androidx.core.content.ContextCompat; +import me.jfenn.colorpickerdialog.R; + +public class ColorUtils { + + public static boolean isColorDark(@ColorInt int color) { + return getColorDarkness(color) > 0.4; + } + + private static double getColorDarkness(@ColorInt int color) { + if (color == Color.BLACK) return 1.0; + else if (color == Color.WHITE || color == Color.TRANSPARENT) return 0.0; + return (1 - (0.259 * Color.red(color) + 0.667 * Color.green(color) + 0.074 * Color.blue(color)) / 255); + } + + @ColorInt + public static int withBackground(@ColorInt int color, @ColorInt int background) { + float alpha = Color.alpha(color) / 255f; + return Color.rgb( + (int) ((Color.red(color) * alpha) + (Color.red(background) * (1 - alpha))), + (int) ((Color.green(color) * alpha) + (Color.green(background) * (1 - alpha))), + (int) ((Color.blue(color) * alpha) + (Color.blue(background) * (1 - alpha))) + ); + } + + @ColorInt + public static int fromAttr(Context context, @AttrRes int attr, @ColorInt int defaultColor) { + TypedValue out = new TypedValue(); + try { + context.getTheme().resolveAttribute(attr, out, true); + if (out.resourceId == 0) + return out.data == 0 ? defaultColor : out.data; + else return ContextCompat.getColor(context, out.resourceId); + } catch (Exception e) { + return defaultColor; + } + } + + @ColorInt + public static int fromAttrRes(Context context, @AttrRes int attr, @ColorRes int defaultColorRes) { + TypedValue out = new TypedValue(); + try { + context.getTheme().resolveAttribute(attr, out, true); + if (out.resourceId == 0) + return out.data == 0 ? ContextCompat.getColor(context, defaultColorRes) : out.data; + else return ContextCompat.getColor(context, out.resourceId); + } catch (Exception e) { + return ContextCompat.getColor(context, defaultColorRes); + } + } + + public static void setProgressBarColor(AppCompatSeekBar seekbar, @ColorInt int color) { + seekbar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN); + seekbar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + + public static void setProgressBarDrawable(AppCompatSeekBar seekbar, @NonNull Drawable drawable) { + Drawable background = new SeekBarBackgroundDrawable(drawable.mutate().getConstantState().newDrawable()); + background.setAlpha(127); + + LayerDrawable layers = new LayerDrawable(new Drawable[]{ + new SeekBarDrawable(drawable), + background + }); + + layers.setId(0, android.R.id.progress); + layers.setId(1, android.R.id.background); + seekbar.setProgressDrawable(layers); + + seekbar.getThumb().setColorFilter( + fromAttr(seekbar.getContext(), R.attr.neutralColor, + fromAttrRes(seekbar.getContext(), android.R.attr.textColorPrimary, R.color.colorPickerDialog_neutral)), + PorterDuff.Mode.SRC_IN + ); + } + + public static int[] getColorWheelArr(float saturation, float brightness) { + int[] arr = new int[13]; + for (int i = 0; i <= 12; i++) + arr[i] = Color.HSVToColor(new float[]{i * 30, saturation, brightness}); + + return arr; + } + +} \ No newline at end of file diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ConversionUtils.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ConversionUtils.java new file mode 100644 index 0000000..e58d469 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ConversionUtils.java @@ -0,0 +1,15 @@ +package me.jfenn.colorpickerdialog.utils; + +import android.content.res.Resources; + +public class ConversionUtils { + + public static float getPixelsFromDp(int dp) { + return dp * Resources.getSystem().getDisplayMetrics().density; + } + + public static float getDpFromPixels(int px) { + return px / Resources.getSystem().getDisplayMetrics().density; + } + +} diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/utils/ImageUtils.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ImageUtils.java similarity index 90% rename from colorpickerdialog/src/main/java/james/colorpickerdialog/utils/ImageUtils.java rename to colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ImageUtils.java index dd5aa44..5c8af4d 100644 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/utils/ImageUtils.java +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/ImageUtils.java @@ -1,4 +1,4 @@ -package james.colorpickerdialog.utils; +package me.jfenn.colorpickerdialog.utils; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -6,7 +6,8 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.support.graphics.drawable.VectorDrawableCompat; + +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; public class ImageUtils { diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/SeekBarBackgroundDrawable.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/SeekBarBackgroundDrawable.java new file mode 100644 index 0000000..121b86f --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/SeekBarBackgroundDrawable.java @@ -0,0 +1,56 @@ +package me.jfenn.colorpickerdialog.utils; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class SeekBarBackgroundDrawable extends Drawable { + + private Drawable drawable; + private float height; + private Paint paint; + + public SeekBarBackgroundDrawable(Drawable drawable) { + this.drawable = drawable; + height = ConversionUtils.getPixelsFromDp(2); + paint = new Paint(); + } + + @Override + public void draw(@NonNull Canvas canvas) { + Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.RGB_565); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(new Canvas(bitmap)); + + Rect bounds = getBounds(); + canvas.clipRect(new Rect( + bounds.left, + (int) (bounds.centerY() - height / 2), + bounds.right, + (int) (bounds.centerY() + height / 2) + )); + + canvas.drawBitmap(bitmap, 0, 0, paint); + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/SeekBarDrawable.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/SeekBarDrawable.java new file mode 100644 index 0000000..baf0c0a --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/utils/SeekBarDrawable.java @@ -0,0 +1,39 @@ +package me.jfenn.colorpickerdialog.utils; + +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.ClipDrawable; +import android.graphics.drawable.Drawable; +import android.view.Gravity; + +public class SeekBarDrawable extends ClipDrawable { + + private float height; + private Rect rect; + + public SeekBarDrawable(Drawable drawable) { + super(drawable, Gravity.START, ClipDrawable.HORIZONTAL); + height = ConversionUtils.getPixelsFromDp(2); + } + + @Override + public void draw(Canvas canvas) { + if (rect == null) { + Rect bounds = getBounds(); + setBounds(rect = new Rect( + bounds.left, + (int) (bounds.centerY() - height / 2), + bounds.right, + (int) (bounds.centerY() + height / 2) + )); + } + + super.draw(canvas); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/CircleColorView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/CircleColorView.java new file mode 100644 index 0000000..13f6fca --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/CircleColorView.java @@ -0,0 +1,68 @@ +package me.jfenn.colorpickerdialog.views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.util.AttributeSet; + +import androidx.annotation.ColorInt; +import me.jfenn.colorpickerdialog.utils.ColorUtils; + +public class CircleColorView extends ColorView { + + Paint outlinePaint; + + public CircleColorView(Context context) { + super(context); + } + + public CircleColorView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CircleColorView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + void setUp() { + super.setUp(); + + outlinePaint = new Paint(); + outlinePaint.setAntiAlias(true); + outlinePaint.setDither(true); + outlinePaint.setStyle(Paint.Style.STROKE); + outlinePaint.setStrokeWidth(outlineSize); + outlinePaint.setColor(Color.BLACK); + } + + @Override + public void setColor(@ColorInt int color) { + outlinePaint.setColor(ColorUtils.isColorDark(color) ? Color.TRANSPARENT : Color.BLACK); + super.setColor(color); + } + + @Override + public void render(Canvas canvas) { + int size = Math.min(canvas.getWidth(), canvas.getHeight()); + + Path path = new Path(); + path.addCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, size / 2, Path.Direction.CW); + canvas.clipPath(path); + + super.render(canvas); + + canvas.drawCircle(getWidth() / 2, getHeight() / 2, (size / 2) - (outlineSize / 2), outlinePaint); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int size = getMeasuredWidth(); + setMeasuredDimension(size, size); + } + +} \ No newline at end of file diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/views/CircleImageView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/CircleImageView.java similarity index 84% rename from colorpickerdialog/src/main/java/james/colorpickerdialog/views/CircleImageView.java rename to colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/CircleImageView.java index 13f930a..de6f9f7 100644 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/views/CircleImageView.java +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/CircleImageView.java @@ -1,16 +1,16 @@ -package james.colorpickerdialog.views; +package me.jfenn.colorpickerdialog.views; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.media.ThumbnailUtils; -import android.support.v4.graphics.drawable.RoundedBitmapDrawable; -import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; -import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; -import james.colorpickerdialog.utils.ImageUtils; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; +import me.jfenn.colorpickerdialog.utils.ImageUtils; public class CircleImageView extends AppCompatImageView { Paint paint; diff --git a/colorpickerdialog/src/main/java/james/colorpickerdialog/views/ColorPickerImageView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorPickerImageView.java similarity index 96% rename from colorpickerdialog/src/main/java/james/colorpickerdialog/views/ColorPickerImageView.java rename to colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorPickerImageView.java index 8df5c32..efa36ff 100644 --- a/colorpickerdialog/src/main/java/james/colorpickerdialog/views/ColorPickerImageView.java +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorPickerImageView.java @@ -1,4 +1,4 @@ -package james.colorpickerdialog.views; +package me.jfenn.colorpickerdialog.views; import android.content.Context; import android.graphics.Bitmap; @@ -8,13 +8,13 @@ import android.graphics.Rect; import android.os.Handler; import android.os.Looper; -import android.support.annotation.ColorInt; -import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ViewTreeObserver; -import james.colorpickerdialog.utils.ColorUtils; +import androidx.annotation.ColorInt; +import androidx.appcompat.widget.AppCompatImageView; +import me.jfenn.colorpickerdialog.utils.ColorUtils; public class ColorPickerImageView extends AppCompatImageView { diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorPickerView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorPickerView.java new file mode 100644 index 0000000..7ad8fda --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorPickerView.java @@ -0,0 +1,197 @@ +package me.jfenn.colorpickerdialog.views; + +import android.animation.ObjectAnimator; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import java.util.Locale; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatSeekBar; +import me.jfenn.colorpickerdialog.R; +import me.jfenn.colorpickerdialog.utils.ColorUtils; + +public abstract class ColorPickerView extends LinearLayout { + + private OnColorPickedListener listener; + + private TextView alphaInt; + private AppCompatSeekBar alpha; + private View alphaLayout; + + private boolean isTrackingTouch; + + public ColorPickerView(Context context) { + super(context); + init(); + postInit(); + } + + public ColorPickerView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + postInit(); + } + + public ColorPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + postInit(); + } + + @TargetApi(21) + public ColorPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + postInit(); + } + + abstract void init(); + + private void postInit() { + alphaInt = findViewById(R.id.alphaInt); + alpha = findViewById(R.id.alpha); + alphaLayout = findViewById(R.id.alphaLayout); + + if (alpha != null) { + alpha.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int i, boolean b) { + alphaInt.setText(String.format(Locale.getDefault(), "%.2f", i / 255f)); + onColorPicked(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + isTrackingTouch = true; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + isTrackingTouch = false; + } + }); + + ColorUtils.setProgressBarColor( + alpha, + ColorUtils.fromAttr(getContext(), R.attr.neutralColor, + ColorUtils.fromAttrRes(getContext(), android.R.attr.textColorPrimary, R.color.colorPickerDialog_neutral)) + ); + } + } + + /** + * Set the picker's color. Changes to values will not be animated. + * + * @param color The picker's color. + */ + public void setColor(@ColorInt int color) { + setColor(color, false); + } + + /** + * Set the picker's color. + * + * @param color The picker's color. + * @param animate Whether to animate changes in values. + */ + public void setColor(@ColorInt int color, boolean animate) { + setColorAlpha(Color.alpha(color), animate); + } + + /** + * Get the current color value. + * + * @return The current color value. + */ + @ColorInt + public abstract int getColor(); + + /** + * Set whether the color's alpha value can be changed. + * + * @param isAlpha Whether the color's alpha value can be changed. + */ + public void setAlphaEnabled(boolean isAlpha) { + if (alphaLayout == null) + return; + + if (isAlpha) { + alphaLayout.setVisibility(View.VISIBLE); + } else { + alphaLayout.setVisibility(View.GONE); + } + } + + /** + * Determine whether the color's alpha value can be modified. + * + * @return Whether the color's alpha value can be modified. + */ + public boolean isAlphaEnabled() { + return alphaLayout != null && alphaLayout.getVisibility() == View.VISIBLE; + } + + /** + * Set the color's alpha, from 0-255. Change in values + * will not be animated. + * + * @param alpha The color's alpha, from 0-255. + */ + public void setColorAlpha(int alpha) { + setColorAlpha(alpha, false); + } + + /** + * Set the color's alpha, between 0-1 (inclusive). + * + * @param alpha The color's alpha, between 0-1 (inclusive). + * @param animate Whether to animate the change in values. + */ + public void setColorAlpha(int alpha, boolean animate) { + if (animate && !isTrackingTouch) { + ObjectAnimator animator = ObjectAnimator.ofInt(this.alpha, "progress", alpha); + animator.setInterpolator(new DecelerateInterpolator()); + animator.start(); + } else { + this.alpha.setProgress(alpha); + } + } + + /** + * Gets the color's alpha, from 0-255. + * + * @return The color's alpha, from 0-255. + */ + public int getColorAlpha() { + return alpha.getProgress(); + } + + protected void onColorPicked() { + if (listener != null) + listener.onColorPicked(this, getColor()); + } + + /** + * Set an interface to receive updates to color values. This may + * be called multiple times in succession if a slider is dragged + * or animated; be wary of performance. + * + * @param listener An interface to receive color updates. + */ + public void setListener(OnColorPickedListener listener) { + this.listener = listener; + } + + public interface OnColorPickedListener { + void onColorPicked(ColorPickerView pickerView, @ColorInt int color); + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorView.java new file mode 100644 index 0000000..2ff6a15 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/ColorView.java @@ -0,0 +1,61 @@ +package me.jfenn.colorpickerdialog.views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; + +import androidx.annotation.ColorInt; +import me.jfenn.colorpickerdialog.utils.ConversionUtils; + +public class ColorView extends RenderableView { + + @ColorInt + int color = Color.BLACK; + float outlineSize; + Paint tilePaint; + + public ColorView(final Context context) { + super(context); + setUp(); + } + + public ColorView(Context context, AttributeSet attrs) { + super(context, attrs); + setUp(); + } + + public ColorView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setUp(); + } + + void setUp() { + outlineSize = ConversionUtils.getPixelsFromDp(2); + + tilePaint = new Paint(); + tilePaint.setAntiAlias(true); + tilePaint.setStyle(Paint.Style.FILL); + tilePaint.setColor(Color.LTGRAY); + } + + public void setColor(@ColorInt int color) { + this.color = color; + startRender(); + } + + @Override + public void render(Canvas canvas) { + if (Color.alpha(color) < 255) { + int outline = Math.round(outlineSize) * 4; + for (int x = 0; x < canvas.getWidth(); x += outline) { + for (int y = x % (outline * 2) == 0 ? 0 : outline; y < canvas.getWidth(); y += (outline * 2)) { + canvas.drawRect(x, y, x + outline, y + outline, tilePaint); + } + } + } + + canvas.drawColor(color); + } +} \ No newline at end of file diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/HSVPickerView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/HSVPickerView.java new file mode 100644 index 0000000..238a837 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/HSVPickerView.java @@ -0,0 +1,136 @@ +package me.jfenn.colorpickerdialog.views; + +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.util.AttributeSet; +import android.view.animation.DecelerateInterpolator; +import android.widget.SeekBar; +import android.widget.TextView; + +import java.util.Locale; + +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatSeekBar; +import me.jfenn.colorpickerdialog.R; +import me.jfenn.colorpickerdialog.utils.ColorUtils; + +public class HSVPickerView extends ColorPickerView { + + private AppCompatSeekBar hue, saturation, brightness; + private TextView hueInt, saturationInt, brightnessInt; + private boolean isTrackingTouch; + + public HSVPickerView(Context context) { + super(context); + } + + public HSVPickerView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public HSVPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public HSVPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + void init() { + inflate(getContext(), R.layout.layout_hsv_picker, this); + hue = findViewById(R.id.hue); + hueInt = findViewById(R.id.hueInt); + saturation = findViewById(R.id.saturation); + saturationInt = findViewById(R.id.saturationInt); + brightness = findViewById(R.id.brightness); + brightnessInt = findViewById(R.id.brightnessInt); + + SeekBar.OnSeekBarChangeListener listener = new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int i, boolean b) { + if (seekBar.getId() == R.id.hue) { + hueInt.setText(String.format("%s", i)); + } else if (seekBar.getId() == R.id.saturation) { + saturationInt.setText(String.format(Locale.getDefault(), "%.2f", i / 255f)); + } else if (seekBar.getId() == R.id.brightness) { + brightnessInt.setText(String.format(Locale.getDefault(), "%.2f", i / 255f)); + } + onColorPicked(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + isTrackingTouch = true; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + isTrackingTouch = false; + } + }; + + hue.setOnSeekBarChangeListener(listener); + saturation.setOnSeekBarChangeListener(listener); + brightness.setOnSeekBarChangeListener(listener); + } + + @Override + public void setColor(int color, boolean animate) { + super.setColor(color, animate); + SeekBar[] bars = new SeekBar[]{hue, saturation, brightness}; + float[] values = new float[3]; + Color.colorToHSV(color, values); + values[1] *= 255; + values[2] *= 255; + + for (int i = 0; i < bars.length; i++) { + if (animate && !isTrackingTouch) { + ObjectAnimator animator = ObjectAnimator.ofInt(bars[i], "progress", (int) values[i]); + animator.setInterpolator(new DecelerateInterpolator()); + animator.start(); + } else { + bars[i].setProgress((int) values[i]); + } + } + + updateProgressBars(); + } + + @Override + public int getColor() { + int color = Color.HSVToColor(new float[]{hue.getProgress(), saturation.getProgress() / 255f, brightness.getProgress() / 255f}); + return (getColorAlpha() << 24) | (color & 0x00ffffff); + } + + @Override + protected void onColorPicked() { + super.onColorPicked(); + updateProgressBars(); + } + + private void updateProgressBars() { + ColorUtils.setProgressBarDrawable(hue, new GradientDrawable( + GradientDrawable.Orientation.LEFT_RIGHT, + ColorUtils.getColorWheelArr(saturation.getProgress() / 255f, brightness.getProgress() / 255f) + )); + + ColorUtils.setProgressBarDrawable(saturation, new GradientDrawable( + GradientDrawable.Orientation.LEFT_RIGHT, + new int[]{ + Color.HSVToColor(new float[]{hue.getProgress(), 0, brightness.getProgress() / 255f}), + Color.HSVToColor(new float[]{hue.getProgress(), 1, brightness.getProgress() / 255f}) + } + )); + + ColorUtils.setProgressBarDrawable(brightness, new GradientDrawable( + GradientDrawable.Orientation.LEFT_RIGHT, + new int[]{ + Color.HSVToColor(new float[]{hue.getProgress(), saturation.getProgress() / 255f, 0}), + Color.HSVToColor(new float[]{hue.getProgress(), saturation.getProgress() / 255f, 1}) + } + )); + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/HeightableViewPager.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/HeightableViewPager.java new file mode 100644 index 0000000..6da54a9 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/HeightableViewPager.java @@ -0,0 +1,46 @@ +package me.jfenn.colorpickerdialog.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + +public class HeightableViewPager extends ViewPager { + + public HeightableViewPager(@NonNull Context context) { + super(context); + } + + public HeightableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int height = 0; + for (int i = 0; i < getChildCount(); i++) { + View v = getChildAt(i); + v.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + height = Math.max(height, v.getMeasuredHeight()); + } + + if (height != 0) + heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/RGBPickerView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/RGBPickerView.java new file mode 100644 index 0000000..c03eaa9 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/RGBPickerView.java @@ -0,0 +1,113 @@ +package me.jfenn.colorpickerdialog.views; + +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.animation.DecelerateInterpolator; +import android.widget.SeekBar; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatSeekBar; +import me.jfenn.colorpickerdialog.R; +import me.jfenn.colorpickerdialog.utils.ColorUtils; + +public class RGBPickerView extends ColorPickerView { + + private AppCompatSeekBar red, green, blue; + private TextView redInt, greenInt, blueInt; + private boolean isTrackingTouch; + + public RGBPickerView(Context context) { + super(context); + } + + public RGBPickerView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RGBPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public RGBPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + void init() { + inflate(getContext(), R.layout.layout_rgb_picker, this); + red = findViewById(R.id.red); + redInt = findViewById(R.id.redInt); + green = findViewById(R.id.green); + greenInt = findViewById(R.id.greenInt); + blue = findViewById(R.id.blue); + blueInt = findViewById(R.id.blueInt); + + SeekBar.OnSeekBarChangeListener listener = new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int i, boolean b) { + if (seekBar.getId() == R.id.red) { + redInt.setText(String.format("%s", i)); + } else if (seekBar.getId() == R.id.green) { + greenInt.setText(String.format("%s", i)); + } else if (seekBar.getId() == R.id.blue) { + blueInt.setText(String.format("%s", i)); + } + onColorPicked(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + isTrackingTouch = true; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + isTrackingTouch = false; + } + }; + + red.setOnSeekBarChangeListener(listener); + green.setOnSeekBarChangeListener(listener); + blue.setOnSeekBarChangeListener(listener); + + ColorUtils.setProgressBarColor( + red, + ColorUtils.fromAttrRes(getContext(), R.attr.redColor, R.color.colorPickerDialog_red) + ); + + ColorUtils.setProgressBarColor( + green, + ColorUtils.fromAttrRes(getContext(), R.attr.greenColor, R.color.colorPickerDialog_green) + ); + + ColorUtils.setProgressBarColor( + blue, + ColorUtils.fromAttrRes(getContext(), R.attr.blueColor, R.color.colorPickerDialog_blue) + ); + } + + @Override + public void setColor(int color, boolean animate) { + super.setColor(color, animate); + SeekBar[] bars = new SeekBar[]{red, green, blue}; + int[] offsets = new int[]{16, 8, 0}; + for (int i = 0; i < bars.length; i++) { + int value = (color >> offsets[i]) & 0xFF; + if (animate && !isTrackingTouch) { + ObjectAnimator animator = ObjectAnimator.ofInt(bars[i], "progress", value); + animator.setInterpolator(new DecelerateInterpolator()); + animator.start(); + } else { + bars[i].setProgress(value); + } + } + } + + @Override + public int getColor() { + return Color.argb(getColorAlpha(), red.getProgress(), green.getProgress(), blue.getProgress()); + } +} diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/RenderableView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/RenderableView.java new file mode 100644 index 0000000..a87138f --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/RenderableView.java @@ -0,0 +1,78 @@ +package me.jfenn.colorpickerdialog.views; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.AsyncTask; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.Nullable; +import me.jfenn.colorpickerdialog.utils.CanvasRenderTask; + +public abstract class RenderableView extends View implements CanvasRenderTask.Renderable { + + private Paint paint; + private Bitmap render; + private AsyncTask task; + + private int width, height; + + public RenderableView(Context context) { + super(context); + init(); + } + + public RenderableView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public RenderableView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + @TargetApi(21) + public RenderableView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + void init() { + paint = new Paint(); + paint.setDither(true); + paint.setAntiAlias(true); + } + + public void startRender() { + if (task != null) + task.cancel(true); + + task = new CanvasRenderTask(this).execute(width, height); + } + + @Override + public void onRendered(@Nullable Bitmap bitmap) { + if (render != null && render != bitmap) + render.recycle(); + + task = null; + render = bitmap; + postInvalidate(); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + width = canvas.getWidth(); + height = canvas.getHeight(); + + if (render != null) + canvas.drawBitmap(render, 0, 0, paint); + else if (task == null) + startRender(); + } +} \ No newline at end of file diff --git a/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/SmoothColorView.java b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/SmoothColorView.java new file mode 100644 index 0000000..5d4d0a9 --- /dev/null +++ b/colorpickerdialog/src/main/java/me/jfenn/colorpickerdialog/views/SmoothColorView.java @@ -0,0 +1,68 @@ +package me.jfenn.colorpickerdialog.views; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import me.jfenn.colorpickerdialog.utils.AlphaColorDrawable; + +public class SmoothColorView extends View { + + private AlphaColorDrawable previous; + + public SmoothColorView(Context context) { + super(context); + init(); + } + + public SmoothColorView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public SmoothColorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + @TargetApi(21) + public SmoothColorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + private void init() { + previous = new AlphaColorDrawable(Color.BLACK); + setBackground(previous); + } + + public void setColor(@ColorInt int color) { + setColor(color, false); + } + + public void setColor(@ColorInt int color, boolean animate) { + AlphaColorDrawable current = new AlphaColorDrawable(color); + + if (previous != null && animate) { + TransitionDrawable transition = new TransitionDrawable(new Drawable[]{previous, current}); + setBackground(transition); + transition.startTransition(100); + } else setBackground(current); + + previous = current; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int height = (int) (getMeasuredWidth() * 0.5625); + setMeasuredDimension(getMeasuredWidth(), height); + } +} diff --git a/colorpickerdialog/src/main/res/drawable/ic_image.xml b/colorpickerdialog/src/main/res/drawable/ic_image.xml deleted file mode 100644 index bd03e2e..0000000 --- a/colorpickerdialog/src/main/res/drawable/ic_image.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/colorpickerdialog/src/main/res/drawable/ic_reset.xml b/colorpickerdialog/src/main/res/drawable/ic_reset.xml deleted file mode 100644 index e68bc65..0000000 --- a/colorpickerdialog/src/main/res/drawable/ic_reset.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/colorpickerdialog/src/main/res/layout/dialog_color_picker.xml b/colorpickerdialog/src/main/res/layout/dialog_color_picker.xml index d19ab63..e0b28d4 100644 --- a/colorpickerdialog/src/main/res/layout/dialog_color_picker.xml +++ b/colorpickerdialog/src/main/res/layout/dialog_color_picker.xml @@ -1,220 +1,58 @@ + android:layout_height="match_parent" + android:minWidth="350dp"> + android:layout_height="wrap_content"> - + android:layout_height="wrap_content" /> - + android:textCursorDrawable="@null" + android:textSize="24sp" + tools:text="#000000" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_gravity="center_horizontal" + app:tabIndicatorHeight="0dp" + app:tabMode="scrollable" /> - - - - - - - - - - - - - - - - - - - - - + android:layout_height="wrap_content" /> @@ -231,28 +69,30 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" - android:paddingBottom="8dp" android:paddingLeft="12dp" - android:paddingRight="12dp" android:paddingTop="8dp" + android:paddingRight="12dp" + android:paddingBottom="8dp" android:text="@android:string/cancel" - android:textColor="@color/textColorPrimary" android:textAllCaps="true" - android:textSize="16sp" /> + android:textColor="?android:attr/textColorPrimary" + android:textSize="14sp" + android:textStyle="bold" /> + android:textColor="?android:attr/textColorPrimary" + android:textSize="14sp" + android:textStyle="bold" /> diff --git a/colorpickerdialog/src/main/res/layout/dialog_image_color_picker.xml b/colorpickerdialog/src/main/res/layout/dialog_image_color_picker.xml index 62e2b64..c41738d 100644 --- a/colorpickerdialog/src/main/res/layout/dialog_image_color_picker.xml +++ b/colorpickerdialog/src/main/res/layout/dialog_image_color_picker.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -46,7 +46,7 @@ android:paddingRight="12dp" android:paddingTop="8dp" android:text="@android:string/ok" - android:textColor="@color/textColorPrimary" + android:textColor="?android:attr/textColorPrimary" android:textAllCaps="true" android:textSize="16sp" /> diff --git a/colorpickerdialog/src/main/res/layout/layout_hsv_picker.xml b/colorpickerdialog/src/main/res/layout/layout_hsv_picker.xml new file mode 100644 index 0000000..17f2bc6 --- /dev/null +++ b/colorpickerdialog/src/main/res/layout/layout_hsv_picker.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/colorpickerdialog/src/main/res/layout/layout_rgb_picker.xml b/colorpickerdialog/src/main/res/layout/layout_rgb_picker.xml new file mode 100644 index 0000000..d41cb04 --- /dev/null +++ b/colorpickerdialog/src/main/res/layout/layout_rgb_picker.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/colorpickerdialog/src/main/res/values/attrs.xml b/colorpickerdialog/src/main/res/values/attrs.xml new file mode 100644 index 0000000..2ec6a1c --- /dev/null +++ b/colorpickerdialog/src/main/res/values/attrs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/colorpickerdialog/src/main/res/values/colors.xml b/colorpickerdialog/src/main/res/values/colors.xml index 8f16ec1..5c4c4ab 100644 --- a/colorpickerdialog/src/main/res/values/colors.xml +++ b/colorpickerdialog/src/main/res/values/colors.xml @@ -2,16 +2,9 @@ - #DE000000 - #8A000000 - #61000000 - - #FFFFFF - #B3FFFFFF - #80FFFFFF - - #D32F2F - #388E3C - #1976D2 + #D32F2F + #388E3C + #1976D2 + #000000 diff --git a/colorpickerdialog/src/main/res/values/strings.xml b/colorpickerdialog/src/main/res/values/strings.xml index 98d804d..1249313 100644 --- a/colorpickerdialog/src/main/res/values/strings.xml +++ b/colorpickerdialog/src/main/res/values/strings.xml @@ -1,12 +1,18 @@ - Color Picker - Image Picker + Color Picker + Image Picker - Reset - Pick Color from Image + Reset + Pick Color from Image - Red - Blue - Green - Image + R + B + G + Image + A + H + S + V + RGB + HSV diff --git a/colorpickerdialog/src/main/res/values/styles.xml b/colorpickerdialog/src/main/res/values/styles.xml deleted file mode 100644 index a0d1527..0000000 --- a/colorpickerdialog/src/main/res/values/styles.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - -