1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | package org.chromium.ui; |
5 | |
6 | import android.content.Context; |
7 | import android.graphics.Canvas; |
8 | import android.graphics.Color; |
9 | import android.graphics.Paint; |
10 | import android.graphics.Rect; |
11 | import android.util.AttributeSet; |
12 | import android.view.MotionEvent; |
13 | import android.view.View; |
14 | |
15 | |
16 | /** |
17 | * Draws a grid of (predefined) colors and allows the user to choose one of |
18 | * those colors. |
19 | */ |
20 | public class ColorPickerSimple extends View { |
21 | private static final int ROW_COUNT = 2; |
22 | |
23 | private static final int COLUMN_COUNT = 4; |
24 | |
25 | private static final int GRID_CELL_COUNT = ROW_COUNT * COLUMN_COUNT; |
26 | |
27 | private static final int[] COLORS = { Color.RED, |
28 | Color.CYAN, |
29 | Color.BLUE, |
30 | Color.GREEN, |
31 | Color.MAGENTA, |
32 | Color.YELLOW, |
33 | Color.BLACK, |
34 | Color.WHITE |
35 | }; |
36 | |
37 | private Paint mBorderPaint; |
38 | |
39 | private Rect[] mBounds; |
40 | |
41 | private Paint[] mPaints; |
42 | |
43 | private OnColorChangedListener mOnColorTouchedListener; |
44 | |
45 | private int mLastTouchedXPosition; |
46 | |
47 | private int mLastTouchedYPosition; |
48 | |
49 | public ColorPickerSimple(Context context) { |
50 | super(context); |
51 | } |
52 | |
53 | public ColorPickerSimple(Context context, AttributeSet attrs) { |
54 | super(context, attrs); |
55 | } |
56 | |
57 | public ColorPickerSimple(Context context, AttributeSet attrs, int defStyle) { |
58 | super(context, attrs, defStyle); |
59 | } |
60 | |
61 | /** |
62 | * Initializes the listener and precalculates the grid and color positions. |
63 | * |
64 | * @param onColorChangedListener The listener that gets notified when the user touches |
65 | * a color. |
66 | */ |
67 | public void init(OnColorChangedListener onColorChangedListener) { |
68 | mOnColorTouchedListener = onColorChangedListener; |
69 | |
70 | // This will get calculated when the layout size is updated. |
71 | mBounds = null; |
72 | |
73 | mPaints = new Paint[GRID_CELL_COUNT]; |
74 | for (int i = 0; i < GRID_CELL_COUNT; ++i) { |
75 | Paint newPaint = new Paint(); |
76 | newPaint.setColor(COLORS[i]); |
77 | mPaints[i] = newPaint; |
78 | } |
79 | |
80 | mBorderPaint = new Paint(); |
81 | int borderColor = getContext().getResources().getColor(R.color.color_picker_border_color); |
82 | mBorderPaint.setColor(borderColor); |
83 | |
84 | // Responds to the user touching the grid and works out which color has been chosen as |
85 | // a result, depending on the X,Y coordinate. Note that we respond to the click event |
86 | // here, but the onClick() method doesn't provide us with the X,Y coordinates, so we |
87 | // track them in onTouchEvent() below. This way the grid reacts properly to touch events |
88 | // whereas if we put this onClick() code in onTouchEvent below then we get some strange |
89 | // interactions with the ScrollView in the parent ColorPickerDialog. |
90 | setOnClickListener(new OnClickListener() { |
91 | @Override |
92 | public void onClick(View v) { |
93 | if (mOnColorTouchedListener != null && getWidth() > 0 && getHeight() > 0) { |
94 | int column = mLastTouchedXPosition * COLUMN_COUNT / getWidth(); |
95 | int row = mLastTouchedYPosition * ROW_COUNT / getHeight(); |
96 | |
97 | int colorIndex = (row * COLUMN_COUNT) + column; |
98 | if (colorIndex >= 0 && colorIndex < COLORS.length) { |
99 | mOnColorTouchedListener.onColorChanged(COLORS[colorIndex]); |
100 | } |
101 | } |
102 | } |
103 | }); |
104 | } |
105 | |
106 | /** |
107 | * Draws the grid of colors, based on the rectangles calculated in onSizeChanged(). |
108 | * Also draws borders in between the colored rectangles. |
109 | * |
110 | * @param canvas The canvas the colors are drawn onto. |
111 | */ |
112 | @Override |
113 | public void onDraw(Canvas canvas) { |
114 | if (mBounds == null || mPaints == null) { |
115 | return; |
116 | } |
117 | |
118 | canvas.drawColor(Color.WHITE); |
119 | |
120 | // Draw the actual colored rectangles. |
121 | for (int i = 0; i < GRID_CELL_COUNT; ++i) { |
122 | canvas.drawRect(mBounds[i], mPaints[i]); |
123 | } |
124 | |
125 | // Draw 1px borders between the rows. |
126 | for (int i = 0; i < ROW_COUNT - 1; ++i) { |
127 | canvas.drawLine(0, |
128 | mBounds[i * COLUMN_COUNT].bottom + 1, |
129 | getWidth(), |
130 | mBounds[i * COLUMN_COUNT].bottom + 1, |
131 | mBorderPaint); |
132 | } |
133 | |
134 | // Draw 1px borders between the columns. |
135 | for (int j = 0; j < COLUMN_COUNT - 1; ++j) { |
136 | canvas.drawLine(mBounds[j].right + 1, |
137 | 0, |
138 | mBounds[j].right + 1, |
139 | getHeight(), |
140 | mBorderPaint); |
141 | } |
142 | } |
143 | |
144 | /** |
145 | * Stores the X,Y coordinates of the touch so that we can use them in the onClick() listener |
146 | * above to work out where the click was on the grid. |
147 | * |
148 | * @param event The MotionEvent the X,Y coordinates are retrieved from. |
149 | */ |
150 | @Override |
151 | public boolean onTouchEvent(MotionEvent event) { |
152 | if (event.getAction() == MotionEvent.ACTION_DOWN) { |
153 | mLastTouchedXPosition = (int) event.getX(); |
154 | mLastTouchedYPosition = (int) event.getY(); |
155 | } |
156 | return super.onTouchEvent(event); |
157 | } |
158 | |
159 | /** |
160 | * Recalculates the color grid with the new sizes. |
161 | */ |
162 | @Override |
163 | protected void onSizeChanged(int width, int height, int oldw, int oldh) { |
164 | calculateGrid(width, height); |
165 | } |
166 | |
167 | /** |
168 | * Calculates the sizes and positions of the cells in the grid, splitting |
169 | * them up as evenly as possible. Leaves 3 pixels between each cell so that |
170 | * we can draw a border between them as well, and leaves a pixel around the |
171 | * edge. |
172 | */ |
173 | private void calculateGrid(final int width, final int height) { |
174 | mBounds = new Rect[GRID_CELL_COUNT]; |
175 | |
176 | for (int i = 0; i < ROW_COUNT; ++i) { |
177 | for (int j = 0; j < COLUMN_COUNT; ++j) { |
178 | int left = j * (width + 1) / COLUMN_COUNT + 1; |
179 | int right = (j + 1) * (width + 1) / COLUMN_COUNT - 2; |
180 | |
181 | int top = i * (height + 1) / ROW_COUNT + 1; |
182 | int bottom = (i + 1) * (height + 1) / ROW_COUNT - 2; |
183 | |
184 | Rect rect = new Rect(left, top, right, bottom); |
185 | mBounds[(i * COLUMN_COUNT) + j] = rect; |
186 | } |
187 | } |
188 | } |
189 | } |