当前位置:   article > 正文

Android SystemUI之NavigationBar,导航栏(四)_android systemui navigationbar

android systemui navigationbar

Android  SystemUI系列:

     1.Android  SystemUI之启动流程(一)

     2.Android SystemUI之StatusBar,状态栏(二)

     3.Android SystemUI之下拉菜单,通知栏,快捷面板(三)

     4.Android SystemUI之NavigationBar,导航栏(四)

     5.Android SystemUI之Recent,近期列表(五)

 

一、导航栏的创建

     1.先上一幅导航栏的View结构图。如下。

    2.导航栏的创建

      StatusBar.makeStatusBarView

  1. try {
  2. //创建导航栏
  3. boolean showNav = mWindowManagerService.hasNavigationBar();
  4. if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
  5. if (showNav) {
  6. createNavigationBar();
  7. }
  8. } catch (RemoteException ex) {
  9. // no window manager? good luck with that
  10. }
  1. protected void createNavigationBar() {
  2. mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
  3. mNavigationBar = (NavigationBarFragment) fragment;
  4. if (mLightBarController != null) {
  5. mNavigationBar.setLightBarController(mLightBarController);
  6. }
  7. mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
  8. });
  9. }

 NavigationBarFragment.create

  1. public static View create(Context context, FragmentListener listener) {
  2. WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
  3. LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
  4. WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
  5. WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
  6. | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  7. | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
  8. | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
  9. | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
  10. | WindowManager.LayoutParams.FLAG_SLIPPERY,
  11. PixelFormat.TRANSLUCENT);
  12. lp.token = new Binder();
  13. lp.setTitle("NavigationBar");
  14. lp.accessibilityTitle = context.getString(R.string.nav_bar);
  15. lp.windowAnimations = 0;
  16. View navigationBarView = LayoutInflater.from(context).inflate(
  17. R.layout.navigation_bar_window, null);
  18. if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
  19. if (navigationBarView == null) return null;
  20. context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
  21. FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
  22. NavigationBarFragment fragment = new NavigationBarFragment();
  23. fragmentHost.getFragmentManager().beginTransaction()
  24. .replace(R.id.navigation_bar_frame, fragment, TAG)
  25. .commit();
  26. fragmentHost.addTagListener(TAG, listener);
  27. return navigationBarView;
  28. }

上述代码做了两件事:1.创建navigationBarView 并且把navigationBarView添加到windowManager中。

                                 2.创建NavigationBarFragment 替换navigation_bar_window的布局文件,改成navigation_bar

navigation_bar.xml

  1. <com.android.systemui.statusbar.phone.NavigationBarView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:systemui="http://schemas.android.com/apk/res-auto"
  4. android:layout_height="match_parent"
  5. android:layout_width="match_parent"
  6. android:background="@drawable/system_bar_background">
  7. <com.android.systemui.statusbar.phone.NavigationBarInflaterView
  8. android:id="@+id/navigation_inflater"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent" />
  11. </com.android.systemui.statusbar.phone.NavigationBarView>

我们先看NavigationBarInflaterView

  1. public NavigationBarInflaterView(Context context, AttributeSet attrs) {
  2. super(context, attrs);
  3. createInflaters();
  4. mDisplay = ((WindowManager)
  5. context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
  6. Mode displayMode = mDisplay.getMode();
  7. isRot0Landscape = displayMode.getPhysicalWidth() > displayMode.getPhysicalHeight();
  8. mOverviewProxyService = Dependency.get(OverviewProxyService.class);
  9. }
  10. private void createInflaters() {
  11. mLayoutInflater = LayoutInflater.from(mContext);
  12. Configuration landscape = new Configuration();
  13. landscape.setTo(mContext.getResources().getConfiguration());
  14. landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;
  15. mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));
  16. }
  17. @Override
  18. protected void onFinishInflate() {
  19. super.onFinishInflate();
  20. inflateChildren();//添加mRot0和mRot90,就是横竖屏的布局
  21. clearViews();//清空所有的view
  22. inflateLayout(getDefaultLayout());
  23. }
  24. private void inflateChildren() {
  25. removeAllViews();
  26. mRot0 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout, this, false);
  27. mRot0.setId(R.id.rot0);
  28. addView(mRot0);
  29. mRot90 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_rot90, this,
  30. false);
  31. mRot90.setId(R.id.rot90);
  32. addView(mRot90);
  33. updateAlternativeOrder();
  34. }

