android機頂盒簡單案例

最近,對Andoid中APK作了一些修改,讓其支持遙控器的支持,這把人弄得焦頭爛額,好不鬱悶,這裏特別記錄下思路,留給有須要的同志。 java

        其實Android自己是作了對遙控器的上下左右按鍵的焦點移動控制,以及對Enter鍵的響應,如今知道的緣由就是1:View的子類在派生時對其動做作了從寫,致使其焦點控制失效;2:不明緣由致使焦點控制失敗。該文只是從功能上總結了本身在APK的IR修改中摸索出的一些方法,來達到支持IR控制的效果。 android

    思路一:android提供了一些焦點相關的屬性,在現有的框架層下經過設置View的屬性來得到焦點 canvas

android:focusable:設置一個控件可否得到焦點  
android:background:設置在做爲背景的drawable  
android:nextFocusDown:定義下一個得到焦點的控件當按下鍵時  
android:nextFocusUp:定義下一個得到焦點的控件當按上鍵時  
android:nextFocusLeft:定義下一個得到焦點的控件當按左鍵時  
android:nextFocusRight:定義下一個得到焦點的控件當按右鍵時  
<requestFocus/>:強制設置一個焦點到指定的view或它的一個子類,前提是android:focusable爲true可以得到焦點

實例一: 數組

[java]

實例二: app

[java]
<ImageButton  
        android:id="@+id/close"  
        android:focusable="true"  
        android:nextFocusDown="@+id/url"  
        android:background="?android:attr/selectableItemBackground"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:contentDescription="@string/accessibility_button_closetab"  
        android:src="@drawable/ic_tab_close" />
  
<EditText android:id="@+id/user_dictionary_add_word_text"  
      ……  
      ……  
      android:imeOptions="flagNoFullscreen">  
      <requestFocus />  
</EditText>

實踐經驗:若是XML設置無效的話,嘗試下在代碼中XXX.requestFocus()顯式設置 框架

         思路二:本身管理焦點的移動,本身進行高亮的重繪,本身進行enter鍵的響應,總之:一切都靠你本身 ide

Step 1:焦點的移動,首先你就要作到IR按鍵的捕獲與響應 函數

         單獨Activity的話,下面的兩組函數都可以捕獲按鍵消息 url

public boolean dispatchKeyEvent (KeyEvent event)   
public boolean onKeyDown (int keyCode, KeyEvent event)  
public boolean onKeyUp(int keyCode, KeyEvent event)  
  
public boolean dispatchKeyEventPreIme (KeyEvent event)  
public boolean onKeyPreIme (int keyCode, KeyEvent event)

通常來講重寫onKeyDownonKeyUp就能夠了,但須要在輸入法以前作一些動做,便須要重寫onKeyPreIme,可是在某些時候在事件分發中會被分發到其餘的view的事件處理,致使不會調用到該ActivityonKeyDownonKeyUp,這個時候就須要在分發函數dispatchKeyEventdispatchKeyEventPreIme作一些動做了;要顯式調用某個view的事件處理函數或是自定義的事件處理函數,也能夠在以上的幾個函數中進行直接調用,至關於繞開系統本身來進行一些事件的分發(只是解決方案,作法並不規範);以上這一系列函數的返回值是boolean型,若是你已經處理了一些消息,記得返回true來通知系統已經作了處理,交由系統處理的消息直接父類super的同名函數進行處理;有些按鍵系統對其所作的關鍵響應有的是在onKeyUp或者onKeyDown中,所以在你攔截的時候需在恰當的函數裏進行攔截。 spa

實例一:


  1. @Override  
    public boolean onKeyUp(int keyCode, KeyEvent event){  
    ActivityState topState = getStateManager().getTopState();  
        if(topState == null){  
                return super.onKeyUp(keyCode, event);  
        }  
      
        /*顯式調用ActivityState的onKeyUp事件處理,這個onKeyUp能夠是重寫View的消息處理,不是View 
        的話也能夠自定義這個函數來進行事件處理,固然在這裏你能夠直接switch keyCode來進行當前Activity 
        事件處理*/  
        if(topState.onKeyUp(keyCode, event)){  
            return true;  
        }  
        else{  
            return super.onKeyUp(keyCode, event);  
        }  
    }  
      
    @Override  
    public boolean onTouchEvent(MotionEvent ev){  
        ActivityState topState = getStateManager().getTopState();  
        if(topState == null){  
        return super.onTouchEvent(ev);  
        }  
         
        if(topState.onTouchEvent(ev)){  
            return true;  
        }  
        else{  
            return super.onTouchEvent(ev);  
        }  
    }  
      
    /* 
    在按鍵事件的分發時期進行攔截,作一些本身想作的處理 
    */  
    @Override  
    public boolean dispatchKeyEvent (KeyEvent event){  
        int keyCode = event.getKeyCode();  
        ActivityState topState = getStateManager().getTopState();  
        if(topState == null){  
            return super.dispatchKeyEvent(event);  
        }  
        //sometimes even the actionbar is hide,but the input focus is on the actionbar,so when the focus is on  
        //the slot,we should intercept the enterkeyevent action from the system  
        if(event.getAction() == KeyEvent.ACTION_UP){  
            if(topState.getFocusOnSlotView()   
                && keyCode == KeyEvent.KEYCODE_ENTER){  
                return  topState.onKeyUp(keyCode, event);  
                }  
        }  
      
        return super.dispatchKeyEvent(event);  
    }  
      
    @Override  
    public boolean dispatchTouchEvent (MotionEvent ev){  
        ActivityState topState = getStateManager().getTopState();  
        if(topState == null){  
            return super.dispatchTouchEvent(ev);  
        }  
          
        if(ev.getAction() == MotionEvent.ACTION_DOWN){  
            //if the user control the gallery by mouse,you should clear the focus rect  
            topState.setFocusOnSlotView(false);  
        }  
      
        return super.dispatchTouchEvent(ev);  
    }

