如何Android中自定義Navigationbar

如何控制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插入一個事件,再發送出去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章