上述代码就是在加载导航栏布局文件,如果是0度的时候加载navigation_layout,如果是90度的时候加载navigation_layout_rot90,横屏竖屏加载的layout不同。

  1. protected void inflateLayout(String newLayout) {
  2. mCurrentLayout = newLayout;
  3. if (newLayout == null) {
  4. newLayout = getDefaultLayout();
  5. }
  6. String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
  7. if (sets.length != 3) {
  8. Log.d(TAG, "Invalid layout.");
  9. newLayout = getDefaultLayout();
  10. sets = newLayout.split(GRAVITY_SEPARATOR, 3);
  11. }
  12. String[] start = sets[0].split(BUTTON_SEPARATOR);
  13. String[] center = sets[1].split(BUTTON_SEPARATOR);
  14. String[] end = sets[2].split(BUTTON_SEPARATOR);
  15. // Inflate these in start to end order or accessibility traversal will be messed up.
  16. inflateButtons(start, mRot0.findViewById(R.id.ends_group), isRot0Landscape, true);
  17. inflateButtons(start, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, true);
  18. inflateButtons(center, mRot0.findViewById(R.id.center_group), isRot0Landscape, false);
  19. inflateButtons(center, mRot90.findViewById(R.id.center_group), !isRot0Landscape, false);
  20. addGravitySpacer(mRot0.findViewById(R.id.ends_group));
  21. addGravitySpacer(mRot90.findViewById(R.id.ends_group));
  22. inflateButtons(end, mRot0.findViewById(R.id.ends_group), isRot0Landscape, false);
  23. inflateButtons(end, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, false);
  24. updateButtonDispatchersCurrentView();
  25. }
  1. protected String getDefaultLayout() {
  2. final int defaultResource = mOverviewProxyService.shouldShowSwipeUpUI()
  3. ? R.string.config_navBarLayoutQuickstep
  4. : R.string.config_navBarLayout;
  5. return mContext.getString(defaultResource);
  6. }
  1. protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
  2. boolean start) {
  3. LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
  4. View v = createView(buttonSpec, parent, inflater);
  5. if (v == null) return null;
  6. v = applySize(v, buttonSpec, landscape, start);//计算view的宽的大小
  7. parent.addView(v);
  8. addToDispatchers(v);
  9. View lastView = landscape ? mLastLandscape : mLastPortrait;
  10. View accessibilityView = v;
  11. if (v instanceof ReverseRelativeLayout) {
  12. accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);
  13. }
  14. if (lastView != null) {
  15. accessibilityView.setAccessibilityTraversalAfter(lastView.getId());
  16. }
  17. if (landscape) {
  18. mLastLandscape = accessibilityView;
  19. } else {
  20. mLastPortrait = accessibilityView;
  21. }
  22. return v;
  23. }
  1. private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
  2. View v = null;
  3. String button = extractButton(buttonSpec);
  4. if (LEFT.equals(button)) {
  5. String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE);
  6. button = extractButton(s);
  7. } else if (RIGHT.equals(button)) {
  8. String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME_ROTATE);
  9. button = extractButton(s);
  10. }
  11. // Let plugins go first so they can override a standard view if they want.
  12. for (NavBarButtonProvider provider : mPlugins) {
  13. v = provider.createView(buttonSpec, parent);
  14. if (v != null) return v;
  15. }
  16. if (HOME.equals(button)) {
  17. v = inflater.inflate(R.layout.home, parent, false);
  18. } else if (BACK.equals(button)) {
  19. v = inflater.inflate(R.layout.back, parent, false);
  20. }
  21. //laiyw add for volume Up/Down
  22. else if (VOLUME_DOWN.equals(button)) {
  23. v = inflater.inflate(R.layout.volume_down, parent, false);
  24. }
  25. else if (VOLUME_UP.equals(button)) {
  26. v = inflater.inflate(R.layout.volume_up, parent, false);
  27. //laiyw add for volume Up/Down
  28. } else if (RECENT.equals(button)) {
  29. v = inflater.inflate(R.layout.recent_apps, parent, false);
  30. } else if (MENU_IME_ROTATE.equals(button)) {
  31. v = inflater.inflate(R.layout.menu_ime, parent, false);
  32. } else if (NAVSPACE.equals(button)) {
  33. v = inflater.inflate(R.layout.nav_key_space, parent, false);
  34. } else if (CLIPBOARD.equals(button)) {
  35. v = inflater.inflate(R.layout.clipboard, parent, false);
  36. } else if (CONTEXTUAL.equals(button)) {
  37. v = inflater.inflate(R.layout.contextual, parent, false);
  38. } else if (button.startsWith(KEY)) {
  39. String uri = extractImage(button);
  40. int code = extractKeycode(button);
  41. v = inflater.inflate(R.layout.custom_key, parent, false);
  42. ((KeyButtonView) v).setCode(code);
  43. if (uri != null) {
  44. if (uri.contains(":")) {
  45. ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
  46. } else if (uri.contains("/")) {
  47. int index = uri.indexOf('/');
  48. String pkg = uri.substring(0, index);
  49. int id = Integer.parseInt(uri.substring(index + 1));
  50. ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
  51. }
  52. }
  53. }
  54. return v;
  55. }

