├── .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 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-AndroidColorPicker-brightgreen.svg?style=flat)](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 | ![](/publish/screenshot.jpg) 12 | 13 | #### Sample App 14 | [Download Sample](https://github.com/jbruchanov/AndroidColorPicker/blob/master/publish/color_picker.apk) 15 | 16 | ![alt tag](http://chart.apis.google.com/chart?cht=qr&chs=200x200&chl=https://github.com/jbruchanov/AndroidColorPicker/blob/master/publish/color_picker.apk&chld=H|0) 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 | 8 | 9 | 10 | 11 | 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 | --------------------------------------------------------------------------------