1. 程式人生 > >Android菜鳥之學習android原始碼之二(SystemUI導航欄初步認識及修改)

Android菜鳥之學習android原始碼之二(SystemUI導航欄初步認識及修改)

涉及到系統的定製開發,不可缺少的一個就是系統導航欄和狀態列的修改,而這部分的修改通常都涉及到SyetemUI這個系統應用的修改,它的路徑通常是位於platform\frameworks\base\packages\SystemUI。
先來說說導航欄的修改吧,導航欄所在的類是在\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarView.class裡,這個類主要是對導航欄所有的按鍵如home鍵,最近任務鍵合返回鍵等的封裝,還有提供對橫屏和豎屏方向的導航欄的顯示方向的方法支援,它的xml佈局是res\layout\navigaton_bar.xml,這是其中的一些程式碼片段,
    <FrameLayout android:id="@+id/rot90"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:visibility="gone"
        android:paddingTop="0dp"
        >

        <LinearLayout 
            android:layout_height="match_parent"
            android:layout_width="match_parent"
android:orientation="vertical" android:clipChildren="false" android:clipToPadding="false" android:background="#000000" android:id="@+id/nav_buttons" android:animateLayoutChanges="true" > <!-- navigation controls --> <FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent" android:visibility="gone"> ... <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps" android:layout_height="@dimen/navigation_key_width" android:layout_width="match_parent" android:src="@drawable/ic_sysbar_recent_land" android:scaleType="center" android:layout_weight="0" android:visibility="gone" android:contentDescription="@string/accessibility_recent" /> ...<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" android:layout_height="@dimen/navigation_key_width" android:layout_width="match_parent" android:src="@drawable/systemback_selector" android:scaleType="center" android:layout_weight="1" systemui:keyCode="4" android:contentDescription="@string/accessibility_back" />

可以看到最外面是一個frame layout,id是rot90,也就是豎屏時的佈局,其實這個佈局裡還有一個rot0的佈局,表示橫屏的佈局,當橫屏模式時豎屏時的rot0佈局是隱藏的,可以看到裡面有id是back,和recent_apps的KeyButtonView,就是代表回退鍵,最近任務鍵啦,導航欄上所有的鍵都被封裝在了KeyButtonView裡了,那怎麼區分是什麼鍵呢?可以看到KeyButtonView的控制元件裡有一個屬性為systemui:keyCode,每個鍵的這個屬性都不一樣,比如上述程式碼的back鍵的keycode值就是4,如果要實現在導航欄上增加或者修改按鈕的話就可以在這個xml佈局上加上或者去掉了,那麼這些鍵又是在哪裡監聽,方向又是怎麼控制呢?又回到最前面的NavigationBarView裡了,如下面的程式碼段,

   public View getCurrentView() {
        return mCurrentView;
    }

    public View getRecentsButton() {
        return mCurrentView.findViewById(R.id.recent_apps);
    }

    public View getMenuButton() {
        return mCurrentView.findViewById(R.id.menu);
    }

    public View getBackButton() {
        return mCurrentView.findViewById(R.id.back);
    }

    public View getHomeButton() {
        return mCurrentView.findViewById(R.id.home);
    }

NavigationBarView裡提供了對導航欄各鍵的獲取方法,而真正的監聽則是在systemui\statusbar\phone\PhoneStatusBar裡,在它的start()方法裡面的addNavigationBar()裡,

@Override
    public void start() {
    ...

        super.start(); // calls createAndAddWindows()

    ...

        addNavigationBar();
    ...

在addNavigationBar裡會呼叫到一個prepareNavigationBarView方法,

    // For small-screen devices (read: phones) that lack hardware navigation buttons
    private void addNavigationBar() {
    ...

        prepareNavigationBarView();

    ...
    }

而prepareNavigationBarView方法就是最終的監聽導航欄上各虛擬鍵的方法啦,

    private void prepareNavigationBarView() {
        mNavigationBarView.reorient();

        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
        mNavigationBarView.getRecentsButton().setLongClickable(true);
        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
        mNavigationBarView.getBackButton().setLongClickable(true);
        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);

...              
        }         
    }

在這裡就可以根據專案要求,找到對應的虛擬按鍵的監聽器改寫系統虛擬按鍵的監聽方法了。
接下來就是導航欄方向的控制了,回到上面的prepareNavigationBarView方法體內第一個呼叫的方法是mNavigationBarView.reorient();就是在初始化當航欄時對整個導航欄方向的重新初始化了,實現方法是在NavigationBarView裡,

    public void reorient() {
        final int rot = mDisplay.getRotation();
        for (int i=0; i<4; i++) {
            mRotatedViews[i].setVisibility(View.GONE);
        }
        mCurrentView = mRotatedViews[rot];
        mCurrentView.setVisibility(View.VISIBLE);
        ...

可以看到,首先從Display這個靜態類裡獲取到一個rot值,mCurrentView表示當前顯示的是橫屏模式還是豎屏模式,它由mRotatedViews[rot]決定,這個mRotatedViews數組裡面又有什麼東西呢?這個陣列是在NavigationBarView載入完成後呼叫的onFinishInflate()方法裡被初始化的,

    @Override
    public void onFinishInflate() {
        mRotatedViews[Surface.ROTATION_0] =
        mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);

        mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);

        mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90];

        mCurrentView = mRotatedViews[Surface.ROTATION_90];
    ...
    }

Surface.ROTATION_0的值是0,Surface.ROTATION_90是1,以此類推,這裡很眼熟吧,定義了橫屏時使用rot0的佈局,豎屏時使用rot90的佈局,並且mCurrentView預設是90度也就是豎屏模式,如果需要改變預設的導航欄方向也可以從這方 面著手哦。