Mega Code Archive

 
Categories / Android / UI
 

Using three adjacent InternalSelectionView that report interesting rects when giving up focus

/*  * Copyright (C) 2007 The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ package com.example.android.apis.view; import android.app.Activity; import android.os.Bundle; import android.view.ViewGroup; import android.widget.LinearLayout; /**  * {@link android.view.View#requestFocus(int, android.graphics.Rect)}  * and  * {@link android.view.View#onFocusChanged(boolean, int, android.graphics.Rect)}  * work together to give a newly focused item a hint about the most interesting  * rectangle of the previously focused view.  The view taking focus can use this  * to set an internal selection more appropriate using this rect.  *  * This Activity excercises that behavior using three adjacent {@link InternalSelectionView}  * that report interesting rects when giving up focus, and use interesting rects  * when taking focus to best select the internal row to show as selected.  *  * Were {@link InternalSelectionView} not to override {@link android.view.View#getFocusedRect}, or  * {@link android.view.View#onFocusChanged(boolean, int, android.graphics.Rect)}, the focus would  * jump to some default internal selection (the top) and not allow for the smooth handoff.  */ public class InternalSelectionFocus extends Activity {     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         final LinearLayout layout = new LinearLayout(this);         layout.setOrientation(LinearLayout.HORIZONTAL);         layout.setLayoutParams(new ViewGroup.LayoutParams(                 ViewGroup.LayoutParams.MATCH_PARENT,                 ViewGroup.LayoutParams.MATCH_PARENT));         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,                 ViewGroup.LayoutParams.MATCH_PARENT, 1);         final InternalSelectionView leftColumn = new InternalSelectionView(this, 5, "left column");         leftColumn.setLayoutParams(params);         leftColumn.setPadding(10, 10, 10, 10);         layout.addView(leftColumn);         final InternalSelectionView middleColumn = new InternalSelectionView(this, 5, "middle column");         middleColumn.setLayoutParams(params);         middleColumn.setPadding(10, 10, 10, 10);         layout.addView(middleColumn);         final InternalSelectionView rightColumn = new InternalSelectionView(this, 5, "right column");         rightColumn.setLayoutParams(params);         rightColumn.setPadding(10, 10, 10, 10);         layout.addView(rightColumn);         setContentView(layout);     } } // /*  * Copyright (C) 2007 The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ package com.example.android.apis.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.View; /**  * A view that has a known number of selectable rows, and maintains a notion of which  * row is selected. The rows take up the  * entire width of the view.  The height of the view is divided evenly among  * the rows.  *  * Notice what this view does to be a good citizen w.r.t its internal selection:  * 1) calls {@link View#requestRectangleOnScreen} each time the selection changes due to  *    internal navigation.  * 2) overrides {@link View#getFocusedRect} by filling in the rectangle of the currently  *    selected row  * 3) overrides {@link View#onFocusChanged} and sets selection appropriately according to  *    the previously focused rectangle.  */ public class InternalSelectionView extends View {     private Paint mPainter = new Paint();     private Paint mTextPaint = new Paint();     private Rect mTempRect = new Rect();     private int mNumRows = 5;     private int mSelectedRow = 0;     private final int mEstimatedPixelHeight = 10;     private Integer mDesiredHeight = null;     private String mLabel = null;     public InternalSelectionView(Context context, int numRows) {         this(context, numRows, "");     }          public InternalSelectionView(Context context, int numRows, String label) {         super(context);         mNumRows = numRows;         mLabel = label;         init();     }     public InternalSelectionView(Context context, AttributeSet attrs) {         super(context, attrs);         init();     }     private void init() {         setFocusable(true);         mTextPaint.setAntiAlias(true);         mTextPaint.setTextSize(10);         mTextPaint.setColor(Color.WHITE);     }     public int getNumRows() {         return mNumRows;     }     public int getSelectedRow() {         return mSelectedRow;     }     public void setDesiredHeight(int desiredHeight) {         mDesiredHeight = desiredHeight;     }     public String getLabel() {         return mLabel;     }     @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         setMeasuredDimension(             measureWidth(widthMeasureSpec),             measureHeight(heightMeasureSpec));     }     private int measureWidth(int measureSpec) {         int specMode = MeasureSpec.getMode(measureSpec);         int specSize = MeasureSpec.getSize(measureSpec);         int desiredWidth = 300 + getPaddingLeft() + getPaddingRight();         if (specMode == MeasureSpec.EXACTLY) {             // We were told how big to be             return specSize;         } else if (specMode == MeasureSpec.AT_MOST) {             return desiredWidth < specSize ? desiredWidth : specSize;         } else {             return desiredWidth;         }     }     private int measureHeight(int measureSpec) {         int specMode = MeasureSpec.getMode(measureSpec);         int specSize = MeasureSpec.getSize(measureSpec);         int desiredHeight = mDesiredHeight != null ?                 mDesiredHeight :                 mNumRows * mEstimatedPixelHeight + getPaddingTop() + getPaddingBottom();         if (specMode == MeasureSpec.EXACTLY) {             // We were told how big to be             return specSize;         } else if (specMode == MeasureSpec.AT_MOST) {             return desiredHeight < specSize ? desiredHeight : specSize;         } else {             return desiredHeight;         }     }     @Override     protected void onDraw(Canvas canvas) {         int rowHeight = getRowHeight();         int rectTop = getPaddingTop();         int rectLeft = getPaddingLeft();         int rectRight = getWidth() - getPaddingRight();         for (int i = 0; i < mNumRows; i++) {             mPainter.setColor(Color.BLACK);             mPainter.setAlpha(0x20);             // draw background rect             mTempRect.set(rectLeft, rectTop, rectRight, rectTop + rowHeight);             canvas.drawRect(mTempRect, mPainter);             // draw forground rect             if (i == mSelectedRow && hasFocus()) {                 mPainter.setColor(Color.RED);                 mPainter.setAlpha(0xF0);                 mTextPaint.setAlpha(0xFF);             } else {                 mPainter.setColor(Color.BLACK);                 mPainter.setAlpha(0x40);                 mTextPaint.setAlpha(0xF0);             }             mTempRect.set(rectLeft + 2, rectTop + 2,                     rectRight - 2, rectTop + rowHeight - 2);             canvas.drawRect(mTempRect, mPainter);             // draw text to help when visually inspecting             canvas.drawText(                     Integer.toString(i),                     rectLeft + 2,                     rectTop + 2 - (int) mTextPaint.ascent(),                     mTextPaint);             rectTop += rowHeight;         }     }     private int getRowHeight() {         return (getHeight() - getPaddingTop() - getPaddingBottom()) / mNumRows;     }     public void getRectForRow(Rect rect, int row) {         final int rowHeight = getRowHeight();         final int top = getPaddingTop() + row * rowHeight;         rect.set(getPaddingLeft(),                 top,                 getWidth() - getPaddingRight(),                 top + rowHeight);     }     void ensureRectVisible() {         getRectForRow(mTempRect, mSelectedRow);         requestRectangleOnScreen(mTempRect);     }     /* (non-Javadoc)     * @see android.view.KeyEvent.Callback#onKeyDown(int, android.view.KeyEvent)     */     @Override     public boolean onKeyDown(int keyCode, KeyEvent event) {         switch(event.getKeyCode()) {             case KeyEvent.KEYCODE_DPAD_UP:                 if (mSelectedRow > 0) {                     mSelectedRow--;                     invalidate();                     ensureRectVisible();                     return true;                 }                 break;             case KeyEvent.KEYCODE_DPAD_DOWN:                 if (mSelectedRow < (mNumRows - 1)) {                     mSelectedRow++;                     invalidate();                     ensureRectVisible();                     return true;                 }                 break;         }         return false;     }     @Override     public void getFocusedRect(Rect r) {         getRectForRow(r, mSelectedRow);     }     @Override     protected void onFocusChanged(boolean focused, int direction,             Rect previouslyFocusedRect) {         super.onFocusChanged(focused, direction, previouslyFocusedRect);         if (focused) {             switch (direction) {                 case View.FOCUS_DOWN:                     mSelectedRow = 0;                     break;                 case View.FOCUS_UP:                     mSelectedRow = mNumRows - 1;                     break;                 case View.FOCUS_LEFT:  // fall through                 case View.FOCUS_RIGHT:                     // set the row that is closest to the rect                     if (previouslyFocusedRect != null) {                         int y = previouslyFocusedRect.top                                 + (previouslyFocusedRect.height() / 2);                         int yPerRow = getHeight() / mNumRows;                         mSelectedRow = y / yPerRow;                     } else {                         mSelectedRow = 0;                     }                     break;                 default:                     // can't gleam any useful information about what internal                     // selection should be...                     return;             }             invalidate();         }     }     @Override     public String toString() {         if (mLabel != null) {             return mLabel;         }         return super.toString();     } }