当前位置:   article > 正文

Android之framework修改底部导航栏NavigationBar动态显示和隐藏_removenavigationbar

removenavigationbar

       大家都知道,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文件,图片资源和字符串我就不提了,具体如下:

  1. diff --git a/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml b/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml
  2. index 16027d9..326aafc 100644
  3. --- a/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml
  4. +++ b/frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml
  5. @@ -42,12 +42,28 @@
  6. >
  7. <!-- navigation controls -->
  8. + <!--BEGIN liweiping
  9. <View
  10. android:layout_width="40dp"
  11. android:layout_height="match_parent"
  12. android:layout_weight="0"
  13. android:visibility="invisible"
  14. />
  15. + -->
  16. + <FrameLayout
  17. + android:layout_width="@dimen/navigation_extra_key_width"
  18. + android:layout_height="match_parent"
  19. + android:layout_weight="0" >
  20. + <ImageButton
  21. + android:id="@+id/hide_bar_btn"
  22. + android:layout_width="@dimen/navigation_extra_key_width"
  23. + android:layout_height="match_parent"
  24. + android:contentDescription="@string/accessibility_hide"
  25. + android:src="@drawable/ic_sysbar_hide"
  26. + />
  27. +
  28. + </FrameLayout>
  29. + <!--END liweiping -->
  30. <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
  31. android:layout_width="@dimen/navigation_key_width"
  32. android:layout_height="match_parent"
  33. @@ -246,12 +262,28 @@
  34. android:layout_weight="0"
  35. android:contentDescription="@string/accessibility_back"
  36. />
  37. + <!--BEGIN liweiping
  38. <View
  39. android:layout_height="40dp"
  40. android:layout_width="match_parent"
  41. android:layout_weight="0"
  42. android:visibility="invisible"
  43. />
  44. + -->
  45. + <FrameLayout
  46. + android:layout_weight="0"
  47. + android:layout_width="match_parent"
  48. + android:layout_height="40dp" >
  49. +
  50. + <ImageButton
  51. + android:id="@+id/hide_bar_btn"
  52. + android:layout_width="match_parent"
  53. + android:layout_height="40dp"
  54. + android:contentDescription="@string/accessibility_hide"
  55. + android:src="@drawable/ic_sysbar_hide_land"
  56. + />
  57. + </FrameLayout>
  58. + <!--END liweiping -->
  59. </LinearLayout>
  60. <!-- lights out layout to match exactly -->

