• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.util;
18 
19 import com.android.frameworks.coretests.R;
20 
21 import android.view.View;
22 import android.view.KeyEvent;
23 import android.content.Context;
24 import android.content.res.TypedArray;
25 import android.graphics.Paint;
26 import android.graphics.Canvas;
27 import android.graphics.Rect;
28 import android.graphics.Color;
29 import android.util.AttributeSet;
30 
31 
32 
33 /**
34  * A view that has a known number of selectable rows, and maintains a notion of which
35  * row is selected. The rows take up the
36  * entire width of the view.  The height of the view is divided evenly among
37  * the rows.
38  *
39  * Note: If the height of the view does not divide exactly to the number of rows,
40  *       the last row's height is inflated with the remainder. For example, if the
41  *       view height is 22 and there are two rows, the height of the first row is
42  *       10 and the second 22.
43  *
44  * Notice what this view does to be a good citizen w.r.t its internal selection:
45  * 1) calls {@link View#requestRectangleOnScreen} each time the selection changes due to
46  *    internal navigation.
47  * 2) implements {@link View#getFocusedRect} by filling in the rectangle of the currently
48  *    selected row
49  * 3) overrides {@link View#onFocusChanged} and sets selection appropriately according to
50  *    the previously focused rectangle.
51  */
52 public class InternalSelectionView extends View {
53 
54     private Paint mPainter = new Paint();
55     private Paint mTextPaint = new Paint();
56     private Rect mTempRect = new Rect();
57 
58     private int mNumRows = 5;
59     private int mSelectedRow = 0;
60     private final int mEstimatedPixelHeight = 10;
61 
62     private Integer mDesiredHeight = null;
63     private String mLabel = null;
64 
InternalSelectionView(Context context, int numRows, String label)65     public InternalSelectionView(Context context, int numRows, String label) {
66         super(context);
67         mNumRows = numRows;
68         mLabel = label;
69         init();
70     }
71 
InternalSelectionView(Context context, AttributeSet attrs)72     public InternalSelectionView(Context context, AttributeSet attrs) {
73         super(context, attrs);
74         TypedArray a =
75                 context.obtainStyledAttributes(
76                         attrs, R.styleable.SelectableRowView);
77         mNumRows = a.getInt(R.styleable.SelectableRowView_numRows, 5);
78         init();
79     }
80 
init()81     private void init() {
82         setFocusable(true);
83         mTextPaint.setAntiAlias(true);
84         mTextPaint.setTextSize(10);
85         mTextPaint.setColor(Color.WHITE);
86     }
87 
getNumRows()88     public int getNumRows() {
89         return mNumRows;
90     }
91 
getSelectedRow()92     public int getSelectedRow() {
93         return mSelectedRow;
94     }
95 
setDesiredHeight(int desiredHeight)96     public void setDesiredHeight(int desiredHeight) {
97         mDesiredHeight = desiredHeight;
98     }
99 
getLabel()100     public String getLabel() {
101         return mLabel;
102     }
103 
104     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)105     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
106         setMeasuredDimension(
107             measureWidth(widthMeasureSpec),
108             measureHeight(heightMeasureSpec));
109     }
110 
measureWidth(int measureSpec)111     private int measureWidth(int measureSpec) {
112         int specMode = MeasureSpec.getMode(measureSpec);
113         int specSize = MeasureSpec.getSize(measureSpec);
114 
115         int desiredWidth = 300 + mPaddingLeft + mPaddingRight;
116         if (specMode == MeasureSpec.EXACTLY) {
117             // We were told how big to be
118             return specSize;
119         } else if (specMode == MeasureSpec.AT_MOST) {
120             return desiredWidth < specSize ? desiredWidth : specSize;
121         } else {
122             return desiredWidth;
123         }
124     }
125 
measureHeight(int measureSpec)126     private int measureHeight(int measureSpec) {
127         int specMode = MeasureSpec.getMode(measureSpec);
128         int specSize = MeasureSpec.getSize(measureSpec);
129 
130         int desiredHeight = mDesiredHeight != null ?
131                 mDesiredHeight :
132                 mNumRows * mEstimatedPixelHeight + mPaddingTop + mPaddingBottom;
133         if (specMode == MeasureSpec.EXACTLY) {
134             // We were told how big to be
135             return specSize;
136         } else if (specMode == MeasureSpec.AT_MOST) {
137             return desiredHeight < specSize ? desiredHeight : specSize;
138         } else {
139             return desiredHeight;
140         }
141     }
142 
143 
144     @Override
onDraw(Canvas canvas)145     protected void onDraw(Canvas canvas) {
146         int rectTop = mPaddingTop;
147         int rectLeft = mPaddingLeft;
148         int rectRight = getWidth() - mPaddingRight;
149         for (int i = 0; i < mNumRows; i++) {
150 
151             mPainter.setColor(Color.BLACK);
152             mPainter.setAlpha(0x20);
153 
154             int rowHeight = getRowHeight(i);
155 
156             // draw background rect
157             mTempRect.set(rectLeft, rectTop, rectRight, rectTop + rowHeight);
158             canvas.drawRect(mTempRect, mPainter);
159 
160             // draw forground rect
161             if (i == mSelectedRow && hasFocus()) {
162                 mPainter.setColor(Color.RED);
163                 mPainter.setAlpha(0xF0);
164                 mTextPaint.setAlpha(0xFF);
165             } else {
166                 mPainter.setColor(Color.BLACK);
167                 mPainter.setAlpha(0x40);
168                 mTextPaint.setAlpha(0xF0);
169             }
170             mTempRect.set(rectLeft + 2, rectTop + 2,
171                     rectRight - 2, rectTop + rowHeight - 2);
172             canvas.drawRect(mTempRect, mPainter);
173 
174             // draw text to help when visually inspecting
175             canvas.drawText(
176                     Integer.toString(i),
177                     rectLeft + 2,
178                     rectTop + 2 - (int) mTextPaint.ascent(),
179                     mTextPaint);
180 
181             rectTop += rowHeight;
182         }
183     }
184 
getRowHeight(int row)185     private int getRowHeight(int row) {
186         final int availableHeight = getHeight() - mPaddingTop - mPaddingBottom;
187         final int desiredRowHeight = availableHeight / mNumRows;
188         if (row < mNumRows - 1) {
189             return desiredRowHeight;
190         } else {
191             final int residualHeight = availableHeight % mNumRows;
192             return desiredRowHeight + residualHeight;
193         }
194     }
195 
getRectForRow(Rect rect, int row)196     public void getRectForRow(Rect rect, int row) {
197         final int rowHeight = getRowHeight(row);
198         final int top = mPaddingTop + row * rowHeight;
199         rect.set(mPaddingLeft,
200                 top,
201                 getWidth() - mPaddingRight,
202                 top + rowHeight);
203     }
204 
205 
ensureRectVisible()206     void ensureRectVisible() {
207         getRectForRow(mTempRect, mSelectedRow);
208         requestRectangleOnScreen(mTempRect);
209     }
210 
211     @Override
onKeyDown(int keyCode, KeyEvent event)212     public boolean onKeyDown(int keyCode, KeyEvent event) {
213         switch(event.getKeyCode()) {
214             case KeyEvent.KEYCODE_DPAD_UP:
215                 if (mSelectedRow > 0) {
216                     mSelectedRow--;
217                     invalidate();
218                     ensureRectVisible();
219                     return true;
220                 }
221                 break;
222             case KeyEvent.KEYCODE_DPAD_DOWN:
223                 if (mSelectedRow < (mNumRows - 1)) {
224                     mSelectedRow++;
225                     invalidate();
226                     ensureRectVisible();
227                     return true;
228                 }
229                 break;
230         }
231         return false;
232     }
233 
234 
235     @Override
getFocusedRect(Rect r)236     public void getFocusedRect(Rect r) {
237         getRectForRow(r, mSelectedRow);
238     }
239 
240     @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)241     protected void onFocusChanged(boolean focused, int direction,
242             Rect previouslyFocusedRect) {
243         super.onFocusChanged(focused, direction, previouslyFocusedRect);
244 
245         if (focused) {
246             switch (direction) {
247                 case View.FOCUS_DOWN:
248                     mSelectedRow = 0;
249                     break;
250                 case View.FOCUS_UP:
251                     mSelectedRow = mNumRows - 1;
252                     break;
253                 case View.FOCUS_LEFT:  // fall through
254                 case View.FOCUS_RIGHT:
255                     // set the row that is closest to the rect
256                     if (previouslyFocusedRect != null) {
257                         int y = previouslyFocusedRect.top
258                                 + (previouslyFocusedRect.height() / 2);
259                         int yPerRow = getHeight() / mNumRows;
260                         mSelectedRow = y / yPerRow;
261                     } else {
262                         mSelectedRow = 0;
263                     }
264                     break;
265                 default:
266                     // can't gleam any useful information about what internal
267                     // selection should be...
268                     return;
269             }
270             invalidate();
271         }
272     }
273 
274     @Override
toString()275     public String toString() {
276         if (mLabel != null) {
277             return mLabel;
278         }
279         return super.toString();
280     }
281 }
282