Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android之framework修改底部導航欄NavigationBar動態顯示和隱藏

Android之framework修改底部導航欄NavigationBar動態顯示和隱藏

編輯:關於Android編程

大家都知道,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.xml
index 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.java
index 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.java
index 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.java
index 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.java
index 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的代碼上修改的,其他版本未驗證。

④本文僅是提供一種思路,並非最優方案。

 


 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved