├── .gitignore
├── ColorPicker.iml
├── README.md
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── scurab
│ │ └── android
│ │ └── colorpicker
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── scurab
│ │ └── android
│ │ └── colorpicker
│ │ ├── GradientView.java
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-xxhdpi
│ ├── pointer_arrows.png
│ └── pointer_simple.png
│ ├── drawable
│ └── gradient_background.xml
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ └── strings.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── publish
├── color_picker.apk
└── screenshot.jpg
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 | #built application files
9 | *.apk
10 | *.ap_
11 |
12 | # files for the dex VM
13 | *.dex
14 |
15 | # Java class files
16 | *.class
17 |
18 | # generated files
19 | bin/
20 | gen/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Windows thumbnail db
26 | Thumbs.db
27 |
28 | # OSX files
29 | .DS_Store
30 |
31 | # Eclipse project files
32 | .classpath
33 | .project
34 |
35 | # Android Studio
36 | .idea
37 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
38 | .gradle
39 | build/
--------------------------------------------------------------------------------
/ColorPicker.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://android-arsenal.com/details/1/1738)
2 | # AndroidColorPicker
3 |
4 | Very simple color picker, Android equialent of
5 | https://github.com/FWCarlos/NKO-Color-Picker-View-iOS
6 |
7 | just simple [GradientView](https://github.com/jbruchanov/AndroidColorPicker/blob/master/app/src/main/java/com/scurab/android/colorpicker/GradientView.java)
8 |
9 |
10 | ### Screenshot Android
11 | 
12 |
13 | #### Sample App
14 | [Download Sample](https://github.com/jbruchanov/AndroidColorPicker/blob/master/publish/color_picker.apk)
15 |
16 | 
17 |
18 | #### License
19 | Licensed under the Apache License, Version 2.0 (the "License");
20 | you may not use this file except in compliance with the License.
21 | You may obtain a copy of the License at
22 |
23 | http://www.apache.org/licenses/LICENSE-2.0
24 |
25 | Unless required by applicable law or agreed to in writing, software
26 | distributed under the License is distributed on an "AS IS" BASIS,
27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28 | See the License for the specific language governing permissions and
29 | limitations under the License.
30 |
31 | Licensed under the Apache License, Version 2.0 (the "License");
32 | you may not use this file except in compliance with the License.
33 | You may obtain a copy of the License at
34 |
35 | http://www.apache.org/licenses/LICENSE-2.0
36 |
37 | Unless required by applicable law or agreed to in writing, software
38 | distributed under the License is distributed on an "AS IS" BASIS,
39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40 | See the License for the specific language governing permissions and
41 | limitations under the License.
42 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.0"
6 |
7 | defaultConfig {
8 | applicationId "com.scurab.android.colorpicker"
9 | minSdkVersion 15
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | }
25 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in f:\0Work\android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/scurab/android/colorpicker/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.scurab.android.colorpicker;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/scurab/android/colorpicker/GradientView.java:
--------------------------------------------------------------------------------
1 | package com.scurab.android.colorpicker;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.ComposeShader;
9 | import android.graphics.LinearGradient;
10 | import android.graphics.Paint;
11 | import android.graphics.PorterDuff;
12 | import android.graphics.RectF;
13 | import android.graphics.Shader;
14 | import android.graphics.drawable.Drawable;
15 | import android.os.Parcel;
16 | import android.os.Parcelable;
17 | import android.util.AttributeSet;
18 | import android.util.TypedValue;
19 | import android.view.MotionEvent;
20 | import android.view.View;
21 |
22 | /**
23 | * Created by jbruchanov on 27/03/2015.
24 | */
25 | @SuppressWarnings("unused")
26 | public class GradientView extends View {
27 |
28 | public interface OnColorChangedListener {
29 | void onColorChanged(GradientView view, int color);
30 | }
31 |
32 | private static final boolean DEBUG = false;
33 |
34 | private static final int[] GRAD_COLORS = new int[]{Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED};
35 | private static final int[] GRAD_ALPHA = new int[]{Color.WHITE, Color.TRANSPARENT};
36 |
37 | private GradientView mBrightnessGradientView;
38 | private Shader mShader;
39 | private Drawable mPointerDrawable;
40 | private Paint mPaint;
41 | private Paint mDebugPaint;
42 | private Paint mPaintBackground;
43 | private RectF mGradientRect = new RectF();
44 |
45 | private int[] mGradColors = GRAD_COLORS;
46 | private int[] mGradAlpha = GRAD_ALPHA;
47 | private float[] mHSV = new float[]{1f, 1f, 1f};
48 |
49 | private int[] mSelectedColorGradient = new int[]{0, Color.BLACK};
50 | private float mRadius = 0;
51 | private int mSelectedColor = 0;
52 | private boolean mIsBrightnessGradient = false;
53 | private int mLastX = Integer.MIN_VALUE;
54 | private int mLastY;
55 | private int mPointerHeight;
56 | private int mPointerWidth;
57 | private boolean mLockPointerInBounds = false;
58 |
59 | private OnColorChangedListener mOnColorChangedListener;
60 |
61 | public GradientView(Context context) {
62 | super(context);
63 | init(null);
64 | }
65 |
66 | public GradientView(Context context, AttributeSet attrs) {
67 | super(context, attrs);
68 | init(attrs);
69 | }
70 |
71 | public GradientView(Context context, AttributeSet attrs, int defStyleAttr) {
72 | super(context, attrs, defStyleAttr);
73 | init(attrs);
74 | }
75 |
76 | @SuppressLint("NewApi")
77 | public GradientView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
78 | super(context, attrs, defStyleAttr, defStyleRes);
79 | init(attrs);
80 | }
81 |
82 | private void init(AttributeSet attrs) {
83 | setClickable(true);
84 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
85 | mPaintBackground = new Paint(Paint.ANTI_ALIAS_FLAG);
86 | mPaintBackground.setColor(Color.WHITE);
87 | setLayerType(View.LAYER_TYPE_SOFTWARE, isInEditMode() ? null : mPaint);
88 | mRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
89 |
90 | if (DEBUG) {
91 | mDebugPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
92 | mDebugPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()));
93 | }
94 |
95 | if (attrs != null) {
96 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.GradientView);
97 | for (int i = 0, n = typedArray.getIndexCount(); i < n; i++) {
98 | int index = typedArray.getIndex(i);
99 | switch (index) {
100 | case R.styleable.GradientView_radius:
101 | setRadius(typedArray.getDimensionPixelSize(index, 0));
102 | break;
103 | case R.styleable.GradientView_pointerDrawable:
104 | setPointerDrawable(typedArray.getDrawable(index));
105 | break;
106 | case R.styleable.GradientView_lockPointerInBounds:
107 | setLockPointerInBounds(typedArray.getBoolean(index, false));
108 | break;
109 | }
110 | }
111 | typedArray.recycle();
112 | }
113 | }
114 |
115 | @Override
116 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
117 | int desiredWidth = 0;
118 | int desiredHeight = 0;
119 |
120 | if (mPointerDrawable != null) {
121 | desiredHeight = mPointerDrawable.getIntrinsicHeight();
122 | //this is nonsense, but at least have something than 0
123 | desiredWidth = mPointerDrawable.getIntrinsicWidth();
124 | }
125 |
126 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
127 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
128 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
129 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
130 |
131 | int width;
132 | int height;
133 |
134 | //Measure Width
135 | if (widthMode == MeasureSpec.EXACTLY) {
136 | //Must be this size
137 | width = widthSize;
138 | } else if (widthMode == MeasureSpec.AT_MOST) {
139 | //Can't be bigger than...
140 | width = Math.min(desiredWidth, widthSize);
141 | } else {
142 | //Be whatever you want
143 | width = desiredWidth;
144 | }
145 |
146 | //Measure Height
147 | if (heightMode == MeasureSpec.EXACTLY) {
148 | //Must be this size
149 | height = heightSize;
150 | } else if (heightMode == MeasureSpec.AT_MOST) {
151 | //Can't be bigger than...
152 | height = Math.min(desiredHeight, heightSize);
153 | } else {
154 | //Be whatever you want
155 | height = desiredHeight;
156 | }
157 |
158 | //MUST CALL THIS
159 | setMeasuredDimension(width, height);
160 | }
161 |
162 | @Override
163 | protected void onDraw(Canvas canvas) {
164 | super.onDraw(canvas);
165 | if (mShader != null) {
166 | canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mPaintBackground);
167 | canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mPaint);
168 | }
169 |
170 | if (DEBUG) {
171 | String color = "#" + Integer.toHexString(mSelectedColor);
172 | float width = mPaint.measureText(color);
173 | mDebugPaint.setColor(mSelectedColor);
174 | mDebugPaint.setStyle(Paint.Style.FILL);
175 | canvas.drawRect(0, mGradientRect.bottom - mDebugPaint.getTextSize(), getWidth(), mGradientRect.bottom, mDebugPaint);
176 | mDebugPaint.setColor(Color.BLACK);
177 | canvas.drawText(color, 0, mGradientRect.bottom, mDebugPaint);
178 | }
179 | onDrawPointer(canvas);
180 |
181 | }
182 |
183 | private void onDrawPointer(Canvas canvas){
184 | if (mPointerDrawable != null) {
185 | int vh = getHeight();
186 | int pwh = mPointerWidth >> 1;
187 | int phh = mPointerHeight >> 1;
188 | float tx, ty;
189 | if (!mIsBrightnessGradient) {
190 | tx = mLastX - pwh;
191 | ty = mLastY - phh;
192 | if (mLockPointerInBounds) {
193 | tx = Math.max(mGradientRect.left, Math.min(tx, mGradientRect.right - mPointerWidth));
194 | ty = Math.max(mGradientRect.top, Math.min(ty, mGradientRect.bottom - mPointerHeight));
195 | } else {
196 | tx = Math.max(mGradientRect.left - pwh, Math.min(tx, mGradientRect.right - pwh));
197 | ty = Math.max(mGradientRect.top - pwh, Math.min(ty, mGradientRect.bottom - phh));
198 | }
199 | } else {//vertical lock
200 | tx = mLastX - pwh;
201 | ty = mPointerHeight != mPointerDrawable.getIntrinsicHeight() ? (vh >> 1) - phh : 0;
202 | if (mLockPointerInBounds) {
203 | tx = Math.max(mGradientRect.left, Math.min(tx, mGradientRect.right - mPointerWidth));
204 | ty = Math.max(mGradientRect.top, Math.min(ty, mGradientRect.bottom - mPointerHeight));
205 | } else {
206 | tx = Math.max(mGradientRect.left - pwh, Math.min(tx, mGradientRect.right - pwh));
207 | ty = Math.max(mGradientRect.top - pwh, Math.min(ty, mGradientRect.bottom - phh));
208 | }
209 | }
210 | canvas.translate(tx, ty);
211 | mPointerDrawable.draw(canvas);
212 | canvas.translate(-tx, -ty);
213 | }
214 | }
215 |
216 | @Override
217 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
218 | super.onLayout(changed, left, top, right, bottom);
219 | mGradientRect.set(getPaddingLeft(), getPaddingTop(), right - left - getPaddingRight(), bottom - top - getPaddingBottom());
220 | if (changed) {
221 | buildShaders();
222 | }
223 |
224 | if (mPointerDrawable != null) {
225 | int h = (int) mGradientRect.height();
226 | int ph = mPointerDrawable.getIntrinsicHeight();
227 | int pw = mPointerDrawable.getIntrinsicWidth();
228 | mPointerHeight = ph;
229 | mPointerWidth = pw;
230 | if (h < ph) {
231 | mPointerHeight = h;
232 | mPointerWidth = (int) (pw * (h / (float)ph));
233 | }
234 | mPointerDrawable.setBounds(0, 0, mPointerWidth, mPointerHeight);
235 | updatePointerPosition();
236 | }
237 | }
238 |
239 | private void buildShaders() {
240 | if (mIsBrightnessGradient) {
241 | mShader = new LinearGradient(mGradientRect.left, mGradientRect.top, mGradientRect.right, mGradientRect.top /* simple line gradient*/, mSelectedColorGradient, null, Shader.TileMode.CLAMP);
242 | } else {
243 | LinearGradient gradientShader = new LinearGradient(mGradientRect.left, mGradientRect.top, mGradientRect.right, mGradientRect.top /* simple line gradient*/, GRAD_COLORS, null, Shader.TileMode.CLAMP);
244 | LinearGradient alphaShader = new LinearGradient(0, mGradientRect.top + (mGradientRect.height() / 3) /* don't start at 0px*/, 0, mGradientRect.bottom, GRAD_ALPHA, null, Shader.TileMode.CLAMP);
245 | mShader = new ComposeShader(alphaShader, gradientShader, PorterDuff.Mode.MULTIPLY);
246 | }
247 | mPaint.setShader(mShader);
248 | }
249 |
250 | /**
251 | * Set radius for gradient rectangle
252 | * @param radius
253 | */
254 | public void setRadius(float radius) {
255 | if(radius != mRadius) {
256 | mRadius = radius;
257 | mRadius = radius;
258 | invalidate();
259 | }
260 | }
261 |
262 | public float getRadius() {
263 | return mRadius;
264 | }
265 |
266 | @Override
267 | public boolean onTouchEvent(MotionEvent event) {
268 | mLastX = (int) event.getX();
269 | mLastY = (int) event.getY();
270 | onUpdateColorSelection(mLastX, mLastY);
271 | invalidate();
272 | switch (event.getAction()) {
273 | case MotionEvent.ACTION_DOWN:
274 | getParent().requestDisallowInterceptTouchEvent(true);
275 | break;
276 | case MotionEvent.ACTION_UP:
277 | case MotionEvent.ACTION_CANCEL:
278 | getParent().requestDisallowInterceptTouchEvent(false);
279 | break;
280 | }
281 | return super.onTouchEvent(event);
282 | }
283 |
284 | /**
285 | * Update color based on touch events
286 | * @param x
287 | * @param y
288 | */
289 | protected void onUpdateColorSelection(int x, int y) {
290 | x = (int) Math.max(mGradientRect.left, Math.min(x, mGradientRect.right));
291 | y = (int) Math.max(mGradientRect.top, Math.min(y, mGradientRect.bottom));
292 | if (mIsBrightnessGradient) {
293 | float b = pointToValueBrightness(x);
294 | mHSV[2] = b;
295 | mSelectedColor = Color.HSVToColor(mHSV);
296 | } else {
297 | float hue = pointToHue(x);
298 | float sat = pointToSaturation(y);
299 | mHSV[0] = hue;
300 | mHSV[1] = sat;
301 | mHSV[2] = 1f;
302 | mSelectedColor = Color.HSVToColor(mHSV);
303 | }
304 | dispatchColorChanged(mSelectedColor);
305 | String color = Integer.toHexString(mSelectedColor);
306 | }
307 |
308 | protected void dispatchColorChanged(int color) {
309 | if (mBrightnessGradientView != null) {
310 | mBrightnessGradientView.setColor(color, false);
311 | }
312 | if (mOnColorChangedListener != null) {
313 | mOnColorChangedListener.onColorChanged(this, color);
314 | }
315 | }
316 |
317 | /**
318 | * Switch view into brightness gradient only
319 | * @param isBrightnessGradient
320 | */
321 | public void setIsBrightnessGradient(boolean isBrightnessGradient) {
322 | mIsBrightnessGradient = isBrightnessGradient;
323 | }
324 |
325 | /**
326 | * Get current selectec color
327 | * @return
328 | */
329 | public int getSelectedColor() {
330 | return mSelectedColor;
331 | }
332 |
333 | /**
334 | * Update current color
335 | * @param selectedColor
336 | */
337 | public void setColor(int selectedColor) {
338 | setColor(selectedColor, true);
339 | }
340 |
341 | protected void setColor(int selectedColor, boolean updatePointers) {
342 | Color.colorToHSV(selectedColor, mHSV);
343 | if (mIsBrightnessGradient) {
344 | mSelectedColorGradient[0] = getColorForGradient(mHSV);
345 | mSelectedColor = Color.HSVToColor(mHSV);
346 | buildShaders();
347 | if (mLastX != Integer.MIN_VALUE) {
348 | mHSV[2] = pointToValueBrightness(mLastX);
349 | }
350 | selectedColor = Color.HSVToColor(mHSV);
351 | }
352 | if (updatePointers) {
353 | updatePointerPosition();
354 | }
355 | mSelectedColor = selectedColor;
356 | invalidate();
357 | dispatchColorChanged(mSelectedColor);
358 | }
359 |
360 | /**
361 | * Get start color for gradient
362 | * @param hsv
363 | * @return
364 | */
365 | private int getColorForGradient(float[] hsv) {
366 | if (hsv[2] != 1f) {
367 | float oldV = hsv[2];
368 | hsv[2] = 1;
369 | int color = Color.HSVToColor(hsv);
370 | hsv[2] = oldV;
371 | return color;
372 | }else{
373 | return Color.HSVToColor(hsv);
374 | }
375 | }
376 |
377 | private void updatePointerPosition() {
378 | if (mGradientRect.width() != 0 && mGradientRect.height() != 0) {
379 | if (!mIsBrightnessGradient) {
380 | mLastX = hueToPoint(mHSV[0]);
381 | mLastY = saturationToPoint(mHSV[1]);
382 | } else {
383 | mLastX = brightnessToPoint(mHSV[2]);
384 | }
385 | }
386 | }
387 |
388 | /**
389 | * Add reference for brightness view
390 | * @param brightnessGradient
391 | */
392 | public void setBrightnessGradientView(GradientView brightnessGradient) {
393 | if (mBrightnessGradientView != brightnessGradient) {
394 | mBrightnessGradientView = brightnessGradient;
395 |
396 | if (mBrightnessGradientView != null) {
397 | mBrightnessGradientView.setIsBrightnessGradient(true);
398 | mBrightnessGradientView.setColor(mSelectedColor);
399 | }
400 | }
401 | }
402 |
403 | public void setOnColorChangedListener(OnColorChangedListener onColorChangedListener) {
404 | mOnColorChangedListener = onColorChangedListener;
405 | }
406 |
407 | //region HSL math
408 | /**
409 | *
410 | * @param x x coordinate of gradient
411 | * @return
412 | */
413 | private float pointToHue(float x) {
414 | x = x - mGradientRect.left;
415 | return x * 360f / mGradientRect.width();
416 | }
417 |
418 | private int hueToPoint(float hue) {
419 | return (int)(mGradientRect.left + ((hue * mGradientRect.width()) / 360));
420 | }
421 |
422 | /**
423 | * Get saturation
424 | *
425 | * @param y
426 | * @return
427 | */
428 | private float pointToSaturation(float y) {
429 | y = y - mGradientRect.top;
430 | return 1 - (1.f / mGradientRect.height() * y);
431 | }
432 |
433 | private int saturationToPoint(float sat) {
434 | sat = 1 - sat;
435 | return (int) (mGradientRect.top + (mGradientRect.height() * sat));
436 | }
437 |
438 | /**
439 | * Get value of brightness
440 | *
441 | * @param x
442 | * @return
443 | */
444 | private float pointToValueBrightness(float x) {
445 | x = x - mGradientRect.left;
446 | return 1 - (1.f / mGradientRect.width() * x);
447 | }
448 |
449 | private int brightnessToPoint(float val) {
450 | val = 1 - val;
451 | return (int) (mGradientRect.left + (mGradientRect.width() * val));
452 | }
453 | //endregion HSL math
454 |
455 | public void setPointerDrawable(Drawable pointerDrawable) {
456 | if (mPointerDrawable != pointerDrawable) {
457 | mPointerDrawable = pointerDrawable;
458 | requestLayout();
459 | }
460 | }
461 |
462 | public void setLockPointerInBounds(boolean lockPointerInBounds) {
463 | if (lockPointerInBounds != mLockPointerInBounds) {
464 | mLockPointerInBounds = lockPointerInBounds;
465 | invalidate();
466 | }
467 | }
468 |
469 | public boolean isLockedPointerInBounds() {
470 | return mLockPointerInBounds;
471 | }
472 |
473 | @Override
474 | protected Parcelable onSaveInstanceState() {
475 | Parcelable superState = super.onSaveInstanceState();
476 | SavedState ss = new SavedState(superState);
477 | ss.isBrightnessGradient = mIsBrightnessGradient;
478 | ss.color = mSelectedColor;
479 | return ss;
480 | }
481 |
482 | @Override
483 | protected void onRestoreInstanceState(Parcelable state) {
484 | if(!(state instanceof SavedState)) {
485 | super.onRestoreInstanceState(state);
486 | return;
487 | }
488 |
489 | SavedState ss = (SavedState)state;
490 | super.onRestoreInstanceState(ss.getSuperState());
491 |
492 | mIsBrightnessGradient = ss.isBrightnessGradient;
493 | setColor(ss.color, true);
494 | }
495 |
496 | private static class SavedState extends BaseSavedState {
497 | int color;
498 | boolean isBrightnessGradient;
499 |
500 | SavedState(Parcelable superState) {
501 | super(superState);
502 | }
503 |
504 | private SavedState(Parcel in) {
505 | super(in);
506 | color = in.readInt();
507 | isBrightnessGradient = in.readInt() == 1;
508 | }
509 |
510 | @Override
511 | public void writeToParcel(Parcel out, int flags) {
512 | super.writeToParcel(out, flags);
513 | out.writeInt(color);
514 | out.writeInt(isBrightnessGradient ? 1 : 0);
515 | }
516 |
517 | //required field that makes Parcelables from a Parcel
518 | public static final Parcelable.Creator CREATOR =
519 | new Parcelable.Creator() {
520 | public SavedState createFromParcel(Parcel in) {
521 | return new SavedState(in);
522 | }
523 |
524 | public SavedState[] newArray(int size) {
525 | return new SavedState[size];
526 | }
527 | };
528 | }
529 | }
530 |
--------------------------------------------------------------------------------
/app/src/main/java/com/scurab/android/colorpicker/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.scurab.android.colorpicker;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.TextView;
9 |
10 |
11 | public class MainActivity extends Activity {
12 |
13 | private GradientView mTop;
14 | private GradientView mBottom;
15 | private TextView mTextView;
16 | private Drawable mIcon;
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | View view = View.inflate(this, R.layout.activity_main, null);
22 | setContentView(view);
23 | mIcon = getResources().getDrawable(R.mipmap.ic_launcher);
24 | mTextView = (TextView)findViewById(R.id.color);
25 | mTextView.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
26 | mTop = (GradientView)findViewById(R.id.top);
27 | mBottom = (GradientView)findViewById(R.id.bottom);
28 | mTop.setBrightnessGradientView(mBottom);
29 | mBottom.setOnColorChangedListener(new GradientView.OnColorChangedListener() {
30 | @Override
31 | public void onColorChanged(GradientView view, int color) {
32 | mTextView.setTextColor(color);
33 | mTextView.setText("#" + Integer.toHexString(color));
34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
35 | mIcon.setTint(color);
36 | }
37 | }
38 | });
39 |
40 | int color = 0xFF394572;
41 | mTop.setColor(color);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/pointer_arrows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbruchanov/AndroidColorPicker/e3d0f4bd243af5dcaff21f96a3095712270f875a/app/src/main/res/drawable-xxhdpi/pointer_arrows.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/pointer_simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbruchanov/AndroidColorPicker/e3d0f4bd243af5dcaff21f96a3095712270f875a/app/src/main/res/drawable-xxhdpi/pointer_simple.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gradient_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
16 |
17 |
24 |
25 |
35 |
36 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbruchanov/AndroidColorPicker/e3d0f4bd243af5dcaff21f96a3095712270f875a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ff7c7c7c
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 10dp
6 | 3dp
7 | 5dp
8 | 20dp
9 | 20sp
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ColorPicker
3 |
4 | Hello world!
5 |
6 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.1.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbruchanov/AndroidColorPicker/e3d0f4bd243af5dcaff21f96a3095712270f875a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/publish/color_picker.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbruchanov/AndroidColorPicker/e3d0f4bd243af5dcaff21f96a3095712270f875a/publish/color_picker.apk
--------------------------------------------------------------------------------
/publish/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbruchanov/AndroidColorPicker/e3d0f4bd243af5dcaff21f96a3095712270f875a/publish/screenshot.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------