我們使用的大多數android手機上的Home鍵,返回鍵以及menu鍵都是實體觸摸感應按鍵。如果你用Google的Nexus4或Nexus5話,你會發現它們並沒有實體按鍵或觸摸感應按鍵,取而代之的是在屏幕的下方加了一個小黑條,在這個黑條上有3個按鈕控件,這種設置無疑使得手機的外觀的設計更加簡約。但我遇到身邊用Nexus 4手機的人都吐槽這種設計,原因很簡單:好端端的屏幕,被划出一塊區域用來顯示3個按鈕(如下圖所示):Back, Home, Recent。並且它一直用在那里占用着。
在android源碼中,那一塊區域被叫做NavigationBar。同時,google在代碼中也預留了標志,用來控制它的顯示與隱藏。NavigationBar的顯示與隱藏的控制是放在SystemU中的,具體的路徑是:\frameworks\base\packages\SystemUI。對android4.0以上的手機而言,SystemUi包含兩部分:StatusBar和NavigationBar。在SystemUI的工程下有一個類PhoneStatusBar.java,在該類中可以發現關於控制NavigationBar的相關代碼:
在start()方法里可以看到NavigationBar是在那時候被添加進來,但只是添加,決定它顯示還是隱藏是在后面控制的。
-
<span style= "font-size:18px;">
-
public void start() {
-
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
-
.getDefaultDisplay();
-
updateDisplaySize();
-
-
/// M: Support Smartbook Feature.
-
if (SIMHelper.isMediatekSmartBookSupport()) {
-
/// M: [ALPS01097705] Query the plug-in state as soon as possible.
-
mIsDisplayDevice = SIMHelper.isSmartBookPluggedIn(mContext);
-
Log.v(TAG, "start, mIsDisplayDevice=" + mIsDisplayDevice);
-
}
-
-
super.start(); // calls createAndAddWindows()
-
-
addNavigationBar();
-
-
// Lastly, call to the icon policy to install/update all the icons.
-
mIconPolicy = new PhoneStatusBarPolicy(mContext);
-
-
mHeadsUpObserver.onChange( true); // set up
-
if (ENABLE_HEADS_UP) {
-
mContext.getContentResolver().registerContentObserver(
-
Settings.Global.getUriFor(SETTING_HEADS_UP), true,
-
mHeadsUpObserver);
-
}
-
}</span>
其中的addNavigationBar()具體的實現方法如下:
-
<span style= "font-size:18px;"> // For small-screen devices (read: phones) that lack hardware navigation buttons
-
private void addNavigationBar() {
-
if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
-
if (mNavigationBarView == null) return;
-
-
prepareNavigationBarView();
-
-
mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
-
}</span>
-
<span style= "font-size:18px;">try {
-
boolean showNav = mWindowManagerService.hasNavigationBar();
-
if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
-
if (showNav) {
-
mNavigationBarView =
-
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
-
-
mNavigationBarView.setDisabledFlags(mDisabled);
-
mNavigationBarView.setBar( this);
-
}
-
} catch (RemoteException ex) {
-
// no window manager? good luck with that
-
}</span>
WindowManagerService類實現了WindowManagerPolicy的接口,所以WindowManagerService會回調WindowManagerPolicy 的hasNavigationBar()接口,
-
<span style= "font-size:18px;">
-
public boolean hasNavigationBar() {
-
return mPolicy.hasNavigationBar();
-
}</span>
Policy向下調用實際上調用的是PhoneWindowManager實現的hasNavigationBar方法,下面代碼是PhoneWindowManager中的hasNavigationBar()方法。
-
<span style= "font-size:18px;">// Use this instead of checking config_showNavigationBar so that it can be consistently
-
// overridden by qemu.hw.mainkeys in the emulator.
-
public boolean hasNavigationBar() {
-
return mHasNavigationBar;
-
}</span>
而mHasNavigationBar的賦值可以在PhoneWindowManager中的setInitialDisplaySize(Display display, int width, int height, int density)方法中找到,
-
<span style= "font-size:18px;"> if (!mHasSystemNavBar) {
-
mHasNavigationBar = mContext.getResources().getBoolean(
-
com.android.internal.R.bool.config_showNavigationBar);
-
// Allow a system property to override this. Used by the emulator.
-
// See also hasNavigationBar().
-
String navBarOverride = SystemProperties.get( "qemu.hw.mainkeys");
-
if (! "".equals(navBarOverride)) {
-
if (navBarOverride.equals("1")) mHasNavigationBar = false;
-
else if (navBarOverride.equals("0")) mHasNavigationBar = true;
-
}
-
} else {
-
mHasNavigationBar = false;
-
}</span>
1.首先從系統的資源文件中取設定值config_showNavigationBar, 這個值的設定的文件路徑是frameworks/base/core/res/res/values/config.xml
-
<!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
-
autodetected from the Configuration. -->
-
<bool name= "config_showNavigationBar">false</bool>
所以上面的兩處設定共同決定了NavigationBar的顯示與隱藏。