赞
踩
NavigationBar可以手动隐藏,随着华为荣耀手机有了这个特点后,目前有很多android手机都有该特性。如下截图所示:
上图的底部虚拟导航按钮的左、右边有两个按钮点击这个按钮,虚拟按钮就会消失,当从屏幕底部向上滑动时候,虚拟按钮就就会出现,这个消失和出现的过程中整个屏幕的布局都会从新计算。
接下来,分下面几个点来说一下具体实现的效果。
在systemui这个app里面有一个类PhoneStatusBar,在这个类被创建的时候会判断当前系统是否定义了虚拟按钮(NavigationBar),如果定义了就添加,否则不添加。源码如下。
判断是否有定义虚拟按钮(NavigationBar):
boolean showNav = mWindowManagerService.hasNavigationBar();
创建虚拟按钮(NavigationBar)的代码:
- int layoutId = R.layout.navigation_bar;
- if(RecentsActivity.FLOAT_WINDOW_SUPPORT){
- layoutId = R.layout.navigation_bar_float_window;
- }
- mNavigationBarView =
- (NavigationBarView) View.inflate(context, /*R.layout.navigation_bar*/layoutId, null);
很显然mNavigationBarView这个view就是显示虚拟按钮(NavigationBar)的。
最后在systemui的这个类PhoneStatusBar里面通过如下方法把mNavigationBarView显示出来(添加到系统中):
mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
隐藏的方法就可以通过 mWindowManager来removeView方法来实现
。实验结果是:这是一个不错的选择。
具体如何实现呢?首先通过上面的mNavigationBarView的布局文件(R.layout.navigation_bar)可以知道,我们可以重写这个布局文件,在重写的布局文件中添加两个隐藏按钮,这个很简单。其次还有个问题,就是在systemui里面隐藏了虚拟按钮(NavigationBar,如何通知WindowManager,这个可以通过定义一个新的KEYCODE,单点击这个隐藏按钮时候发出这个特殊的KEYCODE的按键事件。
(二):如何实现从底部滑动时候能够显示已经隐藏的虚拟按钮(NavigationBar),当然在横屏的时候,是从左、右边缘滑动来显示已经隐藏的虚拟按(NavigationBar) 通过对源码的分析发现,在PhoneWindowManager这个类里面可以找到蛛丝马迹。
在包 com.android.internal.policy.impl里面有PhoneWindowManager类,这是一个主要管理手机显示系统的所有window的类,我们知道在android中每个显示的界面其实都是一个window,比如一个activity的显示界面、一个Dialog弹出框、还有上面提到的 虚拟按钮(NavigationBar)、包括下拉状态栏、已经锁屏界面等等,通过hierarchyviewer.bat这个工具就可以看到当前手机正在显示的所有window.
当然PhoneWindowManager类还处理一些其他的事情,比如按键事件的处理(按home回到待机界面)、锁屏的触发等等,不过我觉得其实都是管理的是window的切换。
在这个PhoneWindowManager类里面,你会发现一个有用的对象:mSystemGestures。从名字可以发现这是一个系统手势的类。没错,你能够从手机屏幕顶部下滑拉出系统状态栏就是靠他了。
我们就需要修改这个类,使得手机可以发现我们从下往上滑动的手势(从左、右边边缘滑动),这样就可以在 PhoneWindowManager里面收到这个我们需要的手势了。
这样一来,在PhoneWindowManager里面收到显示虚拟按钮(NavigationBar)的手势,就想办法去显示虚拟按钮(NavigationBar)就可以了。在PhoneWindowManager中要显示虚拟按钮(NavigationBar)的办法和显示下拉状态栏类似,你需要在systemui里面定义相应的方法,然后在PhoneWindowManager里面通过如下代码获取StatusBarService:
IStatusBarService statusbar = getStatusBarService();
最后通过StatusBarService来调用在PhoneStatusBar里面定义的显示虚拟按钮(NavigationBar)的方法。
如下是源码:
PhoneWindowManager显示虚拟按钮(NavigationBar)的函数:
- private void showNavigationBar(final int type){
-
- if(isKeyguardLocked()){
- return;
- }
- startToShowNavbar = true;
- //Slog.i("yu_PhoneWindowManager", "showNavigationBar: NavigationBarMoveType="+NavigationBarMoveType);
-
- NavigationBarMoveType = type;
- if(mNavigationBar == null) {
-
- Slog.i("yu_PhoneWindowManager", "RAMOS showNavigationBar: showNavigationBar");
- //Log.v("NavigationGuard", "RAMOS showNavigationBar startToShowNavbar="+startToShowNavbar);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IStatusBarService statusbar = getStatusBarService();
- if (statusbar != null) {
- //Slog.i("yu_PhoneWindowManager", "showNavigationBar");
- statusbar.showNavigationBar(type);
- }
- } catch (RemoteException e) {
- // re-acquire status bar service next time it is needed.
- mStatusBarService = null;
- }
- }
- });
- }
-
- }
注意:在PhoneWindowManager类里面可以通过判断这个对象mNavigationBar是否为空来确认 虚拟按钮(NavigationBar)是否被隐藏。
其他的showNavigationBar方法的声明和定义你只需仿照hideRecentApps来做就可以了。
最后在PhoneStatusBar里面实现具体的虚拟按钮(NavigationBar)显示即可。
比如,下面是我的实现方法:
- @Override // CommandQueue
- public void showNavigationBar(int type) {
- //Log.i("way", TAG + " showNavigationBar...");
- Log.i("yu_PhoneStatusBar", "showNavigationBar type="+type);
-
- if (mNavigationBarView != null) {
- try {
- mWindowManagerService.StartToShowNavbar(type);
- } catch (RemoteException ex) {
- }
-
- return;
- }
-
- if(mTempNavigationBarView == null){
- makeNewNavigationBar();
- }
-
- if(mTempNavigationBarView != null){
- mNavigationBarView = mTempNavigationBarView;
- //mNavigationBarView.setVisibility(View.VISIBLE);
-
- mWindowManager.addView(mNavigationBarView, mNavigationBarLayoutParams);
- prepareNavigationBarView(true);
- mNavigationBarView.setDisabledFlags(mDisabled);
-
- //mNavigationBarView.reorient();
- //mNavigationBarView.notifyScreenOn(true);
- mTempNavigationBarView = null;
- }else{
- Log.i("yu_PhoneStatusBar", "showNavigationBar: ERROR");
- }
- }
重要是这这一个语句:
mWindowManager.addView(mNavigationBarView, mNavigationBarLayoutParams);
注意:每次通过mWindowManager来removeView掉NavigationBarView后,这个刚刚被remove的NavigationBarView是不能再次利用的,下次还使用这个NavigationBarView会报错。
(三)如何实现,在设置里面去配置虚拟按钮(NavigationBar)的排序。如下图:
要解决这个问题,需要修改下面三个地方:
1:在frameworks/base/core/java/android/provider/Settings.java里面添加如此代码:
public static final String RAMOS_NAVBAR_STYLE = "RAMOS_NAVBAR_STYLE";
我们就可以通过RAMOS_NAVBAR_STYLE 来保存我们虚拟按钮(NavigationBar)配置的排序了。
2:在设置中app中,添加一个设置的界面重写SettingsPreferenceFragment来实现,配置自己的Preferences的xml文件,其中的RadioPreferences需要自己重写CheckBoxPreference来完成:
如下是我写的CheckBoxPreference的RamosRadioNavbarStylePreference关键代码:
- public RamosRadioNavbarStylePreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
- }
- void setOnClickListener(OnClickListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onClick() {
- if (mListener != null) {
- mListener.onRadioButtonClicked(this);
- }
- }
-
- @Override
- protected void onBindView(View view) {
- super.onBindView(view);
- mViewGroup = view;
- TextView title = (TextView) view.findViewById(android.R.id.title);
- if (title != null) {
- title.setSingleLine(false);
- title.setMaxLines(3);
- }
- UpdateNavbarStyle(view);
- }
- public void setNavbarStyle(int style){
- if(style > -1 && mNavbarStyle != style){
- mNavbarStyle = style;
- notifyChanged();//UpdateNavbarStyle(mViewGroup);
- }
- }
-
- private void UpdateNavbarStyle(View view){
- if(mNavbarStyle < 0){
- return;
- }
- ImageView tempview;
- switch(mNavbarStyle){
- case NAV_BAR_STYLE_0:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_back);//ic_sysbar_back_right
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_recent);
- break;
- case NAV_BAR_STYLE_1:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_recent); // ic_sysbar_back ic_sysbar_recent
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_back_right); //ic_sysbar_back_right ic_sysbar_recent
- break;
-
- case NAV_BAR_STYLE_2:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_back);//ic_sysbar_back_right
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_recent);
- break;
- case NAV_BAR_STYLE_3:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_back);//ic_sysbar_back_right
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_recent);
- break;
- case NAV_BAR_STYLE_4:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_recent); // ic_sysbar_back ic_sysbar_recent
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_back_right); //ic_sysbar_back_right ic_sysbar_recent
- break;
- case NAV_BAR_STYLE_5:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.VISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_recent); // ic_sysbar_back ic_sysbar_recent
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_back_right); //ic_sysbar_back_right ic_sysbar_recent
- break;
- case NAV_BAR_STYLE_6:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_back);//ic_sysbar_back_right
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_recent);
- break;
- case NAV_BAR_STYLE_7:
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_left);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.ramos_hide_navbar_right);
- tempview.setVisibility(View.INVISIBLE);
-
- tempview = (ImageView) view.findViewById(R.id.back);
- tempview.setImageResource(R.drawable.ic_sysbar_recent); // ic_sysbar_back ic_sysbar_recent
-
- tempview = (ImageView) view.findViewById(R.id.recent_apps);
- tempview.setImageResource(R.drawable.ic_sysbar_back_right); //ic_sysbar_back_right ic_sysbar_recent
- break;
- default:
- break;
-
- }
-
-
- }
Settings.System.putInt(mActivity.getContentResolver(), Settings.System.RAMOS_NAVBAR_STYLE, style);
(3)最后在NavigationBarView里面来实现其配置的虚拟按钮(NavigationBar):
在NavigationBarView中通过ContentObserver来监控RAMOS_NAVBAR_STYLE值得变化,一旦变化,就会从新排序NavigationBarView中各个按钮的显示。
这里,我是通过替换他们View的ID、ImageResourc和KeyCode以及他们的长按短按事件ClickLisener。
到这里就算完结了,不过有两个问题需要解决:
其一:在输入法的弹出框出现的时候,来回隐藏和显示虚拟按钮(NavigationBar),会看到输入框下面有黑色背景或者显示不全等问题,如何解决呢?
其实这是因为输入法弹出框比较独立,没有能够试试刷新和布局造成的。解决办法是:在包com.android.internal.policy.impl的PhoneWindow类有一个方法:
private void updateNavigationGuard(WindowInsets insets)
在这个方法里面只需要做到每次虚拟按钮(NavigationBar)有变化时候调用该方法即可:requestFitSystemWindows();
其二:每次要显示虚拟按钮(NavigationBar)时候,从底部往上滑动时候会触发屏幕中其他view的click或者move触摸事件,造成误点击了某个图标,滑动了一些菜单等问题,如何破解?
首先在PhoneWindow中拦截不需要的触摸操作,在PhoneWindow的DecorView中的onInterceptTouchEvent里面把不需要的触摸事件return true即可。
注意:DecorView是所有activity的显示view的父view.
这里难点就是如何判断一个触摸事件是不需要的,也就是说如何判断一个触摸事件是要显示虚拟按钮(NavigationBar)的 ,如果一个使用的操作(手势)是来显示虚拟按钮(NavigationBar)的,那么这个操作就不要用来做其他的,就可以把这次触摸操作当成不需要的操作了。因为通常情况下,你不可能又要显示虚拟按钮(NavigationBar),又要点击一个其他界面的按钮。
如何判断一个触摸(手势)是来显示虚拟按钮(NavigationBar)的呢? 这里需要通过上面说到的PhoneWindowManager和SystemGesturesPointerEventListener了。
大致的办法是:
在SystemGesturesPointerEventListener识别显示虚拟按钮(NavigationBar)的手势,从底部滑动、从左、右边滑动,这里有一个要求,就是要尽快的识别出来,希望能在滑动的前3个MotionEvent事件识别出来,原来的从顶部往下滑动的手势识别需要6个MotionEvent事件以上,这是不够的。
然后在PhoneWindowManager定义一个正在开始显示虚拟按钮(NavigationBar)的boolean startToShowNavbar变量,并定义一个public方法来判断startToShowNavbar的状态。
- private static boolean startToShowNavbar = false;
- //private static final int KEY_CODE_RAMOS_HIDE_NAVBAR = 1994;
-
- @Override
- public boolean hasShowingNavbar() {
- //Log.v("NavigationGuard", "RAMOS hasShowingNavbar startToShowNavbar="+startToShowNavbar);
- return startToShowNavbar;
- }
如何复位
startToShowNavbar这个变量呢,就是说如何判断显示虚拟按钮(NavigationBar)已经完成呢?在PhoneWindowManager的方法layoutWindowLw被调用,并且在
layoutWindowLw中出现了TYPE_NAVIGATION_BAR,就复位。如下修改的截图:
最后在PhoneWindow的DecorView中的onInterceptTouchEvent判断即可了,如下源码:
- private boolean getShowIngNavBar() {
- try {
- return WindowManagerHolder.sWindowManager.hasShowingNavbar();
- } catch (RemoteException ex) {
- Log.e(TAG, "RAMOS getShowIngNavBar:", ex);
- return false;
- }
- }
完毕!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。