涉及到系统的定制开发,不可缺少的一个就是系统导航栏和状态栏的修改,而这部分的修改通常都涉及到SyetemUI这个系统应用的修改,它的路径通常是位于platform\frameworks\base\packages\SystemUI。
先来说说导航栏的修改吧,导航栏所在的类是在\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarView.class里,这个类主要是对导航栏所有的按键如home键,最近任务键合返回键等的封装,还有提供对横屏和竖屏方向的导航栏的显示方向的方法支持,它的xml布局是res\layout\navigaton_bar.xml,这是其中的一些代码片段,
<FrameLayout android:id="@+id/rot90"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:visibility="gone"
android:paddingTop="0dp"
>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false"
android:background="#000000"
android:id="@+id/nav_buttons"
android:animateLayoutChanges="true"
>
<!-- navigation controls -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
...
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_recent_land"
android:scaleType="center"
android:layout_weight="0"
android:visibility="gone"
android:contentDescription="@string/accessibility_recent"
/>
...<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
android:src="@drawable/systemback_selector"
android:scaleType="center"
android:layout_weight="1"
systemui:keyCode="4"
android:contentDescription="@string/accessibility_back"
/>
可以看到最外面是一个frame layout,id是rot90,也就是竖屏时的布局,其实这个布局里还有一个rot0的布局,表示横屏的布局,当横屏模式时竖屏时的rot0布局是隐藏的,可以看到里面有id是back,和recent_apps的KeyButtonView,就是代表回退键,最近任务键啦,导航栏上所有的键都被封装在了KeyButtonView里了,那怎么区分是什么键呢?可以看到KeyButtonView的控件里有一个属性为systemui:keyCode,每个键的这个属性都不一样,比如上述代码的back键的keycode值就是4,如果要实现在导航栏上增加或者修改按钮的话就可以在这个xml布局上加上或者去掉了,那么这些键又是在哪里监听,方向又是怎么控制呢?又回到最前面的NavigationBarView里了,如下面的代码段,
public View getCurrentView() {
return mCurrentView;
}
public View getRecentsButton() {
return mCurrentView.findViewById(R.id.recent_apps);
}
public View getMenuButton() {
return mCurrentView.findViewById(R.id.menu);
}
public View getBackButton() {
return mCurrentView.findViewById(R.id.back);
}
public View getHomeButton() {
return mCurrentView.findViewById(R.id.home);
}
NavigationBarView里提供了对导航栏各键的获取方法,而真正的监听则是在systemui\statusbar\phone\PhoneStatusBar里,在它的start()方法里面的addNavigationBar()里,
@Override
public void start() {
...
super.start(); // calls createAndAddWindows()
...
addNavigationBar();
...
在addNavigationBar里会调用到一个prepareNavigationBarView方法,
// For small-screen devices (read: phones) that lack hardware navigation buttons
private void addNavigationBar() {
...
prepareNavigationBarView();
...
}
而prepareNavigationBarView方法就是最终的监听导航栏上各虚拟键的方法啦,
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getRecentsButton().setLongClickable(true);
mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getBackButton().setLongClickable(true);
mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
...
}
}
在这里就可以根据项目要求,找到对应的虚拟按键的监听器改写系统虚拟按键的监听方法了。
接下来就是导航栏方向的控制了,回到上面的prepareNavigationBarView方法体内第一个调用的方法是mNavigationBarView.reorient();就是在初始化当航栏时对整个导航栏方向的重新初始化了,实现方法是在NavigationBarView里,
public void reorient() {
final int rot = mDisplay.getRotation();
for (int i=0; i<4; i++) {
mRotatedViews[i].setVisibility(View.GONE);
}
mCurrentView = mRotatedViews[rot];
mCurrentView.setVisibility(View.VISIBLE);
...
可以看到,首先从Display这个静态类里获取到一个rot值,mCurrentView表示当前显示的是横屏模式还是竖屏模式,它由mRotatedViews[rot]决定,这个mRotatedViews数组里面又有什么东西呢?这个数组是在NavigationBarView加载完成后调用的onFinishInflate()方法里被初始化的,
@Override
public void onFinishInflate() {
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90];
mCurrentView = mRotatedViews[Surface.ROTATION_90];
...
}
Surface.ROTATION_0的值是0,Surface.ROTATION_90是1,以此类推,这里很眼熟吧,定义了横屏时使用rot0的布局,竖屏时使用rot90的布局,并且mCurrentView默认是90度也就是竖屏模式,如果需要改变默认的导航栏方向也可以从这方 面着手哦。