上述的代码都比较简单,有两点:

第一、导航栏显示哪些控件是由getDefaultLayout来决定。

<string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string>

一般情况下我们是home,recent,back这三个键,如果你需要加其他的就在这个配置文件夹。同时在createView添加对应的布局文件。

第二、createView方法创建对应的布局文件,并且添加到导航栏中。

home.xml

  1. <com.android.systemui.statusbar.policy.KeyButtonView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:systemui="http://schemas.android.com/apk/res-auto"
  4. android:id="@+id/home"
  5. android:layout_width="@dimen/navigation_key_width"
  6. android:layout_height="match_parent"
  7. android:layout_weight="0"
  8. systemui:keyCode="3"
  9. android:scaleType="center"
  10. android:contentDescription="@string/accessibility_home"
  11. android:paddingStart="@dimen/navigation_key_padding"
  12. android:paddingEnd="@dimen/navigation_key_padding"
  13. />

back.xml

  1. <com.android.systemui.statusbar.policy.KeyButtonView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:systemui="http://schemas.android.com/apk/res-auto"
  4. android:id="@+id/back"
  5. android:layout_width="@dimen/navigation_key_width"
  6. android:layout_height="match_parent"
  7. android:layout_weight="0"
  8. systemui:keyCode="4"
  9. android:scaleType="center"
  10. android:contentDescription="@string/accessibility_back"
  11. android:paddingStart="@dimen/navigation_key_padding"
  12. android:paddingEnd="@dimen/navigation_key_padding"
  13. />

recent_app.xml

  1. <com.android.systemui.statusbar.policy.KeyButtonView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:systemui="http://schemas.android.com/apk/res-auto"
  4. android:id="@+id/recent_apps"
  5. android:layout_width="@dimen/navigation_key_width"
  6. android:layout_height="match_parent"
  7. android:layout_weight="0"
  8. android:scaleType="center"
  9. android:contentDescription="@string/accessibility_recent"
  10. android:paddingStart="@dimen/navigation_key_padding"
  11. android:paddingEnd="@dimen/navigation_key_padding"
  12. />

那么我们现在布局文件都添加完成了,但是你会发现在NavigationBarInflaterView没有对资源文件添加的代码已经控件点击触摸事件处理逻辑。那么这两部分代码在哪里呢?

答案是:

1.NavigationBarView 完成资源文件添加。

2.NavigationBarFragment  添加点击事件和触摸事件的处理逻辑。

