主要是注意平板和手機之間布局的區別:
幾個關鍵的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