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度也就是豎屏模式,如果需要改變預設的導航欄方向也可以從這方 面著手哦。