實例二:

@Override  
public boolean onKeyDown(int keyCode, KeyEvent event) {  
    final int uniChar = event.getUnicodeChar();  
    final boolean handled = super.onKeyDown(keyCode, event);  
    final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);  
    if (!handled && acceptFilter() && isKeyNotWhitespace) {  
        boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,  
            keyCode, event);  
            if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {  
                 return onSearchRequested();  
            }  
        }  
  
        // Eat the long press event so the keyboard doesn't come up.  
        if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {  
            return true;  
        }  
  
    return handled;  
}

假如要控制的是一個View的話,你的這個View是頂層並能得到焦點,對你有幫助的函數以下

public void setOnKeyListener (View.OnKeyListener l)  
  
public boolean dispatchKeyEvent (KeyEvent event)   
public boolean dispatchKeyEventPreIme (KeyEvent event)

第一組的做用在於你定義一個View.OnKeyListener,並重寫其onClick函數,本身來處理按鍵消息,並將此listener註冊進該view中;第二組的做用固然是使你在事件分發或輸入法以前的時候進行自定義的一些操做

實例一:


step 2:在能捕獲按鍵以後,須要作的本身來作焦點的邏輯管理

實例一:

//收集本身打算管理的view的數組  
private void colloctionViews(){  
    mViews = new View[NEEDVIEWS_SIZE];  
    mViews[0] = mYearSpinnerInput;  
    mViews[1] = mMonthSpinnerInput;  
    mViews[2] = mDaySpinnerInput;  
}  
  
//焦點的移動對View的邏輯來講其實就是數組的下標的管理  
private void  increaseCurIndex(){  
    mLastIndex = mCurrentIndex;  
    mCurrentIndex = (mCurrentIndex + 1) % mViews.length;  
}  
  
private void decreaseCurIndex(){  
    mLastIndex = mCurrentIndex;  
    if(mCurrentIndex == 0){  
        mCurrentIndex = mViews.length - 1;  
    }  
    else{  
        mCurrentIndex--;  
    }  
}  
  
//對按鍵的響應來改變的焦點主要是改變View的索引flag,而後對對應的view的高亮進行重繪  
public boolean onKeyDown(int keyCode, KeyEvent event){  
    if(KeyEvent.KEYCODE_DPAD_LEFT == keyCode){  
        decreaseCurIndex();  
    mViews[mCurrentIndex].requestFocus();  
        updateBackground();  
        return true;  
    }  
    else if(KeyEvent.KEYCODE_DPAD_RIGHT == keyCode){  
        increaseCurIndex();  
    mViews[mCurrentIndex].requestFocus();  
        updateBackground();  
        return true;  
    }  
    else{  
    }  
          
    return false;  
}

Step3:接下來是對當前焦點的高亮進行重繪

實例一:

OnKeyListener mCheckboxKeyListenner = new OnKeyListener(){  
    public boolean onKey(View v, int keyCode, KeyEvent event){  
        return onKeyDone(v, keyCode, event);  
    }  
};  
  
public boolean onKeyDone(View v, int keyCode, KeyEvent event) {  
    CollectCurrentSelected();  
          
    if(event.getAction() == KeyEvent.ACTION_UP){  
        return moveIndexOnKeyUp(keyCode);  
    }  
          
    if(event.getAction() == KeyEvent.ACTION_DOWN){  
        return moveIndexOnKeyDown(keyCode);  
    }  
    return false;  
}  
  
private boolean moveIndexOnKeyUp(int keyCode){  
    if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){  
        if(mResponseIndex == INDEX_SETTINGBTN){  
            return true;  
        }  
    }  
  
    return false;  
}  
      
