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数组,这是提供给应用的接口,可以主动加入最近任务