赞
踩
导航栏是指在屏幕底端或侧边容纳了一排虚拟按键的一个窗口,这些按键包括了常用的返回键、主页键、最近任务键等。导航栏窗口作为 SystemUI 的一个重要组件,它的启动时机是在状态栏之后,同时导航栏窗口也是通过 WindowManager.addView() 方法创建的。
和状态栏一样,导航栏的创建入口也是在 StatusBar.makeStatusBarView()。
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java protected void makeStatusBarView() { // 创建状态栏... ... // 创建导航栏 try { boolean showNav = mWindowManagerService.hasNavigationBar(); // 1 是否显示导航栏 if (showNav) { createNavigationBar(); // 2 创建导航栏的控件树 } } catch (RemoteException ex) { // no window manager? good luck with that } ... }
通过 hasNavigationBar() 判断是否需要创建显示导航栏,首先看配置文件,然后检查系统属性 qemu.hw.mainkeys 是否设置,两个判断标志都在 PhoneWindowManager 类的 setInitialDisplaySize() 方法中获取设置。
// frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java public boolean hasNavigationBar() { return mPolicy.hasNavigationBar(); } // frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java public class PhoneWindowManager implements WindowManagerPolicy { boolean mHasNavigationBar = false; public boolean hasNavigationBar() { return mHasNavigationBar; // } public void setInitialDisplaySize(Display display, int width, int height, int density) { // frameworks\base\core\res\res\values\config.xml 默认为true mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); // 可以在模拟器中通过设置系统属性进行覆盖 String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); if ("1".equals(navBarOverride)) { mHasNavigationBar = false; } else if ("0".equals(navBarOverride)) { mHasNavigationBar = true; } } }
createNavigationBar() 通过调用 NavigationBarFragment.create() 来加载导航栏控件树。
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java private NavigationBarFragment mNavigationBar; private View mNavigationBarView; protected void createNavigationBar() { // navigation_bar_window.xml mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> { // navigation_bar.xml mNavigationBar = (NavigationBarFragment) fragment; if (mLightBarController != null) { mNavigationBar.setLightBarController(mLightBarController); } mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility); }); }
create() 方法和创建状态栏相似,同样是调用 WindowManager.addView 创建导航栏窗口,而在 onCreateView() 方法中真正加载的 RootView 是 navigation_bar.xml 布局。在这里,导航栏的窗口类型是 TYPE_NAVIGATION_BAR,导航栏的宽度高度都是 MATCH_PARENT,但不会充满整个屏幕,而是充满由 WindowManager 提供的父布局 ,其大小是在 PhoneWindowManager.beginLayoutLw() 中计算的,后面会分析到。
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarFragment.java public static View create(Context context, FragmentListener listener) { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, // 高度宽度 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, //窗口类型 FIRST_SYSTEM_WINDOW+19 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING // 接受导致设备唤醒的触摸事件 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE //不接受按键事件 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL // 消耗所有指针事件本身,而不管它们是否在窗口内 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH // // 该窗口将接受超出其边界的触摸事件,并将其发送到也支持拆分触摸的其他窗口 | WindowManager.LayoutParams.FLAG_SLIPPERY, // 在手势中期,使触摸能够从窗口滑出到相邻窗口中,而不是在手势持续时间内被捕获 PixelFormat.TRANSLUCENT); lp.token = new Binder(); lp.setTitle("NavigationBar"); lp.windowAnimations = 0; // 不使用窗口动画 //加载导航栏控件树 frameworks\base\packages\SystemUI\res\layout\navigation_bar_window.xml View navigationBarView = LayoutInflater.from(context).inflate( R.layout.navigation_bar_window, null); if (navigationBarView == null) return null; // 创建导航栏窗口 context.getSystemService(WindowManager.class).addView(navigationBarView, lp); FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView); NavigationBarFragment fragment = new NavigationBarFragment(); fragmentHost.getFragmentManager().beginTransaction() .replace(R.id.navigation_bar_frame, fragment, TAG) // navigation_bar_window.xml .commit(); fragmentHost.addTagListener(TAG, listener); return navigationBarView; }
NavigationBarFragment
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarFragment.java public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { // 加载布局 frameworks\base\packages\SystemUI\res\layout\navigation_bar.xml return inflater.inflate(R.layout.navigation_bar, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mNavigationBarView = (NavigationBarView) view; // 设置导航栏按键触摸事件的监听器 prepareNavigationBarView(); ... } private void prepareNavigationBarView() { mNavigationBarView.reorient(); ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); recentsButton.setOnClickListener(this::onRecentsClick); recentsButton.setOnTouchListener(this::onRecentsTouch); recentsButton.setLongClickable(true); recentsButton.setOnLongClickListener(this::onLongPressBackRecents); ButtonDispatcher backButton = mNavigationBarView.getBackButton(); backButton.setLongClickable(true); backButton.setOnLongClickListener(this::onLongPressBackRecents); ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(this::onHomeTouch); homeButton.setOnLongClickListener(this::onHomeLongClick); ... }
再来看一下 PhoneWindowManager.beginLayoutLw() 是怎么计算的导航栏位置的。
一般来说,导航栏位于屏幕底部和右侧,这是根据屏幕方向及高宽大小决定的。 这里 mNavigationBarCanMove 变量代表导航栏是否可以移动,是在PhoneWindowManager.setInitialDisplaySize() 方法中设置的。mNavigationBarPosition 变量代表导航栏最终的布局位置。
// frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,int displayRotation, int uiMode) {
...
boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,navAllowedHidden, statusBarExpandedNotKeyguard); //
updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing); //
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
private boolean<
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。