在如何控制Android系統中NavigationBar 的顯示與隱藏文章里簡要地介紹了Navigationbar的背景知識,
NavigationBar的代碼是放在... rameworksasepackagesSystemUI路徑下面的。該路徑下的工程主要負責手機中系統級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插入一個事件,再發送出去。