Android之framework修改底部导航栏NavigationBar动态显示和隐藏
来源:互联网 发布:上海游族网络招聘 编辑:程序博客网 时间:2024/05/21 22:59
大家都知道,Android从3.0版本开始就加入了NavigationBar,主要是为那些没有实体按键的设备提供虚拟按键,但是,它始终固定在底部,占用48dp的像素高度,尽管从android 4.4开始可以全透明,使用这一部分像素,但三个按钮始终悬浮在屏幕上,这对于有强迫症的朋友来说是无法忍受的。因此,本文的目的就是修改framework部分代码,可以动态隐藏和显示NavigationBar,同时又尽量不影响系统的正常。
主要思路:
在NavigationBar的布局左部加入一个Button(在SystemUI模块实现),点击隐藏NavigationBar,即将NavigationBar从WindowManager中移除掉。需要的时候,通过一个从屏幕底部向上的滑动手势(在policy模块实现)调出NavigationBar。如下两图对比所示:一张为移除前,另一张为移除后。
具体实现:
①.增加按钮实现动态隐藏,主要修改在frameworks/base/packages/SystemUI模块,首先我们增加一个按钮,主要修改
frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml文件,图片资源和字符串我就不提了,具体如下:
diff --git a/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml b/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xmlindex 16027d9..326aafc 100644--- a/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml+++ b/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml@@ -42,12 +42,28 @@ > + + <framelayout android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_extra_key_width">+ ++ </framelayout>+ + + <framelayout android:layout_height="40dp" android:layout_weight="0" android:layout_width="match_parent">++ + </framelayout>+
接下来修改frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java,为按钮提供一个接口,具体如下:
diff --git a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.javaindex 88e71e2..7545984 100644--- a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java+++ b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java@@ -45,6 +45,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.DelegateViewHelper; import com.android.systemui.statusbar.policy.DeadZone;+import com.android.systemui.statusbar.policy.KeyButtonRipple; import com.android.systemui.statusbar.policy.KeyButtonView; import java.io.FileDescriptor;@@ -265,6 +266,13 @@ public class NavigationBarView extends LinearLayout { public View getImeSwitchButton() { return mCurrentView.findViewById(R.id.ime_switcher); }+ //BEGIN liweiping+ public View getHideBarButton() {+ View view = mCurrentView.findViewById(R.id.hide_bar_btn);+ view.setBackground(new KeyButtonRipple(getContext(), view));+ return view;+ }+ //END liweiping private void getIcons(Resources res) { mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);@@ -412,7 +420,6 @@ public class NavigationBarView extends LinearLayout { mCurrentView = mRotatedViews[Surface.ROTATION_0]; getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);- updateRTLOrder(); }
最后便是在frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java实现点击事件了:
+ private final OnClickListener mHideBarClickListener = new OnClickListener() {+ @Override+ public void onClick(View view) {+ Log.i(way, mHideBarClickListener onClick...);+ removeNavigationBar();+ }+ };+ private void removeNavigationBar() {+ if (DEBUG) Log.d(TAG, removeNavigationBar: about to remove + mNavigationBarView);+ if (mNavigationBarView == null) return;++ mWindowManager.removeView(mNavigationBarView);+ mNavigationBarView = null;+ }
到此,隐藏NavigationBar告一段落了。
②.接下来便是显示NavigationBar,这个修改相对复杂一点。因为此时NavigationBar处于不可见状态,我们无法通过增加按钮的方式让其显示,但是我们知道,状态栏下拉通过手势向下滑动即可。因此很容易便想到通过手势从屏幕底部向上滑动来显示NavigationBar。我的想法是在policy模块中增加一个接口,通过frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java服务传递到状态栏中,从而触发显示NavigationBar事件。
也许大家会有疑问,为什么是在policy模块修改?其实我这只是一种解决方案,因为我知道
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java 有现成的手势滑动接口。其实你也可以SystemUI中增加一个这样的事件,我们需要的就是这么一个触发事件。
PhoneWindowManager.java的修改主要是实现onSwipeFromBottom(竖屏时)和onSwipeFromRight(横屏时)两个接口,然后调用showNavigationBar,在showNavigationBar函数中,我们调用StatusBarManagerService服务中的showNavigationBar函数,具体如下:
diff --git a/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.javaindex bb53e12..907202d 100644--- a/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java+++ b/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java@@ -1241,13 +1241,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void onSwipeFromBottom() { if (mNavigationBar != null && mNavigationBarOnBottom) { requestTransientBars(mNavigationBar);+ Log.i(way, onSwipeFromBottom... mNavigationBar != null && mNavigationBarOnBottom); }+ //BEGIN liweiping+ else{+ Log.i(way, onSwipeFromBottom...);+ showNavigationBar();+ }+ //END liweiping } @Override public void onSwipeFromRight() { if (mNavigationBar != null && !mNavigationBarOnBottom) { requestTransientBars(mNavigationBar);+ Log.i(way, onSwipeFromRight... mNavigationBar != null && !mNavigationBarOnBottom);+ }+ //BEGIN liweiping+ else{+ Log.i(way, onSwipeFromRight...);+ showNavigationBar(); }+ //END liweiping } @Override public void onDebug() {@@ -1293,7 +1307,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { goingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER); } }-+ //BEGIN liweiping+ private void showNavigationBar(){+ mHandler.post(new Runnable() {+ @Override+ public void run() {+ try {+ IStatusBarService statusbar = getStatusBarService();+ if (statusbar != null) {+ statusbar.showNavigationBar();+ }+ } catch (RemoteException e) {+ // re-acquire status bar service next time it is needed.+ mStatusBarService = null;+ }+ }+ });+ }+ //END liweiping private void updateKeyAssignments() { final boolean hasMenu = (mDeviceHardwareKeys & KEY_MASK_MENU) != 0; final boolean hasHome = (mDeviceHardwareKeys & KEY_MASK_HOME) != 0;
这时事件传递到了StatusBarManagerService中,我们来看看StatusBarManagerService.java如何实现showNavigationBar:
diff --git a/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.javaindex f85e2d9..3f75840 100644--- a/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java+++ b/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java@@ -366,6 +366,27 @@ public class StatusBarManagerService extends IStatusBarService.Stub { WindowManager.LayoutParams); } }+ //BEGIN liweiping+ @Override+ public void showNavigationBar() {+ enforceStatusBar();++ android.util.Log.d(way, TAG + showNavigationBar...);++ synchronized(mLock) {+ mHandler.post(new Runnable() {+ public void run() {+ if (mBar != null) {+ try {+ mBar.showNavigationBar();+ } catch (RemoteException ex) {+ }+ }+ }+ });+ }+ }+ //END liweiping private void updateUiVisibilityLocked(final int vis, final int mask) { if (mSystemUiVisibility != vis) {
从上述代码可以看出,StatusBarManagerService只是起到一个传递作用,将消息传递到StatusBar中,最终的实现是在SystemUI模块的frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java,如下所示:
diff --git a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.javaindex 9db875f..4f24b6e 100644--- a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java+++ b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java@@ -56,6 +56,7 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_BUZZ_BEEP_BLINKED = 15 << MSG_SHIFT; private static final int MSG_NOTIFICATION_LIGHT_OFF = 16 << MSG_SHIFT; private static final int MSG_NOTIFICATION_LIGHT_PULSE = 17 << MSG_SHIFT;+ private static final int MSG_SHOW_NAVIGATIONBAR = 18 << MSG_SHIFT;//ADD liweiping public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;@@ -83,6 +84,7 @@ public class CommandQueue extends IStatusBar.Stub { public void animateCollapsePanels(int flags); public void animateExpandSettingsPanel(); public void setSystemUiVisibility(int vis, int mask);+ public void showNavigationBar();//ADD liweiping public void topAppWindowChanged(boolean visible); public void setImeWindowStatus(IBinder token, int vis, int backDisposition, boolean showImeSwitcher);@@ -154,6 +156,14 @@ public class CommandQueue extends IStatusBar.Stub { mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget(); } }+ //BEGIN liweiping+ public void showNavigationBar() {+ synchronized (mList) {+ mHandler.removeMessages(MSG_SHOW_NAVIGATIONBAR);+ mHandler.sendEmptyMessage(MSG_SHOW_NAVIGATIONBAR);+ }+ }+ //END liweiping public void topAppWindowChanged(boolean menuVisible) { synchronized (mList) {@@ -283,6 +293,11 @@ public class CommandQueue extends IStatusBar.Stub { case MSG_SET_SYSTEMUI_VISIBILITY: mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2); break;+ //BEGIN liweiping+ case MSG_SHOW_NAVIGATIONBAR:+ mCallbacks.showNavigationBar();+ break;+ //END liweiping case MSG_TOP_APP_WINDOW_CHANGED: mCallbacks.topAppWindowChanged(msg.arg1 != 0); break;
CommandQueue.java收到了这个消息之后,又回调给了base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java,绕了大半天,消息终于回来了,我们就是需要在PhoneStatusBar.java实现显示NavigationBar的函数了:
+ @Override // CommandQueue+ public void showNavigationBar() {+ Log.i(way, TAG + showNavigationBar...);+ forceAddNavigationBar();+ }+ private void forceAddNavigationBar() {+ // If we have no Navbar view and we should have one, create it+ if (mNavigationBarView != null) {+ return;+ }++ mNavigationBarView =+ (NavigationBarView) View.inflate(mContext, R.layout.navigation_bar, null);+ mNavigationBarView.setDisabledFlags(mDisabled);+ mNavigationBarView.setBar(this);+ addNavigationBar(true); // dynamically adding nav bar, reset System UI visibility!+ }+ private void prepareNavigationBarView(boolean forceReset) {+ mNavigationBarView.reorient();+ mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);+ mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);+ mNavigationBarView.getRecentsButton().setLongClickable(true);+ mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);+ mNavigationBarView.getBackButton().setLongClickable(true);+ mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);+ mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);+ mNavigationBarView.getHideBarButton().setOnClickListener(mHideBarClickListener);//ADD liweiping++ if (forceReset) {+ // Nav Bar was added dynamically - we need to reset the mSystemUiVisibility and call+ // setSystemUiVisibility so that mNavigationBarMode is set to the correct value+ Log.i(way, prepareNavigationBarView mNavigationBarMode = + mNavigationBarMode + mSystemUiVisibility = + mSystemUiVisibility + mNavigationIconHints = + mNavigationIconHints);+ mNavigationBarMode = 0;+ + int newVal = mSystemUiVisibility;+ mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;+ setSystemUiVisibility(newVal, /*SYSTEM_UI_VISIBILITY_MASK*/0xffffffff);+ int hints = mNavigationIconHints;+ mNavigationIconHints = 0;+ setNavigationIconHints(hints);+ topAppWindowChanged(mShowMenu);+ }++ updateSearchPanel();+ }++ // For small-screen devices (read: phones) that lack hardware navigation buttons+ private void addNavigationBar(boolean forceReset) {+ if (DEBUG) Log.v(TAG, addNavigationBar: about to add + mNavigationBarView);+ if (mNavigationBarView == null) return;++ prepareNavigationBarView(forceReset);++ mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());+ }+ //END liweiping
需要注意的是:
①显示NavigationBar时,需要重新实例化一次NavigationBarView,我之前有试过移除NavigationBarView后未置空,下次添加时直接使用,会出现状态栏重启的情况,具体原因未知,log显示动画播放错误之类。
②重新添加NavigationBarView时需要恢复NavigationBarView之前的状态,比如说隐藏前时是透明的、显示输入法按钮、菜单键等等。③本文是在Android5.0的代码上修改的,其他版本未验证。
④本文仅是提供一种思路,并非最优方案。
- Android之framework修改底部导航栏NavigationBar动态显示和隐藏
- Android之framework修改底部导航栏NavigationBar动态显示和隐藏
- Android之framework修改底部导航栏NavigationBar动态显示和隐藏
- Android之framework修改底部导航栏NavigationBar动态显示和隐藏
- Android 底部导航栏动态显示和隐藏(上滑,下拉)
- 隐藏导航栏底部横线 隐藏navigationBar底部的线
- Android4.2.2 动态显示隐藏屏幕底部的导航栏(对系统源码进行修改)
- android APP隐藏NavigationBar,通过修改framework隐藏/显示 navigation bar
- Android开发 之 动态显示和隐藏状态栏和导航栏
- android系统定制开发动态显示隐藏虚拟按键虚拟导航Navigationbar
- Android 4.2 通过修改FrameWork源码实现动态隐藏导航栏,实现全屏
- Android 动态隐藏显示导航栏,状态栏
- 开发中导航栏底部的黑线显示和隐藏
- 上拉隐藏/下拉显示 头部和底部导航栏
- Android 删除隐藏NavigationBar (虚拟导航栏)
- Android 自定义底部导航栏和动态添加fragment
- Android 自定义底部导航栏和动态添加fragment
- 隐藏手机下方的底部导航条NavigationBar
- Fragment的通信问题, 新建Fragment为何不要在构造方法中传递参数
- DTP中过滤写例程 取今天
- Fleet Commander项目启动流程
- 用Margin还是用Padding(转)
- android 过场动画如何加载
- Android之framework修改底部导航栏NavigationBar动态显示和隐藏
- blogs
- AJAX异步局部更新简单案列
- 查看linux是否是虚拟机
- 使用NSURLSession发起HTTPS网络请求
- 每周数据结构【2】:删除带头结点的含有x的值的节点
- ruby引入shell命令
- 堆排序
- TOJ 2821.Grouping Problem