Android4.0的StatusBar和NavigationBar

主要是註意平板和手機之間佈局的區別:
幾個關鍵的java類
SystemUIService
PhoneWindowManager
WindowManagerPolicy
PhoneStatusBar
TabletStatusBar
NavigationBar

 

    SystemUIService–>onCreate中判斷加載平板佈局還是手機佈局
       [java]
<SPAN style="WHITE-SPACE: pre"> </SPAN>// Pick status bar or system bar.  
       IWindowManager wm = IWindowManager.Stub.asInterface( 
               ServiceManager.getService(Context.WINDOW_SERVICE)); 
       try { 
           SERVICES[0] = wm.canStatusBarHide() 
                   ? R.string.config_statusBarComponent 
                   : R.string.config_systemBarComponent; 
       } catch (RemoteException e) { 
           Slog.w(TAG, "Failing checking whether status bar can hide", e); 
       } 

  // Pick status bar or system bar.
        IWindowManager wm = IWindowManager.Stub.asInterface(
                ServiceManager.getService(Context.WINDOW_SERVICE));
        try {
            SERVICES[0] = wm.canStatusBarHide()
                    ? R.string.config_statusBarComponent
                    : R.string.config_systemBarComponent;
        } catch (RemoteException e) {
            Slog.w(TAG, "Failing checking whether status bar can hide", e);
        }    然後在frameworks\base\packages\systemui\res\values\config.xml中
        [html] view plaincopyprint?<SPAN style="WHITE-SPACE: pre"> </SPAN><string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string> 
        <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string> 

 <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
        <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>

    然後通過類加載器加載PhoneStatusBar或者TabletStatusBar。也就是手機的狀態欄還是平板的狀態欄。
    判斷的關鍵就在wm.canStatusBarHide()這個方法裡面。
    我們跳過幾個類後最後跟進到PhoneWindowManager中:   
[java]
<SPAN style="WHITE-SPACE: pre"> </SPAN>public boolean canStatusBarHide() { 
            return mStatusBarCanHide; 
        } 

 public boolean canStatusBarHide() {
            return mStatusBarCanHide;
        }    mStatusBarCanHide是一個成員變量,修改它值得地方在:

     public void setInitialDisplaySize(int width, int height) {…}中。
[java]
<SPAN style="WHITE-SPACE: pre"> </SPAN>int shortSizeDp = shortSize 
                * DisplayMetrics.DENSITY_DEFAULT 
                / DisplayMetrics.DENSITY_DEVICE; 
        mStatusBarCanHide = shortSizeDp < 600; 
        mStatusBarHeight = mContext.getResources().getDimensionPixelSize( 
                mStatusBarCanHide 
                ? com.android.internal.R.dimen.status_bar_height 
                : com.android.internal.R.dimen.system_bar_height); 
 
        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; 
        } 

 int shortSizeDp = shortSize
                * DisplayMetrics.DENSITY_DEFAULT
                / DisplayMetrics.DENSITY_DEVICE;
        mStatusBarCanHide = shortSizeDp < 600;
        mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
                mStatusBarCanHide
                ? com.android.internal.R.dimen.status_bar_height
                : com.android.internal.R.dimen.system_bar_height);

        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;
        }

    這個方法略微有點長,繞過前面的判斷,取上面一部分來分析。大致的意思是:
    首先取shortSize,shortSize指的是屏幕最短的像素,比如1024*600,那麼值就是600。 shortSizeDp是把pix單位轉化成dip單位。  DisplayMetrics.DENSITY_DEFAULT的值是固定死的160,在更具手機的密度算出shortSizeDp。突出標記的地方就是平板和手機狀態欄切換的關鍵瞭。隻有最短屏幕dp大於600的設備才會加載平板佈局。
    剩下的代碼用來判斷底部虛擬按鍵是否顯示的關鍵。首先是去讀取frameworks\base\core\res\res\values\config.xml中config_showNavigationBar這個key,下面 SystemProperties.get("qemu.hw.mainkeys")這個屬性主要是給模擬器使用,可以直接忽略掉。
    分析完畢!

    現在如何切換平板和手機狀態欄佈局已經很明瞭瞭,就是讓shortSizeDp 的值>=600。以1024*600為例,因為DisplayMetrics.DENSITY_DEFAULT的值已經固定死是160,所以隻要把DisplayMetrics.DENSITY_DEVICE的值<=160即可。修改屏幕密度的方法:
    /build/tools/buildinfo.sh中設定 ro.sf.lcd_density=160,
    當然MTK自己定義瞭一套屬性,MTK項目中可以通過這個屬性來設置,比較保險

    /mediatek/config/[Project_name]/system.prop中設定該屬性的值
如此基本大功告成。mm編譯,然後報錯。。。。
錯誤定位到TabletStatusBar的makeStatusBarView()中,代碼如下:
   [java]
<SPAN style="WHITE-SPACE: pre"> </SPAN> try { 
            // Sanity-check that someone hasn't set up the config wrong and asked for a navigation  
            // bar on a tablet that has only the system bar  
            if (mWindowManager.hasNavigationBar()) { 
                throw new RuntimeException( 
                        "Tablet device cannot show navigation bar and system bar"); 
            } 
        } catch (RemoteException ex) { 
        } 

  try {
            // Sanity-check that someone hasn't set up the config wrong and asked for a navigation
            // bar on a tablet that has only the system bar
            if (mWindowManager.hasNavigationBar()) {
                throw new RuntimeException(
                        "Tablet device cannot show navigation bar and system bar");
            }
        } catch (RemoteException ex) {
        }

    問題很明顯,平板設備中不需要NavigationBar,因為NavigationBar的東西已經全部合並到StatusBar裡面瞭。修改方法:

    frameworks\base\core\res\res\values\config.xml中config_showNavigationBar修改為false

 

囉嗦半天,其實就兩個問題:
    一。虛擬按鍵的隱藏和顯示:

     修改frameworks\base\core\res\res\values\config.xml中config_showNavigationBar

    二。狀態欄平板和手機之間的切換:

    1.        /build/tools/buildinfo.sh中設定 ro.sf.lcd_density=160,
        當然MTK自己定義瞭一套屬性,MTK項目中可以通過這個屬性來設置,比較保險
        /mediatek/config/[Project_name]/system.prop中設定該屬性的值
  2.    frameworks\base\core\res\res\values\config.xml中config_showNavigationBar修改為false

You May Also Like