3.NavigationBarView

  1. public NavigationBarView(Context context, AttributeSet attrs) {
  2. super(context, attrs);
  3. mDisplay = ((WindowManager) context.getSystemService(
  4. Context.WINDOW_SERVICE)).getDefaultDisplay();
  5. mVertical = false;
  6. mShowMenu = false;
  7. mShowAccessibilityButton = false;
  8. mLongClickableAccessibilityButton = false;
  9. mOverviewProxyService = Dependency.get(OverviewProxyService.class);
  10. mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
  11. mConfiguration = new Configuration();
  12. mConfiguration.updateFrom(context.getResources().getConfiguration());
  13. M : New plugin architecture - Navigation bar plugin
  14. try {
  15. mSystemUICustomizationFactory =
  16. OpSystemUICustomizationFactoryBase.getOpFactory(context);
  17. mNavBarPlugin = mSystemUICustomizationFactory.makeNavigationBar(context);
  18. } catch (Exception e) {
  19. if (DEBUG) Log.d(TAG,"mNavBarPlugin init fail");
  20. e.printStackTrace();
  21. }
  22. reloadNavIcons();//初始化加载资源,主要是图片。
  23. mBarTransitions = new NavigationBarTransitions(this);
  24. mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
  25. mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
  26. mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
  27. mButtonDispatchers.put(R.id.volume_down, new ButtonDispatcher(R.id.volume_down));
  28. mButtonDispatchers.put(R.id.volume_up, new ButtonDispatcher(R.id.volume_up));
  29. getVolumeDownButton().setLongClickable(false);
  30. getVolumeUpButton().setLongClickable(false);
  31. mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));
  32. mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
  33. mButtonDispatchers.put(R.id.accessibility_button,
  34. new ButtonDispatcher(R.id.accessibility_button));
  35. mButtonDispatchers.put(R.id.rotate_suggestion,
  36. new ButtonDispatcher(R.id.rotate_suggestion));
  37. mButtonDispatchers.put(R.id.menu_container,
  38. new ButtonDispatcher(R.id.menu_container));
  39. mDeadZone = new DeadZone(this);
  40. }
  1. private void reloadNavIcons() {
  2. updateIcons(mContext, Configuration.EMPTY, mConfiguration);
  3. }
  1. private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
  2. int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
  3. int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
  4. Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
  5. Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
  6. if (oldConfig.orientation != newConfig.orientation
  7. || oldConfig.densityDpi != newConfig.densityDpi) {
  8. mDockedIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_docked);
  9. mHomeDefaultIcon = getHomeDrawable(lightContext, darkContext);
  10. }
  11. if (oldConfig.densityDpi != newConfig.densityDpi
  12. || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
  13. Log.d("yangxiucheng", "oldConfig.densityDpi != newConfig.densityDpi");
  14. mBackIcon = getBackDrawable(lightContext, darkContext);
  15. mRecentIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_recent);
  16. mMenuIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_menu);
  17. mAccessibilityIcon = getDrawable(lightContext, darkContext,
  18. R.drawable.ic_sysbar_accessibility_button, false /* hasShadow */);
  19. //mImeIcon = getDrawable(lightContext, darkContext, R.drawable.ic_ime_switcher_default,
  20. //false /* hasShadow */);
  21. updateRotateSuggestionButtonStyle(mRotateBtnStyle, false);
  22. if (ALTERNATE_CAR_MODE_UI) {
  23. updateCarModeIcons(ctx);
  24. }
  25. }
  26. mVolumeDown = getDrawable(ctx,R.drawable.ic_sysbar_volume_down,R.drawable.ic_sysbar_volume_down_dark);
  27. mVolumeUp = getDrawable(ctx,R.drawable.ic_sysbar_volume_up,R.drawable.ic_sysbar_volume_up_dark);
  28. }

上述代码都比较简单,可以结合源码看一些细节,就不做太多说明

4.NavigationBarFragment

  1. public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
  2. super.onViewCreated(view, savedInstanceState);
  3. mNavigationBarView = (NavigationBarView) view;
  4. mNavigationBarView.setDisabledFlags(mDisabledFlags1);
  5. mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());
  6. mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
  7. mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
  8. if (savedInstanceState != null) {
  9. mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
  10. }
  11. prepareNavigationBarView();//添加home ,recent触摸事件回调
  12. checkNavBarModes();
  13. setDisabled2Flags(mDisabledFlags2);
  14. IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
  15. filter.addAction(Intent.ACTION_SCREEN_ON);
  16. filter.addAction(Intent.ACTION_USER_SWITCHED);
  17. getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
  18. notifyNavigationBarScreenOn();
  19. mOverviewProxyService.addCallback(mOverviewProxyListener);
  20. }
  1. private void prepareNavigationBarView() {
  2. mNavigationBarView.reorient();
  3. //近期列表安静控制
  4. ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
  5. recentsButton.setOnClickListener(this::onRecentsClick);
  6. recentsButton.setOnTouchListener(this::onRecentsTouch);
  7. recentsButton.setLongClickable(true);
  8. recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
  9. ButtonDispatcher backButton = mNavigationBarView.getBackButton();
  10. backButton.setLongClickable(true);
  11. ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
  12. homeButton.setOnTouchListener(this::onHomeTouch);
  13. homeButton.setOnLongClickListener(this::onHomeLongClick);
  14. ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
  15. accessibilityButton.setOnClickListener(this::onAccessibilityClick);
  16. accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
  17. updateAccessibilityServicesState(mAccessibilityManager);
  18. ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
  19. rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
  20. rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
  21. updateScreenPinningGestures();
  22. }

上述代码中的setOnClickListener,setOnTouchListener,setLongClickable,setOnLongClickListener就是给对应的控件添加控制代码

到现在为止整个导航栏的View的添加,资源图片的加载,以及点击触摸事件的响应逻辑都讲了,其中还有一些细节,结合源码看应该不难,导航栏的讲解也就到此结束。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/219275
推荐阅读
相关标签
  

闽ICP备14008679号