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

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

NavigationBarFragment.create

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,WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_SLIPPERY,PixelFormat.TRANSLUCENT);lp.token = new Binder();lp.setTitle("NavigationBar");lp.accessibilityTitle = context.getString(R.string.nav_bar);lp.windowAnimations = 0;View navigationBarView = LayoutInflater.from(context).inflate(R.layout.navigation_bar_window, null);if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);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).commit();fragmentHost.addTagListener(TAG, listener);return navigationBarView;}

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

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

navigation_bar.xml

<com.android.systemui.statusbar.phone.NavigationBarViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:systemui="http://schemas.android.com/apk/res-auto"android:layout_height="match_parent"android:layout_width="match_parent"android:background="@drawable/system_bar_background"><com.android.systemui.statusbar.phone.NavigationBarInflaterViewandroid:id="@+id/navigation_inflater"android:layout_width="match_parent"android:layout_height="match_parent" /></com.android.systemui.statusbar.phone.NavigationBarView>

我们先看NavigationBarInflaterView

 public NavigationBarInflaterView(Context context, AttributeSet attrs) {super(context, attrs);createInflaters();mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();Mode displayMode = mDisplay.getMode();isRot0Landscape = displayMode.getPhysicalWidth() > displayMode.getPhysicalHeight();mOverviewProxyService = Dependency.get(OverviewProxyService.class);}private void createInflaters() {mLayoutInflater = LayoutInflater.from(mContext);Configuration landscape = new Configuration();landscape.setTo(mContext.getResources().getConfiguration());landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));}@Overrideprotected void onFinishInflate() {super.onFinishInflate();inflateChildren();//添加mRot0和mRot90,就是横竖屏的布局clearViews();//清空所有的viewinflateLayout(getDefaultLayout());}private void inflateChildren() {removeAllViews();mRot0 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout, this, false);mRot0.setId(R.id.rot0);addView(mRot0);mRot90 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_rot90, this,false);mRot90.setId(R.id.rot90);addView(mRot90);updateAlternativeOrder();}

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

 protected void inflateLayout(String newLayout) {mCurrentLayout = 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, mRot0.findViewById(R.id.ends_group), isRot0Landscape, true);inflateButtons(start, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, true);inflateButtons(center, mRot0.findViewById(R.id.center_group), isRot0Landscape, false);inflateButtons(center, mRot90.findViewById(R.id.center_group), !isRot0Landscape, false);addGravitySpacer(mRot0.findViewById(R.id.ends_group));addGravitySpacer(mRot90.findViewById(R.id.ends_group));inflateButtons(end, mRot0.findViewById(R.id.ends_group), isRot0Landscape, false);inflateButtons(end, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, false);updateButtonDispatchersCurrentView();}
 protected String getDefaultLayout() {final int defaultResource = mOverviewProxyService.shouldShowSwipeUpUI()? R.string.config_navBarLayoutQuickstep: R.string.config_navBarLayout;return mContext.getString(defaultResource);}
 protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,boolean start) {LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;View v = createView(buttonSpec, parent, inflater);if (v == null) return null;v = applySize(v, buttonSpec, landscape, start);//计算view的宽的大小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;}
 private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {View v = null;String button = extractButton(buttonSpec);if (LEFT.equals(button)) {String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE);button = extractButton(s);} else if (RIGHT.equals(button)) {String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME_ROTATE);button = extractButton(s);}// Let plugins go first so they can override a standard view if they want.for (NavBarButtonProvider provider : mPlugins) {v = provider.createView(buttonSpec, parent);if (v != null) return v;}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);} //laiyw add for volume Up/Downelse if (VOLUME_DOWN.equals(button)) {v = inflater.inflate(R.layout.volume_down, parent, false);}else if (VOLUME_UP.equals(button)) {v = inflater.inflate(R.layout.volume_up, parent, false);//laiyw add for volume Up/Down} else if (RECENT.equals(button)) {v = inflater.inflate(R.layout.recent_apps, parent, false);} else if (MENU_IME_ROTATE.equals(button)) {v = inflater.inflate(R.layout.menu_ime, parent, false);} else if (NAVSPACE.equals(button)) {v = inflater.inflate(R.layout.nav_key_space, parent, false);} else if (CLIPBOARD.equals(button)) {v = inflater.inflate(R.layout.clipboard, parent, false);} else if (CONTEXTUAL.equals(button)) {v = inflater.inflate(R.layout.contextual, parent, false);} else if (button.startsWith(KEY)) {String uri = extractImage(button);int code = extractKeycode(button);v = inflater.inflate(R.layout.custom_key, parent, false);((KeyButtonView) v).setCode(code);if (uri != null) {if (uri.contains(":")) {((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));} else if (uri.contains("/")) {int index = uri.indexOf('/');String pkg = uri.substring(0, index);int id = Integer.parseInt(uri.substring(index + 1));((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));}}}return v;}

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

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

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

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

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

