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