«   2025/01   »
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
Tags
more
Archives
Today
Total
관리 메뉴

올해는 머신러닝이다.

Photoshop like color picker 본문

Android/Tip&Tech

Photoshop like color picker

행복한 수지아빠 2011. 5. 24. 16:04

Depending on your application, you might one day need a good color picker.

With Shake Them All! I needed a color picker to select the color of the default skin and default background. I first had a look at the Android API to see if I could find a Dialog that I could use, without success. I then looked at the SDK samples, and found this color picker:

ColorPickerDialog - Before

Before

It is nice but quite limited regarding the number of colors available (black and white are missing for instance),  so I used it for a while but I soon needed to make my own.

As I’m quite familiar with the Photoshop color picker, I wanted to make one that looked like it and came up with this list of feature:

  • it should allow the selection of any color without limitation
  • be able to keep the default color in memory and restore it
  • be implemented as a custom Dialog for re usability
  • and still be simple enough to be used on a smartphone
  • finally the selected color should be associated to a key to be stored in the SharedPreferences

ColorPickerDialog - After

After

To achieve this, I needed to implement my own custom Dialog (ColorPickerDialog), associated with a custom View (ColorPickerView).

I’m now going to describe the classes and methods, so if you’re in a hurry, you can jump directly to the end of the post where you will find the download link with instructions on how to use the dialog.

ColorPickerDialog

The ColorPickerDialog is quite simple.

01 public class ColorPickerDialog extends Dialog {
02     public interface OnColorChangedListener {
03         void colorChanged(String key, int color);
04     }
05  
06     private OnColorChangedListener mListener;
07     private int mInitialColor, mDefaultColor;
08     private String mKey;
09  
10     public ColorPickerDialog(Context context, OnColorChangedListener listener, String key, int initialColor, int defaultColor) {
11         super(context);
12  
13         mListener = listener;
14         mKey = key;
15         mInitialColor = initialColor;
16         mDefaultColor = defaultColor;
17     }
18  
19     @Override
20     protected void onCreate(Bundle savedInstanceState) {
21         super.onCreate(savedInstanceState);
22         OnColorChangedListener l = new OnColorChangedListener() {
23             public void colorChanged(String key, int color) {
24                 mListener.colorChanged(mKey, color);
25                 dismiss();
26             }
27         };
28  
29         setContentView(new ColorPickerView(getContext(), l, mInitialColor, mDefaultColor));
30         setTitle(R.string.settings_bg_color_dialog);
31     }
32 }

Basically, this Dialog first declares a listener Interface that will be used to notify the new selected color to the class that opened it. The constructor then initializes a few variables:

  • mListener: the listener passed to the constructor
  • mKey: the key associated to the color (I needed this because I’m using the same Dialog for 2 Preferences – skin color and background color)
  • mInitialColor: the initial color
  • mDefaultColor: the default color

The onCreate method declares what to do when the OnColorChangedListener is called: notify the caller, and close (dismiss) the dialog. The method also sets the content view to our custom View, and the title.

ColorPickerView

This one is more complicated… And to be honest I made this a while ago and I won’t be able to explain every line of code :)

Here are the main methods used in the class. I’ve left some comments in the code which will hopefully help you understand how it works. If not, feel free to ask me in the comments of the post.

Constructor

The constructor initializes an array of colors used in the hue bar (the top bar):

