上一篇:http://www.bkjia.com/kf/201208/146799.html
使用方向鍵或是traceball 移動UI焦點時,有一些UI控制項,如Listview ,GridView 內部可以帶有子UI (如清單項目,Cell項),當Focus移動到這類UI控制項後,再使用使用方向鍵或是traceball 可以在Listview 或是 GridView 內部移動當前選中的清單項目或是Cell項等。
本例介紹了一個自訂UI控制項InternalSelectionView ,自訂UI控制項的步驟可以參見Android ApiDemos樣本解析(109):Views->Custom
InternalSelectionView 可以顯示一個矩形列表,矩形的寬度為View的寬度,允許自訂欄表的行數,矩形的高度為View的高度平分為列表的行數。預設行數為5.
其onDraw 顯示 矩形,並以高亮(紅色)顯示當前選中的矩形。否則以黑色顯示.
[java]
@Override
protected void onDraw(Canvas canvas) {
...
// draw forground rect
if (i == mSelectedRow && hasFocus()) {
mPainter.setColor(Color.RED);
mPainter.setAlpha(0xF0);
mTextPaint.setAlpha(0xFF);
} else {
mPainter.setColor(Color.GREEN);
mPainter.setAlpha(0x40);
mTextPaint.setAlpha(0xF0);
}
...
}
@Override
protected void onDraw(Canvas canvas) {
...
// draw forground rect
if (i == mSelectedRow && hasFocus()) {
mPainter.setColor(Color.RED);
mPainter.setAlpha(0xF0);
mTextPaint.setAlpha(0xFF);
} else {
mPainter.setColor(Color.GREEN);
mPainter.setAlpha(0x40);
mTextPaint.setAlpha(0xF0);
}
...
}
註:黑色背景矩形顯示看不大清楚,這裡改為綠色。
和UI焦點(Focus)相關的幾個方法如下:
1. 處理方向鍵“上”,“下” ,InternalSelectionView 顯示了一個縱向矩形列表,可以使用“上”,“下”鍵移動InternalSelectionView 當前選中的矩形。
[java
@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 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;
}
2. 重載getFocusedRect 方法,設定被選中地區矩形框大小, 預設情況(對應沒有內部選擇功能的UI控制項) getFocusedRect 和 getDrawingRect大小相同,也就是和View大小相同。當將UI焦點從本控制項移動到下一個UI控制項時,Android系統將根據本UI控制項的Focused Rect位置根據UI焦點移動的方向找到和這個Focused Rect距離最近的下一個UI控制項。 對應支援內部選擇的View(如本例或ListView)getFocusedRect 應正確設定選中地區矩形的大小:
[java]
@Override
public void getFocusedRect(Rect r) {
getRectForRow(r, mSelectedRow);
}
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);
}
@Override
public void getFocusedRect(Rect r) {
getRectForRow(r, mSelectedRow);
}
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);
}
本例將Focused Rect 設定成當前選中的矩形的大小。
3. 重載 onFocusChanged ,這個方法在當前UI View Focus狀態發生變化時調用,狀態為取得焦點和失去焦點。 重載時記住要調用基類的方法以保證標準Focus移動行為。
[java]
@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
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();
}
}
函數參數:
focused: 為true時表示此View擷取焦點,否則為失去焦點。
direction: 當前焦點移動的方向,可以為上,下,左,右,前向或是後向。
previouslyFocusedRect: 不為null時表示前一個擷取焦點UI控制項(View整體)的矩形地區大小。
對於本例來說,只有FOCUS_LEFT 和 FOCUS_RIGHT 會被執行到。 InternalSelectionFocus 從左至右並排顯示3個InternalSelectionView的樣本。 “左”,“右”鍵可以在左,中,右三個InternalSelectionView 來回移動UI焦點。 “上”,“下”就可以在當前擷取UI焦點的一個InternalSelectionView對象上下選擇一個矩形。
看看“左”,“右”鍵移動UI焦點時,下一個擷取焦點的InternalSelectionView 中哪個矩形會被選中:
[java]
if (previouslyFocusedRect != null) {
int y = previouslyFocusedRect.top
+ (previouslyFocusedRect.height() / 2);
int yPerRow = getHeight() / mNumRows;
mSelectedRow = y / yPerRow;
}
if (previouslyFocusedRect != null) {
int y = previouslyFocusedRect.top
+ (previouslyFocusedRect.height() / 2);
int yPerRow = getHeight() / mNumRows;
mSelectedRow = y / yPerRow;
}
由於三個View的頂部和底部對齊 ,也就是previouslyFocusedRect 的top 和bottom 對於當前選中哪個View都是一樣的,因此算出來的mSelectuedRow 都一樣,對應本例都為3. 因此使用”左”,”右” 移動UI焦點時,下一個擷取焦點的InternalSelectionView 總是首先選中序號為3的矩形。
作者:mapdigit