网站配置,番禺建设网站策划,网站建设的想法和意见,陕西网站建设品牌公司推荐需求
SystemUI是一个与系统组件显示紧密相关的应用#xff0c;包含快捷中心、消息通知、状态栏、导航栏、任务中心等诸多模块#xff0c;本文介绍NavigationBar模块。SystemUI源码位于/frameworks/base/packages/SystemUI#xff0c;Android13平台。NavigationBar显示如下包含快捷中心、消息通知、状态栏、导航栏、任务中心等诸多模块本文介绍NavigationBar模块。SystemUI源码位于/frameworks/base/packages/SystemUIAndroid13平台。NavigationBar显示如下
关键类
NavigationBarComponent.javaNavigationBar组件类采用Dagger进行依赖注入NavigationBar.java将导航栏view添加到windownavigation_bar.xmlNavigationBar布局文件NavigationBarView.java设置导航栏图标NavigationBarInflaterView解析config中导航栏排布信息创建对应的viewhome.xml/back.xml导航栏按钮对应的布局KeyButtonView.java导航栏图标的View如果设置了keycode则将点击事件touch以keycode方式交由系统处理
代码流程
1. NavigationBar模块启动
Android13平台的SystemUI代码较旧平台变化比较大各个组件采用了Dagger进行依赖注入DI。在SystemUIApplication启动的时候进行了组件的初始化NavigationBar组件如下
// SystemUI\src\com\android\systemui\navigationbar\NavigationBarComponent.java
Subcomponent(modules { NavigationBarModule.class })
NavigationBarComponent.NavigationBarScope
public interface NavigationBarComponent {Subcomponent.Factoryinterface Factory {NavigationBarComponent create(BindsInstance DisplayId Context context,BindsInstance Nullable Bundle savedState);}NavigationBar getNavigationBar();
}// SystemUI\src\com\android\systemui\navigationbar\NavigationBarModule.java
Module
public interface NavigationBarModule {ProvidesNavigationBarScopestatic NavigationBarFrame provideNavigationBarFrame(DisplayId LayoutInflater layoutInflater) {return (NavigationBarFrame) layoutInflater.inflate(R.layout.navigation_bar_window, null);}ProvidesNavigationBarScopestatic NavigationBarView provideNavigationBarview(DisplayId LayoutInflater layoutInflater, NavigationBarFrame frame) {View barView layoutInflater.inflate(R.layout.navigation_bar, frame);return barView.findViewById(R.id.navigation_bar_view);}
}从上面可以看到navigation_bar是布局文件NavigationBarView是具体的viewNavigationBar中实现导航栏view添加到window。
2.布局文件navigation_bar.xml
NavigationBarView和NavigationBarInflaterView实际上都是Framelayout
// SystemUI\res\layout\navigation_bar.xml
com.android.systemui.navigationbar.NavigationBarViewxmlns:androidhttp://schemas.android.com/apk/res/androidandroid:idid/navigation_bar_viewandroid:layout_heightmatch_parentandroid:layout_widthmatch_parentandroid:clipChildrenfalseandroid:clipToPaddingfalseandroid:backgrounddrawable/system_bar_backgroundcom.android.systemui.navigationbar.NavigationBarInflaterViewandroid:idid/navigation_inflaterandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:clipChildrenfalseandroid:clipToPaddingfalse //com.android.systemui.navigationbar.NavigationBarView
3.NavigationBarView
我们接着看NavigationBarView主要做了下面几件事情
在构造方法中创建了返回、主页等ButtonDispatcher。布局加载完成时找到了子viewNavigationInflaterView并将ButtonDispatcher设置给了NavigationInflaterViewonAttachedToWindow()时将对应的图标设置给返回、主页等view
我们发现NavigationBarView中并没有创建返回、主页等对应的view将返回、主页等对应的view添加到ViewGroup的操作在NavigationInflaterView中
// SystemUI\src\com\android\systemui\navigationbar\NavigationBarView.java
// 创建ButtonDispatcher
public NavigationBarView(Context context, AttributeSet attrs) {mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
}
// 布局加载完成
public void onFinishInflate() {super.onFinishInflate();mNavigationInflaterView findViewById(R.id.navigation_inflater);mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);reloadNavIcons();// reloadNavIcons()中调用了updateIcons()
}
// 获取图标
private void updateIcons(Configuration oldConfig) {final boolean orientationChange oldConfig.orientation ! mConfiguration.orientation;final boolean densityChange oldConfig.densityDpi ! mConfiguration.densityDpi;final boolean dirChange oldConfig.getLayoutDirection() ! mConfiguration.getLayoutDirectin();// 获取返回按钮、主页、按钮图标drawableif (orientationChange || densityChange) {mDockedIcon getDrawable(R.drawable.ic_sysbar_docked);mHomeDefaultIcon getHomeDrawable();}if (densityChange || dirChange) {mRecentIcon getDrawable(R.drawable.ic_sysbar_recent);mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor);}if (orientationChange || densityChange || dirChange) {mBackIcon getBackDrawable();}
}// 返回按钮图标KeyButtonDrawable实际上是一个Drawable
public KeyButtonDrawable getBackDrawable() {KeyButtonDrawable drawable getDrawable(getBackDrawableRes());orientBackButton(drawable);return drawable;
}// 设置图标
protected void onAttachedToWindow() {super.onAttachedToWindow();requestApplyInsets();reorient();updateNavButtonIcons();
}4.NavigationBarInflaterView
NavigationBarInflaterView是真正创建返回、主页按钮view的地方先解析config中设置config_navBarLayout排列信息然后通过对应layout创建KeyButtonView。部分代码如下
// SystemUI\src\com\android\systemui\navigationbar\NavigationBarInflaterView.java
// 布局加载完成
protected void onFinishInflate() {super.onFinishInflate();inflateChildren(); // 加载布局clearViews();// 清空传递过来的ButtonDispatcher中保存的viewinflateLayout(getDefaultLayout()); // 关键点加载布局创建view
}// getDefaultLayout()是获取按钮排布信息从config.xml中获取如string nameconfig_navBarLayout translatablefalseleft[.5W],back[1WC];home;recent[1WC],right[.5W]/string
// 解析newLayout创建view
protected void inflateLayout(String newLayout) {if (newLayout null) {newLayout getDefaultLayout();}String[] sets newLayout.split(GRAVITY_SEPARATOR, 3);if (sets.length ! 3) {Log.d(TAG, Invalid layout.);newLayout getDefaultLayout();sets newLayout.split(GRAVITY_SEPARATOR, 3);}String[] start sets[0].split(BUTTON_SEPARATOR);String[] center sets[1].split(BUTTON_SEPARATOR);String[] end sets[2].split(BUTTON_SEPARATOR);// Inflate these in start to end order or accessibility traversal will be messed up.inflateButtons(start, mHorizontal.findViewById(com.android.internal.R.id.input_method_nav_ends_group),false /* landscape */, true /* start */);inflateButtons(center, mHorizontal.findViewById(com.android.internal.R.id.input_method_nav_center_group),false /* landscape */, false /* start */);addGravitySpacer(mHorizontal.findViewById(com.android.internal.R.id.input_method_nav_ends_group));inflateButtons(end, mHorizontal.findViewById(com.android.internal.R.id.input_method_nav_ends_group),false /* landscape */, false /* start */);updateButtonDispatchersCurrentView();
}// 创建view并添加到viewgroup
protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,boolean start) {LayoutInflater inflater landscape ? mLandscapeInflater : mLayoutInflater;View v createView(buttonSpec, parent, inflater); // 关键点创建viewif (v null) return null;v applySize(v, buttonSpec, landscape, start);parent.addView(v);addToDispatchers(v);View lastView landscape ? mLastLandscape : mLastPortrait;View accessibilityView v;if (v instanceof ReverseRelativeLayout) {accessibilityView ((ReverseRelativeLayout) v).getChildAt(0);}if (lastView ! null) {accessibilityView.setAccessibilityTraversalAfter(lastView.getId());}if (landscape) {mLastLandscape accessibilityView;} else {mLastPortrait accessibilityView;}return v;
}// 通过对应的布局创建view实际上创建的是KeyButtonView
View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {View v null;String button extractButton(buttonSpec);if (LEFT.equals(button)) {button extractButton(NAVSPACE);} else if (RIGHT.equals(button)) {button extractButton(MENU_IME_ROTATE);}if (HOME.equals(button)) {v inflater.inflate(R.layout.home, parent, false);} else if (BACK.equals(button)) {v inflater.inflate(R.layout.back, parent, false);} else if (RECENT.equals(button)) {v inflater.inflate(R.layout.recent_apps, parent, false);}return v;
}5.KeyButtonView
如上一步back按钮的布局文件如下。
com.android.systemui.navigationbar.buttons.KeyButtonViewxmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:systemuihttp://schemas.android.com/apk/res-autoandroid:idid/backandroid:layout_widthdimen/navigation_key_widthandroid:layout_heightmatch_parentandroid:layout_weight0systemui:keyCode4android:scaleTypecenterandroid:contentDescriptionstring/accessibility_backandroid:paddingStartdimen/navigation_key_paddingandroid:paddingEnddimen/navigation_key_padding/KeyButtonView是一个ImageView重写了onTouchEvent设置了keyCode则点击后给系统发送对应的keyevent
// SystemUI\src\com\android\systemui\navigationbar\buttons\KeyButtonView.java
public boolean onTouchEvent(MotionEvent ev) {...switch (action) {case MotionEvent.ACTION_DOWN:if (mCode ! KEYCODE_UNKNOWN) {sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);} else {// Provide the same haptic feedback that the system offers for virtual keys.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);}}
}
private 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);int displayId INVALID_DISPLAY;if (getDisplay() ! null) {displayId getDisplay().getDisplayId();}if (displayId ! INVALID_DISPLAY) {ev.setDisplayId(displayId);}mInputManager.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
} 总结
将导航栏View添加到Window进行显示通过读取解析xml里config的图标排布信息来创建对应的view如果设置了keycode则将点击事件touch以keycode方式交由系统处理
参考
Dagger/Hilt依赖注入使用https://developer.android.com/training/dependency-injection?hlzh-cn解析Android 8.1平台SystemUI 导航栏加载流程https://www.jb51.net/article/174313.htm