home.xml

<com.android.systemui.statusbar.policy.KeyButtonViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:systemui="http://schemas.android.com/apk/res-auto"android:id="@+id/home"android:layout_width="@dimen/navigation_key_width"android:layout_height="match_parent"android:layout_weight="0"systemui:keyCode="3"android:scaleType="center"android:contentDescription="@string/accessibility_home"android:paddingStart="@dimen/navigation_key_padding"android:paddingEnd="@dimen/navigation_key_padding"/>

back.xml

<com.android.systemui.statusbar.policy.KeyButtonViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:systemui="http://schemas.android.com/apk/res-auto"android:id="@+id/back"android:layout_width="@dimen/navigation_key_width"android:layout_height="match_parent"android:layout_weight="0"systemui:keyCode="4"android:scaleType="center"android:contentDescription="@string/accessibility_back"android:paddingStart="@dimen/navigation_key_padding"android:paddingEnd="@dimen/navigation_key_padding"/>

recent_app.xml

<com.android.systemui.statusbar.policy.KeyButtonViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:systemui="http://schemas.android.com/apk/res-auto"android:id="@+id/recent_apps"android:layout_width="@dimen/navigation_key_width"android:layout_height="match_parent"android:layout_weight="0"android:scaleType="center"android:contentDescription="@string/accessibility_recent"android:paddingStart="@dimen/navigation_key_padding"android:paddingEnd="@dimen/navigation_key_padding"/>

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

答案是:

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

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

3.NavigationBarView

    public NavigationBarView(Context context, AttributeSet attrs) {super(context, attrs);mDisplay = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();mVertical = false;mShowMenu = false;mShowAccessibilityButton = false;mLongClickableAccessibilityButton = false;mOverviewProxyService = Dependency.get(OverviewProxyService.class);mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);mConfiguration = new Configuration();mConfiguration.updateFrom(context.getResources().getConfiguration());M : New plugin architecture - Navigation bar plugintry {mSystemUICustomizationFactory =OpSystemUICustomizationFactoryBase.getOpFactory(context);mNavBarPlugin = mSystemUICustomizationFactory.makeNavigationBar(context);} catch (Exception e) {if (DEBUG) Log.d(TAG,"mNavBarPlugin init fail");e.printStackTrace();}reloadNavIcons();//初始化加载资源,主要是图片。mBarTransitions = new NavigationBarTransitions(this);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));mButtonDispatchers.put(R.id.volume_down, new ButtonDispatcher(R.id.volume_down));    mButtonDispatchers.put(R.id.volume_up, new ButtonDispatcher(R.id.volume_up));getVolumeDownButton().setLongClickable(false);getVolumeUpButton().setLongClickable(false);mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));mButtonDispatchers.put(R.id.accessibility_button,new ButtonDispatcher(R.id.accessibility_button));mButtonDispatchers.put(R.id.rotate_suggestion,new ButtonDispatcher(R.id.rotate_suggestion));mButtonDispatchers.put(R.id.menu_container,new ButtonDispatcher(R.id.menu_container));mDeadZone = new DeadZone(this);}
private void reloadNavIcons() {updateIcons(mContext, Configuration.EMPTY, mConfiguration);}
 private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);if (oldConfig.orientation != newConfig.orientation|| oldConfig.densityDpi != newConfig.densityDpi) {mDockedIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_docked);mHomeDefaultIcon = getHomeDrawable(lightContext, darkContext);}if (oldConfig.densityDpi != newConfig.densityDpi|| oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {Log.d("yangxiucheng", "oldConfig.densityDpi != newConfig.densityDpi");mBackIcon = getBackDrawable(lightContext, darkContext);mRecentIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_recent);mMenuIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_menu);mAccessibilityIcon = getDrawable(lightContext, darkContext,R.drawable.ic_sysbar_accessibility_button, false /* hasShadow */);//mImeIcon = getDrawable(lightContext, darkContext, R.drawable.ic_ime_switcher_default,//false /* hasShadow */);updateRotateSuggestionButtonStyle(mRotateBtnStyle, false);if (ALTERNATE_CAR_MODE_UI) {updateCarModeIcons(ctx);}}mVolumeDown = getDrawable(ctx,R.drawable.ic_sysbar_volume_down,R.drawable.ic_sysbar_volume_down_dark);mVolumeUp = getDrawable(ctx,R.drawable.ic_sysbar_volume_up,R.drawable.ic_sysbar_volume_up_dark);}

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

