在如何控制android系統中NavigationBar 的顯示與隐藏文章裡簡要地介紹了Navigationbar的背景知識,
NavigationBar的代碼是放在...\frameworks\base\packages\SystemUI\路徑下面的。該路徑下的工程主要負責手機中系統級UI的顯示部分,如下圖框中選中部分(包含其中的通知欄的顯示),USB的連接配接,截屏等等。
NavigationBar的建立
navigationbar 的代碼是在SystemUI工程SystemUI/src/com/android/systemui/statusbar/phone的路徑下,其中navigationbar是由PhoneStatusBar.java類建立的。在該類的makeStatusBarView()方法下,可以看到建立Navigationbar的過程:
try {
boolean showNav = mWindowManagerService.hasNavigationBar();
/// M: Support Smartbook Feature.
if (true) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
mNavigationBarView =
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
mNavigationBarView.setDisabledFlags(mDisabled);
mNavigationBarView.setBar(this);
mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
return false;
}});
}
} catch (RemoteException ex) {
// no window manager? good luck with that
}
WindowManagerService通過判斷是否需要顯示NavigationBar來決定是否需要建立NavigationBarView, NavigationBarView即為我們看到視圖的view了,navigation_bar即為NavigationBarView執行個體化的layout,你可以在SystemUI工程下的layout檔案夾下找到。
通過修改navigation_bar布局的方式來自定義NavigationBar的UI。在該layout檔案中有這樣一個類。com.android.systemui.statusbar.policy.KeyButtonView,它是系統定義的在NavigationBar上的按鈕類(後面會講到),點選會産生波紋的效果。
NavigationBarView主負責UI的初始化工作,執行個體化布局,根據螢幕方向先取正确的圖檔。
NavigationBar按鈕的事件綁定
NavigationBar按鈕上的事件綁定并不是在NavigationBarView裡實作,而是在SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java類中完成的。
通過NavigationBarView對外提供的擷取按鈕接口來完成按鈕的綁定:
Recent, Home, SearchLight按鈕事件的綁定
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
updateSearchPanel();
}
Menu, Home, Back按鈕事件的綁定:
上面三個按鈕都是KeyButtonView類,它們的事件響應過程都是在類本身裡面完成的。它們通過onTouchEvent()方法來響應點選事件,
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
int x, y;
switch (action) {
case MotionEvent.ACTION_DOWN:
//Slog.d("KeyButtonView", "press");
mDownTime = SystemClock.uptimeMillis();
setPressed(true);
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
if (mSupportsLongpress) {
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
}
break;
case MotionEvent.ACTION_MOVE:
x = (int)ev.getX();
y = (int)ev.getY();
setPressed(x >= -mTouchSlop
&& x < getWidth() + mTouchSlop
&& y >= -mTouchSlop
&& y < getHeight() + mTouchSlop);
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
if (mSupportsLongpress) {
removeCallbacks(mCheckLongPress);
}
break;
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed();
setPressed(false);
if (mCode != 0) {
if (doIt) {
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
playSoundEffect(SoundEffectConstants.CLICK);
} else {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
} else {
// no key code, just a regular ImageView
if (doIt) {
performClick();
}
}
if (mSupportsLongpress) {
removeCallbacks(mCheckLongPress);
}
break;
}
return true;
}
mCode是用來判斷該觸摸是來自于哪個button,表示不同button的keycode在KeyEvent中類都有定義。該值在布局檔案中通過擷取navigationbar_view中的systemui:keycode屬性來獲得,下面是layout布局檔案中back相應代碼段:
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_back"
systemui:keyCode="4"
android:layout_weight="0"
android:scaleType="center"
systemui:glowBackground="@drawable/ic_sysbar_highlight"
android:contentDescription="@string/accessibility_back"
/>
在onTouch中方法通過sendEvent()方法來執行不同的keycode響應事件,該方法會建立一個包含keycode的KeyEvent對象封裝,然後通過injectInputEvent()向InputManager插入一個事件,再發送出去。