接下来修改frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java,为按钮提供一个接口,具体如下:

  1. 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.java
  2. index 88e71e2..7545984 100644
  3. --- a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
  4. +++ b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
  5. @@ -45,6 +45,7 @@ import com.android.systemui.R;
  6. import com.android.systemui.statusbar.BaseStatusBar;
  7. import com.android.systemui.statusbar.DelegateViewHelper;
  8. import com.android.systemui.statusbar.policy.DeadZone;
  9. +import com.android.systemui.statusbar.policy.KeyButtonRipple;
  10. import com.android.systemui.statusbar.policy.KeyButtonView;
  11. import java.io.FileDescriptor;
  12. @@ -265,6 +266,13 @@ public class NavigationBarView extends LinearLayout {
  13. public View getImeSwitchButton() {
  14. return mCurrentView.findViewById(R.id.ime_switcher);
  15. }
  16. + //BEGIN liweiping
  17. + public View getHideBarButton() {
  18. + View view = mCurrentView.findViewById(R.id.hide_bar_btn);
  19. + view.setBackground(new KeyButtonRipple(getContext(), view));
  20. + return view;
  21. + }
  22. + //END liweiping
  23. private void getIcons(Resources res) {
  24. mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
  25. @@ -412,7 +420,6 @@ public class NavigationBarView extends LinearLayout {
  26. mCurrentView = mRotatedViews[Surface.ROTATION_0];
  27. getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
  28. -
  29. updateRTLOrder();
  30. }



最后便是在frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java实现点击事件了:

  1. + private final OnClickListener mHideBarClickListener = new OnClickListener() {
  2. + @Override
  3. + public void onClick(View view) {
  4. + Log.i("way", "mHideBarClickListener onClick...");
  5. + removeNavigationBar();
  6. + }
  7. + };
  8. + private void removeNavigationBar() {
  9. + if (DEBUG) Log.d(TAG, "removeNavigationBar: about to remove " + mNavigationBarView);
  10. + if (mNavigationBarView == null) return;
  11. +
  12. + mWindowManager.removeView(mNavigationBarView);
  13. + mNavigationBarView = null;
  14. + }



到此,隐藏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函数,具体如下:

  1. 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.java
  2. index bb53e12..907202d 100644
  3. --- a/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
  4. +++ b/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
  5. @@ -1241,13 +1241,27 @@ public class PhoneWindowManager implements WindowManagerPolicy {
  6. public void onSwipeFromBottom() {
  7. if (mNavigationBar != null && mNavigationBarOnBottom) {
  8. requestTransientBars(mNavigationBar);
  9. + Log.i("way", "onSwipeFromBottom... mNavigationBar != null && mNavigationBarOnBottom");
  10. }
  11. + //BEGIN liweiping
  12. + else{
  13. + Log.i("way", "onSwipeFromBottom...");
  14. + showNavigationBar();
  15. + }
  16. + //END liweiping
  17. }
  18. @Override
  19. public void onSwipeFromRight() {
  20. if (mNavigationBar != null && !mNavigationBarOnBottom) {
  21. requestTransientBars(mNavigationBar);
  22. + Log.i("way", "onSwipeFromRight... mNavigationBar != null && !mNavigationBarOnBottom");
  23. + }
  24. + //BEGIN liweiping
  25. + else{
  26. + Log.i("way", "onSwipeFromRight...");
  27. + showNavigationBar();
  28. }
  29. + //END liweiping
  30. }
  31. @Override
  32. public void onDebug() {
  33. @@ -1293,7 +1307,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
  34. goingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
  35. }
  36. }
  37. -
  38. + //BEGIN liweiping
  39. + private void showNavigationBar(){
  40. + mHandler.post(new Runnable() {
  41. + @Override
  42. + public void run() {
  43. + try {
  44. + IStatusBarService statusbar = getStatusBarService();
  45. + if (statusbar != null) {
  46. + statusbar.showNavigationBar();
  47. + }
  48. + } catch (RemoteException e) {
  49. + // re-acquire status bar service next time it is needed.
  50. + mStatusBarService = null;
  51. + }
  52. + }
  53. + });
  54. + }
  55. + //END liweiping
  56. private void updateKeyAssignments() {
  57. final boolean hasMenu = (mDeviceHardwareKeys & KEY_MASK_MENU) != 0;
  58. final boolean hasHome = (mDeviceHardwareKeys & KEY_MASK_HOME) != 0;



这时事件传递到了StatusBarManagerService中,我们来看看StatusBarManagerService.java如何实现showNavigationBar:

  1. 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.java
  2. index f85e2d9..3f75840 100644
  3. --- a/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
  4. +++ b/frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
  5. @@ -366,6 +366,27 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
  6. "WindowManager.LayoutParams");
  7. }
  8. }
  9. + //BEGIN liweiping
  10. + @Override
  11. + public void showNavigationBar() {
  12. + enforceStatusBar();
  13. +
  14. + android.util.Log.d("way", TAG + " showNavigationBar...");
  15. +
  16. + synchronized(mLock) {
  17. + mHandler.post(new Runnable() {
  18. + public void run() {
  19. + if (mBar != null) {
  20. + try {
  21. + mBar.showNavigationBar();
  22. + } catch (RemoteException ex) {
  23. + }
  24. + }
  25. + }
  26. + });
  27. + }
  28. + }
  29. + //END liweiping
  30. private void updateUiVisibilityLocked(final int vis, final int mask) {
  31. if (mSystemUiVisibility != vis) {



从上述代码可以看出,StatusBarManagerService只是起到一个传递作用,将消息传递到StatusBar中,最终的实现是在SystemUI模块的frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java,如下所示:

  1. 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.java
  2. index 9db875f..4f24b6e 100644
  3. --- a/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
  4. +++ b/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
  5. @@ -56,6 +56,7 @@ public class CommandQueue extends IStatusBar.Stub {
  6. private static final int MSG_BUZZ_BEEP_BLINKED = 15 << MSG_SHIFT;
  7. private static final int MSG_NOTIFICATION_LIGHT_OFF = 16 << MSG_SHIFT;
  8. private static final int MSG_NOTIFICATION_LIGHT_PULSE = 17 << MSG_SHIFT;
  9. + private static final int MSG_SHOW_NAVIGATIONBAR = 18 << MSG_SHIFT;//ADD liweiping
  10. public static final int FLAG_EXCLUDE_NONE = 0;
  11. public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
  12. @@ -83,6 +84,7 @@ public class CommandQueue extends IStatusBar.Stub {
  13. public void animateCollapsePanels(int flags);
  14. public void animateExpandSettingsPanel();
  15. public void setSystemUiVisibility(int vis, int mask);
  16. + public void showNavigationBar();//ADD liweiping
  17. public void topAppWindowChanged(boolean visible);
  18. public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
  19. boolean showImeSwitcher);
  20. @@ -154,6 +156,14 @@ public class CommandQueue extends IStatusBar.Stub {
  21. mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
  22. }
  23. }
  24. + //BEGIN liweiping
  25. + public void showNavigationBar() {
  26. + synchronized (mList) {
  27. + mHandler.removeMessages(MSG_SHOW_NAVIGATIONBAR);
  28. + mHandler.sendEmptyMessage(MSG_SHOW_NAVIGATIONBAR);
  29. + }
  30. + }
  31. + //END liweiping
  32. public void topAppWindowChanged(boolean menuVisible) {
  33. synchronized (mList) {
  34. @@ -283,6 +293,11 @@ public class CommandQueue extends IStatusBar.Stub {
  35. case MSG_SET_SYSTEMUI_VISIBILITY:
  36. mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
  37. break;
  38. + //BEGIN liweiping
  39. + case MSG_SHOW_NAVIGATIONBAR:
  40. + mCallbacks.showNavigationBar();
  41. + break;
  42. + //END liweiping
  43. case MSG_TOP_APP_WINDOW_CHANGED:
  44. mCallbacks.topAppWindowChanged(msg.arg1 != 0);
  45. break;



CommandQueue.java收到了这个消息之后,又回调给了base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java,绕了大半天,消息终于回来了,我们就是需要在PhoneStatusBar.java实现显示NavigationBar的函数了:

  1. + @Override // CommandQueue
  2. + public void showNavigationBar() {
  3. + Log.i("way", TAG + " showNavigationBar...");
  4. + forceAddNavigationBar();
  5. + }
  6. + private void forceAddNavigationBar() {
  7. + // If we have no Navbar view and we should have one, create it
  8. + if (mNavigationBarView != null) {
  9. + return;
  10. + }
  11. +
  12. + mNavigationBarView =
  13. + (NavigationBarView) View.inflate(mContext, R.layout.navigation_bar, null);
  14. + mNavigationBarView.setDisabledFlags(mDisabled);
  15. + mNavigationBarView.setBar(this);
  16. + addNavigationBar(true); // dynamically adding nav bar, reset System UI visibility!
  17. + }
  18. + private void prepareNavigationBarView(boolean forceReset) {
  19. + mNavigationBarView.reorient();
  20. + mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
  21. + mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
  22. + mNavigationBarView.getRecentsButton().setLongClickable(true);
  23. + mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
  24. + mNavigationBarView.getBackButton().setLongClickable(true);
  25. + mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
  26. + mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
  27. + mNavigationBarView.getHideBarButton().setOnClickListener(mHideBarClickListener);//ADD liweiping
  28. +
  29. + if (forceReset) {
  30. + // Nav Bar was added dynamically - we need to reset the mSystemUiVisibility and call
  31. + // setSystemUiVisibility so that mNavigationBarMode is set to the correct value
  32. + Log.i("way", "prepareNavigationBarView mNavigationBarMode = "+ mNavigationBarMode + " mSystemUiVisibility = " + mSystemUiVisibility + " mNavigationIconHints = " + mNavigationIconHints);
  33. + mNavigationBarMode = 0;
  34. +
  35. + int newVal = mSystemUiVisibility;
  36. + mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
  37. + setSystemUiVisibility(newVal, /*SYSTEM_UI_VISIBILITY_MASK*/0xffffffff);
  38. + int hints = mNavigationIconHints;
  39. + mNavigationIconHints = 0;
  40. + setNavigationIconHints(hints);
  41. + topAppWindowChanged(mShowMenu);
  42. + }
  43. +
  44. + updateSearchPanel();
  45. + }
  46. +
  47. + // For small-screen devices (read: phones) that lack hardware navigation buttons
  48. + private void addNavigationBar(boolean forceReset) {
  49. + if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
  50. + if (mNavigationBarView == null) return;
  51. +
  52. + prepareNavigationBarView(forceReset);
  53. +
  54. + mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
  55. + }
  56. + //END liweiping


需要注意的是:

①显示NavigationBar时,需要重新实例化一次NavigationBarView,我之前有试过移除NavigationBarView后未置空,下次添加时直接使用,会出现状态栏重启的情况,具体原因未知,log显示动画播放错误之类。

②重新添加NavigationBarView时需要恢复NavigationBarView之前的状态,比如说隐藏前时是透明的、显示输入法按钮、菜单键等等。

③本文是在Android5.0的代码上修改的,其他版本未验证。

④本文仅是提供一种思路,并非最优方案。

⑤转载请注明出处:http://blog.csdn.net/way_ping_li



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

闽ICP备14008679号