01 ColorPickerView(Context c, OnColorChangedListener l, int color, int defaultColor) {
02     super(c);
03     mListener = l;
04     mDefaultColor = defaultColor;
05  
06     // Get the current hue from the current color and update the main color field
07     float[] hsv = new float[3];
08     Color.colorToHSV(color, hsv);
09     mCurrentHue = hsv[0];
10     updateMainColors();
11  
12     mCurrentColor = color;
13  
14     // Initialize the colors of the hue slider bar
15     int index = 0;
16     for (float i=0; i<256; i += 256/42// Red (#f00) to pink (#f0f)
17     {
18         mHueBarColors[index] = Color.rgb(2550, (int) i);
19         index++;
20     }
21     for (float i=0; i<256; i += 256/42// Pink (#f0f) to blue (#00f)
22     {
23         mHueBarColors[index] = Color.rgb(255-(int) i, 0255);
24         index++;
25     }
26     for (float i=0; i<256; i += 256/42// Blue (#00f) to light blue (#0ff)
27     {
28         mHueBarColors[index] = Color.rgb(0, (int) i, 255);
29         index++;
30     }
31     for (float i=0; i<256; i += 256/42// Light blue (#0ff) to green (#0f0)
32     {
33         mHueBarColors[index] = Color.rgb(0255255-(int) i);
34         index++;
35     }
36     for (float i=0; i<256; i += 256/42// Green (#0f0) to yellow (#ff0)
37     {
38         mHueBarColors[index] = Color.rgb((int) i, 2550);
39         index++;
40     }
41     for (float i=0; i<256; i += 256/42// Yellow (#ff0) to red (#f00)
42     {
43         mHueBarColors[index] = Color.rgb(255255-(int) i, 0);
44         index++;
45     }
46  
47     // Initializes the Paint that will draw the View
48     mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
49     mPaint.setTextAlign(Paint.Align.CENTER);
50     mPaint.setTextSize(12);
51 }

getCurrentMainColor

Returns the current selected color from the hue bar:

01 private int getCurrentMainColor()
02 {
03     int translatedHue = 255-(int)(mCurrentHue*255/360);
04     int index = 0;
05     for (float i=0; i<256; i += 256/42)
06     {
07         if (index == translatedHue)
08             return Color.rgb(2550, (int) i);
09         index++;
10     }
11     for (float i=0; i<256; i += 256/42)
12     {
13         if (index == translatedHue)
14             return Color.rgb(255-(int) i, 0255);
15         index++;
16     }
17     for (float i=0; i<256; i += 256/42)
18     {
19         if (index == translatedHue)
20             return Color.rgb(0, (int) i, 255);
21         index++;
22     }
23     for (float i=0; i<256; i += 256/42)
24     {
25         if (index == translatedHue)
26             return Color.rgb(0255255-(int) i);
27         index++;
28     }
29     for (float i=0; i<256; i += 256/42)
30     {
31         if (index == translatedHue)
32             return Color.rgb((int) i, 2550);
33         index++;
34     }
35     for (float i=0; i<256; i += 256/42)
36     {
37         if (index == translatedHue)
38             return Color.rgb(255255-(int) i, 0);
39         index++;
40     }
41     return Color.RED;
42 }

updateMainColors

Updates the main field colors depending on the current selected hue:

01 private void updateMainColors()
02 {
03     int mainColor = getCurrentMainColor();
04     int index = 0;
05     int[] topColors = new int[256];
06     for (int y=0; y<256; y++)
07     {
08         for (int x=0; x<256; x++)
09         {
10             if (y == 0)
11             {
12                 mMainColors[index] = Color.rgb(255-(255-Color.red(mainColor))*x/255255-(255-Color.green(mainColor))*x/255255-(255-Color.blue(mainColor))*x/255);
13                 topColors[x] = mMainColors[index];
14             }
15             else
16                 mMainColors[index] = Color.rgb((255-y)*Color.red(topColors[x])/255, (255-y)*Color.green(topColors[x])/255, (255-y)*Color.blue(topColors[x])/255);
17             index++;
18         }
19     }
20 }

onDraw

Displays the hue bar, the main field, the circle around the selected color, and the buttons to close the dialog with either the selected or the default color:

01 @Override
02 protected void onDraw(Canvas canvas) {
03     int translatedHue = 255-(int)(mCurrentHue*255/360);
04     // Display all the colors of the hue bar with lines
05     for (int x=0; x<256; x++)
06     {
07         // If this is not the current selected hue, display the actual color
08         if (translatedHue != x)
09         {
10             mPaint.setColor(mHueBarColors[x]);
11             mPaint.setStrokeWidth(1);
12         }
13         else // else display a slightly larger black line
14         {
15             mPaint.setColor(Color.BLACK);
16             mPaint.setStrokeWidth(3);
17         }
18         canvas.drawLine(x+100, x+1040, mPaint);
19     }
20  
21     // Display the main field colors using LinearGradient
22     for (int x=0; x<256; x++)
23     {
24         int[] colors = new int[2];
25         colors[0] = mMainColors[x];
26         colors[1] = Color.BLACK;
27         Shader shader = new LinearGradient(0500306, colors, null, Shader.TileMode.REPEAT);
28         mPaint.setShader(shader);
29         canvas.drawLine(x+1050, x+10306, mPaint);
30     }
31     mPaint.setShader(null);
32  
33     // Display the circle around the currently selected color in the main field
34     if (mCurrentX != 0 && mCurrentY != 0)
35     {
36         mPaint.setStyle(Paint.Style.STROKE);
37         mPaint.setColor(Color.BLACK);
38         canvas.drawCircle(mCurrentX, mCurrentY, 10, mPaint);
39     }
40  
41     // Draw a 'button' with the currently selected color
42     mPaint.setStyle(Paint.Style.FILL);
43     mPaint.setColor(mCurrentColor);
44     canvas.drawRect(10316138356, mPaint);
45  
46     // Set the text color according to the brightness of the color
47     if(Color.red(mCurrentColor)+Color.green(mCurrentColor)+Color.blue(mCurrentColor) <384)
48         mPaint.setColor(Color.WHITE);
49     else
50         mPaint.setColor(Color.BLACK);
51     canvas.drawText(getResources().getString(R.string.settings_bg_color_confirm),74340, mPaint);
52  
53     // Draw a 'button' with the default color
54     mPaint.setStyle(Paint.Style.FILL);
55     mPaint.setColor(mDefaultColor);
56     canvas.drawRect(138316266356, mPaint);
57  
58     // Set the text color according to the brightness of the color
59     if(Color.red(mDefaultColor)+Color.green(mDefaultColor)+Color.blue(mDefaultColor) <384)
60         mPaint.setColor(Color.WHITE);
61     else
62         mPaint.setColor(Color.BLACK);
63     canvas.drawText(getResources().getString(R.string.settings_default_color_confirm),202340, mPaint);
64 }

onTouchEvent

Deals with the touch events:

01 @Override
02 public boolean onTouchEvent(MotionEvent event) {
03     if (event.getAction() != MotionEvent.ACTION_DOWN) return true;
04     float x = event.getX();
05     float y = event.getY();
06  
07     // If the touch event is located in the hue bar
08     if (x > 10 && x < 266 && y > 0 && y < 40)
09     {
10         // Update the main field colors
11         mCurrentHue = (255-x)*360/255;
12         updateMainColors();
13  
14         // Update the current selected color
15         int transX = mCurrentX-10;
16         int transY = mCurrentY-60;
17         int index = 256*(transY-1)+transX;
18         if (index > 0 && index < mMainColors.length)
19             mCurrentColor = mMainColors[256*(transY-1)+transX];
20  
21         // Force the redraw of the dialog
22         invalidate();
23     }
24  
25     // If the touch event is located in the main field
26     if (x > 10 && x < 266 && y > 50 && y < 306)
27     {
28         mCurrentX = (int) x;
29         mCurrentY = (int) y;
30         int transX = mCurrentX-10;
31         int transY = mCurrentY-60;
32         int index = 256*(transY-1)+transX;
33         if (index > 0 && index < mMainColors.length)
34         {
35             // Update the current color
36             mCurrentColor = mMainColors[index];
37             // Force the redraw of the dialog
38             invalidate();
39         }
40     }
41  
42     // If the touch event is located in the left button, notify the listener with the current color
43     if (x > 10 && x < 138 && y > 316 && y < 356)
44         mListener.colorChanged("", mCurrentColor);
45  
46     // If the touch event is located in the right button, notify the listener with the default color
47     if (x > 138 && x < 266 && y > 316 && y < 356)
48         mListener.colorChanged("", mDefaultColor);
49  
50     return true;
51 }

How to use it

Here is an excerpt of the Shake Them All! settings:

01 public class MySettings extends PreferenceActivity implementsOnPreferenceClickListener, ColorPickerDialog.OnColorChangedListener {
02  
03     ................................................
04  
05     public boolean onPreferenceClick(Preference pref)
06     {
07         new ColorPickerDialog(thisthis, DROIDS_COLOR_KEY, mPrefs.getInt(DROIDS_COLOR_KEY, DROIDS_COLOR_DEFAULT), DROIDS_COLOR_DEFAULT).show();
08  
09         return true;
10     }
11  
12     public void colorChanged(String key, int color)
13     {
14         ((PreferenceScreen)this.findPreference(SETTINGS_KEY)).getEditor().putInt(key, color).commit();
15     }
16 }

Basically, your PreferenceActivity should implement the OnColorChangedListener interface. Then, you can open the color picker like any dialog by initializing it and calling its show method. Finally, in your listener, you can use your SharedPreference editor to change the value of the preference with the selected color, and commit your changes.

Download ColorPickerDialog.java

'Android > Tip&Tech' 카테고리의 다른 글

DB변경시 어댑터 자동변경 코드  (0) 2011.05.25
[펌]Android Custom CursorAdapter 사용법  (0) 2011.05.25
[펌]android trigger 참고  (0) 2011.05.24
listview 최적화하기 2/2  (0) 2011.05.19
listview 최적화하기  (0) 2011.05.19