android StatusBar和NavigationBar分析
整理的比較亂,希望有所幫助。
StatusBar是手機頂部狀態欄 NavigationBar是手機底部“導航欄”,即Home,back,menu鍵
2. 隱藏StatusBar
frameworks/base/core/res/res/values/dimens.xml
把 <dimen name="status_bar_height">25dip</dimen> 修改爲<dimen name="status_bar_height">0dip</dimen>
3. 隱藏NavigationBar
frameworks/base/packages/SystemUI/src/com/android/systemui/
statusbar/phone/PhoneStatusBar.java
在start函數中註釋掉 "addNavigationBar();"
這是最最簡單最粗暴的修改,一般不建議這麼做,同樣可以修改xml文件來實現
frameworks/base/core/res/res/values/config.xml
<bool name="config_showNavigationBar">true</bool>//此處true即表示要顯示NavigationBar,false表示不顯示NavigationBar
還有另一種方法,通過屬性設置
//屬性設置可以寫在init.rc內
setprop qemu.hw.mainkeys 0
如果同時設置xml和屬性,那麼以設置的屬性會覆蓋xml的設置,以屬性設置爲主
>>>下面簡單此處代碼的流程
1).首先在/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java$makeStatusBarView方法
try {
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) { //{}進而的代碼用來設置view是否顯示和對應的Listener
mNavigationBarView =
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
mNavigationBarView.setDisabledFlags(mDisabled);
mNavigationBarView.setBar(this);
mNavigationBarView.setOnVerticalChangedListener(
new NavigationBarView.OnVerticalChangedListener() {
@Override
public void onVerticalChanged(boolean isVertical) {
if (mSearchPanelView != null) {
mSearchPanelView.setHorizontal(isVertical);
}
mNotificationPanel.setQsScrimEnabled(!isVertical);
}
});
mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
return false;
}});
}
} catch (RemoteException ex) {
// no window manager? good luck with that
}
2).很顯示此處調用的是hasNavigationBar方法
這裏通過WindowManagerService調用到PhoneWindowManager的hasNavigationBar方法
下面是mHasNavigationBar初始化的地方,現在很清楚看到爲什麼可以通過xml和屬性修改了吧!
mHasNavigationBar = res.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 ("1".equals(navBarOverride)) {
mHasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
mHasNavigationBar = true;
}
4. 虛擬按鍵是如何調用的?
NavigationBarView中包含的是KeyButtonView,我們看到的home,back,recentApp三個虛擬按鍵實際是都是一個KeyButtonView
當我們點擊這三個按鍵的任意一個時,都會調用KeyButtonView的onTouchEvent方法,在onTouchEvent方法中會調用sendevent傳遞一個按鍵值、
void sendEvent(int action, int flags, long when) {
final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
InputManager.getInstance().injectInputEvent(ev,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
此處封裝了一個KeyEvent對象,mCode即爲按鍵值,在./packages/SystemUI/res/layout/navigation_bar.xml定義
back按鍵值是4,home按鍵值是3
此單獨列一下recent_app按鍵事件處理流程
1). 在PhoneStatusBar.java的prepareNavigationBarView方法中註冊一個button的listener
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
//mRecentsClickListener實現
private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
public void onClick(View v) {
awakenDreams();
toggleRecentApps();
}
};
2. 下一步調用用父類BaseStatusBar的toggleRecentApps方法
public void toggleRecentApps() {
int msg = MSG_TOGGLE_RECENTS_APPS;
mHandler.removeMessages(msg);
mHandler.sendEmptyMessage(msg);
}
接下來通過msg調用RecentsComponent的toggleRecents,其實此處調用的是Recents的toggleRecents方法
@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
if (mUseAlternateRecents) {
// Launch the alternate recents if required
mAlternateRecents.onToggleRecents(statusBarView);
return;
}
........
}
3. 接下來調用AlternateRecentsComponent的onToggleRecents方法
間接調用startAlternateRecentsActivity方法,在這裏通過startActivity啓動RecentsActivity
在這個Activity中調用了updateRecentsTasks方法
此處可以看到取這時取到了一個stack,stack啊包含了最近任務詳細信息,並把該stacks給RecentsView
void updateRecentsTasks(Intent launchIntent) {
.......
// Load all the tasks
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SpaceNode root = loader.reload(this,
Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
mConfig.launchedFromHome);
ArrayList<TaskStack> stacks = root.getStacks();
if (!stacks.isEmpty()) {
mRecentsView.setTaskStacks(root.getStacks());
}
.......
}
相關的類還有RecentsView.java TaskStackView.java TaskStack.java
補充一下:
RecentsTaskLoader的reload等到的stacks本質上是調用SystemServicesProxy的getRecentTasks方法,
進而調用ActivityManagerService的getRecentTasks方法返回RecentTaskInfo列表
注:通過Log顯示RecentTasks對應的縮略圖存放的目錄/data/system/recent_images/
4. 縮略圖是在哪截取的?
那麼,還有一個問題縮略圖是哪哪截取的?
每一個電近的任務都保存在mRecentTasks數組中通過ActivityManager->ActivityManagerNative->ActivityManagerService
調用addAppTask方法將最近任務信息加入mRecentTasks數組,這是提供給應用的接口,可以主動加入最近任務