4.NavigationBarFragment

  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mNavigationBarView = (NavigationBarView) view;mNavigationBarView.setDisabledFlags(mDisabledFlags1);mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);mNavigationBarView.setOnTouchListener(this::onNavigationTouch);if (savedInstanceState != null) {mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);}prepareNavigationBarView();//添加home  ,recent触摸事件回调checkNavBarModes();setDisabled2Flags(mDisabledFlags2);IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_USER_SWITCHED);getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);notifyNavigationBarScreenOn();mOverviewProxyService.addCallback(mOverviewProxyListener);}
 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);ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();homeButton.setOnTouchListener(this::onHomeTouch);homeButton.setOnLongClickListener(this::onHomeLongClick);ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();accessibilityButton.setOnClickListener(this::onAccessibilityClick);accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);updateAccessibilityServicesState(mAccessibilityManager);ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);updateScreenPinningGestures();}

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

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

Android SystemUI之NavigationBar,导航栏(四)相关推荐

  1. SystemUI之NavigationBar导航栏

    SystemUI之NavigationBar导航栏 布局结构 navigation_bar_window.xml à navigation_bar.xml [NavigationBarFrame [N ...

  2. Framework定制系列(一)-----SystemUI NavigationBar导航栏上滑返回Launcher

    1. NavigationBar导航栏上滑解决方案 代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayPolic ...

  3. Android隐藏状态栏、导航栏

    Android隐藏状态栏.导航栏 [java] view plaincopy private void hideStatusNavigationBar(){ if(Build.VERSION.SDK_ ...

  4. android仿微信的activity平滑水平切换动画,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    Android实现简单底部导航栏 Android仿微信滑动切换效果 发布时间:2020-10-09 19:48:00 来源:脚本之家 阅读:96 作者:丶白泽 Android仿微信滑动切换最终实现效果 ...

  5. 史上最完美的Android沉浸式状态导航栏攻略

    前言 最近我在小破站开发一款新App,叫高能链.我是一个完美主义者,所以不管对架构还是UI,我都是比较抠细节的,在状态栏和导航栏沉浸式这一块,我还是踩了挺多坑,费了挺多精力的.这次我将我踩坑,适配各机 ...

  6. Android 检查设备是否存在 导航栏 NavigationBar

    http://blog.csdn.NET/lnb333666/article/details/41821149 目前也没有可靠的方法来检查设备上是否有导航栏.可以使用KeyCharacterMap.d ...

  7. 红橙Darren视频笔记 builder设计模式 navigationbar 导航栏第二版

    1.builder设计模式简介 builder的实际应用的典型案例有AlertDialog和OKHttp 例如 // AlertDialogAlertDialog alertDialog = new ...

  8. Android界面被底部导航栏挡住

    资料 解决android 显示内容被底部导航栏遮挡的问题 AndroidBug5497Workaround Android手机底部NavigationBar挡住界面的解决方法 android 显示内容 ...

  9. android功能导航布局,Android全面屏虚拟导航栏适配

    手机正朝着全面屏的方向演进,与此同时也给开发者带来了很多适配上的新问题,虚拟导航栏就是其中一个.最近在糗百的项目中,就有相关的适配问题,我查阅了目前关于虚拟导航栏适配的相关文章,基本上在全面屏手机里都 ...

最新文章

  1. swig error : Unrecognized option -doxygen Ubuntu 安装 swig-3.0.12
  2. malloc(0)-malloc 0 字节
  3. springboot基于maven多模块项目搭建(直接启动webApplication)
  4. 面试必会系列 - 2.1 MySQL知识点大汇总(基本架构,存储引擎,锁,事务,索引,B+树等等)
  5. mysql有闪回吗_mysql 闪回测试
  6. Automapper 3.2.1以下方法或属性之间的调用不明确
  7. 微信公众号web端关闭本页面
  8. RAID6结构原理详解
  9. 栅栏密码解密——Java实现
  10. 运算符之前加加后加加的运算规则
  11. 作业帮冯雪胡不归问题_【凭谁唤君胡不归作文】作业帮
  12. vue 图片画框获取坐标
  13. Unity新手开发VR项目
  14. (爱斯维尔期刊:遇到问题已解决) 使用elsarticle-harv的style引文格式时报错 mand\Nat@force@numbers{}\NAT@force@numbers
  15. LightGBM综述
  16. 第五章 ERP计划的时间概念
  17. GO语言基础----简易计算器
  18. Antlr Tool与antlr runtime的版本一致性问题
  19. python安装http server轻量应用服务器的方法
  20. 180个非常有用的电脑知识[转贴]

热门文章

  1. Oracle帐户被锁了,怎么解锁
  2. 国产步进电机驱动芯片TMI8420,可pin to pin​替代DRV8825
  3. VB6 简单实现 支付宝二维码扫马支付
  4. CentOS系统重新删除磁盘分区和挂载、开机自启(大于2T做法)
  5. Google翻译API使用案例
  6. 数据,数据元素,数据项,数据对象的区别
  7. MySQL 触发器使用入门
  8. Unity Steam_VR VRTK开发插件自带案例详解(一)
  9. apollo github的说明介绍
  10. Unnecessary escape character: \- no-useless-escape eslint