• 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 com.example.android.apis.view;
18  
19  import android.content.Context;
20  import android.graphics.Canvas;
21  import android.graphics.Color;
22  import android.graphics.Paint;
23  import android.graphics.Rect;
24  import android.util.AttributeSet;
25  import android.view.KeyEvent;
26  import android.view.View;
27  
28  
29  
30  /**
31   * A view that has a known number of selectable rows, and maintains a notion of which
32   * row is selected. The rows take up the
33   * entire width of the view.  The height of the view is divided evenly among
34   * the rows.
35   *
36   * Notice what this view does to be a good citizen w.r.t its internal selection:
37   * 1) calls {@link View#requestRectangleOnScreen} each time the selection changes due to
38   *    internal navigation.
39   * 2) overrides {@link View#getFocusedRect} by filling in the rectangle of the currently
40   *    selected row
41   * 3) overrides {@link View#onFocusChanged} and sets selection appropriately according to
42   *    the previously focused rectangle.
43   */
44  public class InternalSelectionView extends View {
45  
46      private Paint mPainter = new Paint();
47      private Paint mTextPaint = new Paint();
48      private Rect mTempRect = new Rect();
49  
50      private int mNumRows = 5;
51      private int mSelectedRow = 0;
52      private final int mEstimatedPixelHeight = 10;
53  
54      private Integer mDesiredHeight = null;
55      private String mLabel = null;
56  
57  
InternalSelectionView(Context context, int numRows)58      public InternalSelectionView(Context context, int numRows) {
59          this(context, numRows, "");
60      }
61  
InternalSelectionView(Context context, int numRows, String label)62      public InternalSelectionView(Context context, int numRows, String label) {
63          super(context);
64          mNumRows = numRows;
65          mLabel = label;
66          init();
67      }
68  
InternalSelectionView(Context context, AttributeSet attrs)69      public InternalSelectionView(Context context, AttributeSet attrs) {
70          super(context, attrs);
71          init();
72      }
73  
init()74      private void init() {
75          setFocusable(true);
76          mTextPaint.setAntiAlias(true);
77          mTextPaint.setTextSize(10);
78          mTextPaint.setColor(Color.WHITE);
79      }
80  
getNumRows()81      public int getNumRows() {
82          return mNumRows;
83      }
84  
getSelectedRow()85      public int getSelectedRow() {
86          return mSelectedRow;
87      }
88  
setDesiredHeight(int desiredHeight)89      public void setDesiredHeight(int desiredHeight) {
90          mDesiredHeight = desiredHeight;
91      }
92  
getLabel()93      public String getLabel() {
94          return mLabel;
95      }
96  
97      @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)98      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
99          setMeasuredDimension(
100              measureWidth(widthMeasureSpec),
101              measureHeight(heightMeasureSpec));
102      }
103  
measureWidth(int measureSpec)104      private int measureWidth(int measureSpec) {
105          int specMode = MeasureSpec.getMode(measureSpec);
106          int specSize = MeasureSpec.getSize(measureSpec);
107  
108          int desiredWidth = 300 + getPaddingLeft() + getPaddingRight();
109          if (specMode == MeasureSpec.EXACTLY) {
110              // We were told how big to be
111              return specSize;
112          } else if (specMode == MeasureSpec.AT_MOST) {
113              return desiredWidth < specSize ? desiredWidth : specSize;
114          } else {
115              return desiredWidth;
116          }
117      }
118  
measureHeight(int measureSpec)119      private int measureHeight(int measureSpec) {
120          int specMode = MeasureSpec.getMode(measureSpec);
121          int specSize = MeasureSpec.getSize(measureSpec);
122  
123          int desiredHeight = mDesiredHeight != null ?
124                  mDesiredHeight :
125                  mNumRows * mEstimatedPixelHeight + getPaddingTop() + getPaddingBottom();
126          if (specMode == MeasureSpec.EXACTLY) {
127              // We were told how big to be
128              return specSize;
129          } else if (specMode == MeasureSpec.AT_MOST) {
130              return desiredHeight < specSize ? desiredHeight : specSize;
131          } else {
132              return desiredHeight;
133          }
134      }
135  
136  
137      @Override
onDraw(Canvas canvas)138      protected void onDraw(Canvas canvas) {
139  
140          int rowHeight = getRowHeight();
141  
142          int rectTop = getPaddingTop();
143          int rectLeft = getPaddingLeft();
144          int rectRight = getWidth() - getPaddingRight();
145          for (int i = 0; i < mNumRows; i++) {
146  
147              mPainter.setColor(Color.BLACK);
148              mPainter.setAlpha(0x20);
149  
150              // draw background rect
151              mTempRect.set(rectLeft, rectTop, rectRight, rectTop + rowHeight);
152              canvas.drawRect(mTempRect, mPainter);
153  
154              // draw forground rect
155              if (i == mSelectedRow && hasFocus()) {
156                  mPainter.setColor(Color.RED);
157                  mPainter.setAlpha(0xF0);
158                  mTextPaint.setAlpha(0xFF);
159              } else {
160                  mPainter.setColor(Color.BLACK);
161                  mPainter.setAlpha(0x40);
162                  mTextPaint.setAlpha(0xF0);
163              }
164              mTempRect.set(rectLeft + 2, rectTop + 2,
165                      rectRight - 2, rectTop + rowHeight - 2);
166              canvas.drawRect(mTempRect, mPainter);
167  
168              // draw text to help when visually inspecting
169              canvas.drawText(
170                      Integer.toString(i),
171                      rectLeft + 2,
172                      rectTop + 2 - (int) mTextPaint.ascent(),
173                      mTextPaint);
174  
175              rectTop += rowHeight;
176          }
177      }
178  
getRowHeight()179      private int getRowHeight() {
180          return (getHeight() - getPaddingTop() - getPaddingBottom()) / mNumRows;
181      }
182  
getRectForRow(Rect rect, int row)183      public void getRectForRow(Rect rect, int row) {
184          final int rowHeight = getRowHeight();
185          final int top = getPaddingTop() + row * rowHeight;
186          rect.set(getPaddingLeft(),
187                  top,
188                  getWidth() - getPaddingRight(),
189                  top + rowHeight);
190      }
191  
192  
ensureRectVisible()193      void ensureRectVisible() {
194          getRectForRow(mTempRect, mSelectedRow);
195          requestRectangleOnScreen(mTempRect);
196      }
197  
198  
199      /* (non-Javadoc)
200      * @see android.view.KeyEvent.Callback#onKeyDown(int, android.view.KeyEvent)
201      */
202      @Override
onKeyDown(int keyCode, KeyEvent event)203      public boolean onKeyDown(int keyCode, KeyEvent event) {
204          switch(event.getKeyCode()) {
205              case KeyEvent.KEYCODE_DPAD_UP:
206                  if (mSelectedRow > 0) {
207                      mSelectedRow--;
208                      invalidate();
209                      ensureRectVisible();
210                      return true;
211                  }
212                  break;
213              case KeyEvent.KEYCODE_DPAD_DOWN:
214                  if (mSelectedRow < (mNumRows - 1)) {
215                      mSelectedRow++;
216                      invalidate();
217                      ensureRectVisible();
218                      return true;
219                  }
220                  break;
221          }
222          return false;
223      }
224  
225  
226      @Override
getFocusedRect(Rect r)227      public void getFocusedRect(Rect r) {
228          getRectForRow(r, mSelectedRow);
229      }
230  
231      @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)232      protected void onFocusChanged(boolean focused, int direction,
233              Rect previouslyFocusedRect) {
234          super.onFocusChanged(focused, direction, previouslyFocusedRect);
235  
236          if (focused) {
237              switch (direction) {
238                  case View.FOCUS_DOWN:
239                      mSelectedRow = 0;
240                      break;
241                  case View.FOCUS_UP:
242                      mSelectedRow = mNumRows - 1;
243                      break;
244                  case View.FOCUS_LEFT:  // fall through
245                  case View.FOCUS_RIGHT:
246                      // set the row that is closest to the rect
247                      if (previouslyFocusedRect != null) {
248                          int y = previouslyFocusedRect.top
249                                  + (previouslyFocusedRect.height() / 2);
250                          int yPerRow = getHeight() / mNumRows;
251                          mSelectedRow = y / yPerRow;
252                      } else {
253                          mSelectedRow = 0;
254                      }
255                      break;
256                  default:
257                      // can't gleam any useful information about what internal
258                      // selection should be...
259                      return;
260              }
261              invalidate();
262          }
263      }
264  
265      @Override
toString()266      public String toString() {
267          if (mLabel != null) {
268              return mLabel;
269          }
270          return super.toString();
271      }
272  }
273