private boolean moveIndexOnKeyDown(int keyCode){  
    if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT  
        || keyCode == KeyEvent.KEYCODE_DPAD_UP  
        || keyCode == KeyEvent.KEYCODE_DPAD_DOWN){  
        mResponseIndex = INDEX_CHECKBOX;  
        resetDefaultBackground();  
        return false;  
    }  
    else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){  
        if(mResponseIndex == INDEX_SETTINGBTN){  
            mResponseIndex = INDEX_CHECKBOX;  
            resetDefaultBackground();  
            return false;  
        }  
        else{  
        mResponseIndex = INDEX_SETTINGBTN;  
            mSettingBtn.setBackgroundResource(R.drawable.appwidget_item_bg_pressed);  
            mSettingBtn.invalidate();  
            mTextLayout.setBackgroundResource(R.drawable.appwidget_item_bg_normal);  
            mTextLayout.invalidate();  
            return true;  
        }  
    }  
    else if(keyCode == KeyEvent.KEYCODE_ENTER){  
        if(mResponseIndex == INDEX_CHECKBOX){  
            checkBoxOption();  
        }  
        else if(mResponseIndex == INDEX_SETTINGBTN){  
            startShowSetting();  
        }  
                  
        return true;  
    }  
    else{  
        return false;  
    }  
}  
  
/*通常View的話,能夠直接XXX. setOnKeyListener(XXX),在Setting中大量的用到PreferenceFragment,其 
內部鑲嵌了一個listview,所以對一個PreferenceFragment的操做,實際上是對該listview的操做,因此須要設這 
該listview的keyListener*/  
ListView listView = getListView();  
listView.setOnKeyListener(mCheckboxKeyListenner);

//對響應的view調用invalidate(),便能觸發其對焦點高亮進行重繪,固然這裏須要mIsOnFocus來做爲高亮  
//打開和關閉的開關  
@Override  
protected void onDraw (Canvas canvas){  
    super.onDraw(canvas);  
  
    if(mIsOnFocus){  
        // 或者使用drawBoundsOfKey(canvas);  
        drawFocusBackground();  
    }  
}

在上面的drawFocusBackground其實會遇到兩種狀況

狀況一爲:直接對當前view進行高亮邊框的繪製,你能直接拿到它的canvas進行繪製,那麼重繪代碼以下:

實例一:

//直接在外部繪製高亮矩形  
private void drawBoundsOfKey(Canvas canvas){  
    Paint p = new Paint();  
    p.setColor(Color.CYAN);  
    p.setStyle(p.getStyle().STROKE);  
    p.setStrokeWidth(3.75f);  
    focusedKey = mSoftKeyboard.getKeys().get(mLastKeyIndex);  
    rect = new Rect(focusedKey.mLeft, focusedKey.mTop,  
        focusedKey.mRight, focusedKey.mBottom);  
    canvas.drawRect(rect, p);  
}

狀況二爲:繪製的是內部一些子View的高亮邊框進行重繪,這個時候你拿不到它canvas進行繪製,那麼重繪能夠經過設置背景色的方式來達到高亮提示的效果,重繪代碼以下:

實例二:

//須要保存舊的drawable來作清除焦點的動做  
private void drawFocusBackground(){  
    View curView = mViews[mCurrentIndex];  
    mOldDrawable = curView.getBackground();  
    curView.setBackgroundColor(0xAA07FFFF);  
    curView.invalidate();     
}  
  
private void rebackOldBackground(){  
    View oldView = mViews[mLastIndex];  
    oldView.setBackgroundDrawable(mOldDrawable);  
    oldView.invalidate();  
    oldView.clearFocus();  
}

Step4:最後一步即是按下enter鍵的時候的功能響應

思路一:對一個View的功能響應,其實能夠對該view進行一次模擬的touch事件,這樣能讓系統本身走touchview的功能流程,達到思路最簡單,代碼最少的目的

實例一:

//這裏的view須要給出你須要響應的View。而構造的點擊中心點普通的view的話設定爲view的中心即可  
//以了,若是是edit類的view,或許你的點擊中心點應當在它的focusRect()  
private void emulateTouchEvent(int left, int top, int right, int bottom, View v){  
    final int x = left + (right - left) / 2;  
    final int y = top + (bottom - top) / 2;  
    final long downTime = SystemClock.uptimeMillis();  
    final MotionEvent downEvent = MotionEvent.obtain(  
        downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);  
    final MotionEvent upEvent = MotionEvent.obtain(  
        downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);  
  
     v.onTouchEvent(downEvent);  
     v.onTouchEvent(upEvent);  
  
     downEvent.recycle();  
     upEvent.recycle();  
}

思路二:查看代碼,看這個view響應的功能函數是什麼,直接對功能函數進行調用

相關文章
相關標籤/搜索