| 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 | } |