Android 7.0 虛擬按鍵(NavigationBar)源碼分析(一) 之 View的創建流程

轉自: http://blog.csdn.net/kuaiguixs/article/details/78291695

最近有個需求是修改虛擬按鍵的單擊和長按效果。所以研究了下Android關於虛擬按鍵的實現流程。好記性不如爛筆頭,記錄如下。


    首先,幾個重要的類:

//實現 單個虛擬按鍵的 自定義ImageView

    frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java

//虛擬按鍵的容器,實現整個 虛擬導航條的 自定義LinearLayout

    frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java

//動態加載虛擬按鍵,放入NavigationBarView

   frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java

//虛擬導航條對應的佈局文件

    frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml

//實現虛擬按鍵的點擊效果

    frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java


下面從幾個方面來分析

1、View的創建流程

2、點擊效果的實現流程

3、客製化:給NavigationBar添加一個隱藏鍵,點擊隱藏NavigationBar,上滑又會顯示NavigationBar

注:以下源碼如果沒有特殊說明,都在 frameworks/base/packages/SystemUI/ 目錄下。


【一、View的創建流程】

跟 StatusBar 一樣,NavigationBar 也是在 PhoneStatusBar.java 中初始化的。

1、通過IWindowManager,先判斷是否顯示 NavigationBar。如果顯示,則加載。

[java] view plain copy
  1.   protected PhoneStatusBarView makeStatusBarView() {  
  2.       final Context context = mContext;  
  3. ...  
  4.   
  5.       try {  
  6.           boolean showNav = mWindowManagerService.hasNavigationBar();  
  7.           if (showNav) {  
  8.               createNavigationBarView(context);  
  9.           }  
  10.       } catch (RemoteException ex) {  
  11.           // no window manager? good luck with that  
  12.       }  
  13.   
  14. ...  
  15.       return mStatusBarView;  
  16.   }  

2、主要看這個  inflateNavigationBarView 方法,它加載了佈局 R.layout.navigation_bar,作爲虛擬按鍵的容器

[java] view plain copy
  1. protected void createNavigationBarView(Context context) {  
  2.     inflateNavigationBarView(context);  
  3.     mNavigationBarView.setDisabledFlags(mDisabled1);  
  4.     mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));  
  5.     mNavigationBarView.setOnVerticalChangedListener(  
  6.             new NavigationBarView.OnVerticalChangedListener() {  
  7.         @Override  
  8.         public void onVerticalChanged(boolean isVertical) {  
  9.             if (mAssistManager != null) {  
  10.                 mAssistManager.onConfigurationChanged();  
  11.             }  
  12.             mNotificationPanel.setQsScrimEnabled(!isVertical);  
  13.         }  
  14.     });  
  15.     mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {  
  16.         @Override  
  17.         public boolean onTouch(View v, MotionEvent event) {  
  18.             checkUserAutohide(v, event);  
  19.             return false;  
  20.         }});  
  21. }  
  22.   
  23. protected void inflateNavigationBarView(Context context) {  
  24.     mNavigationBarView = (NavigationBarView) View.inflate(  
  25.             context, R.layout.navigation_bar, null);  
  26. }  

3、來到 navigation_bar.xml。我們發現,它並沒有直接將虛擬按鍵加入進來,而是添加了一個NavigationBarInflaterView。

[html] view plain copy
  1. <com.android.systemui.statusbar.phone.NavigationBarView  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:systemui="http://schemas.android.com/apk/res-auto"  
  4.     android:layout_height="match_parent"  
  5.     android:layout_width="match_parent"  
  6.     android:background="@drawable/system_bar_background">  
  7.   
  8.     <com.android.systemui.statusbar.phone.NavigationBarInflaterView  
  9.         android:id="@+id/navigation_inflater"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="match_parent" />  
  12.   
  13. </com.android.systemui.statusbar.phone.NavigationBarView>  

4、來到 NavigationBarInflaterView.java 繼承自 FrameLayout。

    可以看到,在回調方法onFinishInflate()中通過 inflateLayout(getDefaultLayout())。getDefaultLayout()返回了一個字符串:“space,back;home;recent,menu_ime”。用來表示虛擬按鍵載入的順序。

    將字串傳入 inflateLayout()。通過“;”作爲分隔符,將字串分爲三個部分。 space,back  homerecent,menu_ime

再通過“,”將字串進行二次分割,分別存入 start,center,end三個數組。start表示NavigationBar左邊第一個位置,這裏表示後退鍵在第一個位置,HOME鍵在中間,應用列表鍵在最右邊。然後通過 inflateButton() 去加載xml佈局文件。


[java] view plain copy
  1. protected void onFinishInflate() {  
  2.     super.onFinishInflate();  
  3.     inflateChildren();  
  4.     clearViews();  
  5.     inflateLayout(getDefaultLayout());  
  6. }  
  7.   
  8. protected String getDefaultLayout() {  
  9.     /// M: BMW @{  
  10.     if (MultiWindowManager.isSupported()) {  
  11.         return mContext.getString(R.string.config_navBarLayout_float);  
  12.     }  
  13.     /// @}  
  14.     return mContext.getString(R.string.config_navBarLayout);  
  15. }  
  16.   
  17. protected void inflateLayout(String newLayout) {  
  18.     mCurrentLayout = newLayout;  
  19.     if (newLayout == null) {  
  20.         newLayout = getDefaultLayout();  
  21.     }  
  22.     String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);  
  23.     String[] start = sets[0].split(BUTTON_SEPARATOR);  
  24.     String[] center = sets[1].split(BUTTON_SEPARATOR);  
  25.     String[] end = sets[2].split(BUTTON_SEPARATOR);  
  26.     // Inflate these in start to end order or accessibility traversal will be messed up.  
  27.     inflateButtons(start, (ViewGroup) mRot0.findViewById(R.id.ends_group), false);  
  28.     inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group), true);  
  29.   
  30.     inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group), false);  
  31.     inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group), true);  
  32.   
  33.     addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group));  
  34.     addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group));  
  35.   
  36.     inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group), false);  
  37.     inflateButtons(end, (ViewGroup) mRot90.findViewById(R.id.ends_group), true);  
  38. }  

5、字串作爲加載標識:1、space:填充一段空白內容;2、back:返回鍵;3、home:Home鍵;4、recent:應用列表鍵

如果想要調整虛擬按鍵的順序,只需要調整字符串的順序,好像還挺方便的。

例如:我要把back鍵和recent鍵調換位置,只需要修改config_navBarLayout的值爲"space,recent;home;back,menu_ime"即可。

修改前,對應 <string name="config_navBarLayout" translatable="false">space,back;home;recent,menu_ime</string>


修改後,對應 <string name="config_navBarLayout" translatable="false">space,recent;home;back,menu_ime</string>


[java] view plain copy
  1.   protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,  
  2.           int indexInParent) {  
  3.       LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;  
  4.       float size = extractSize(buttonSpec);  
  5.       String button = extractButton(buttonSpec);  
  6.       View v = null;  
  7.       if (HOME.equals(button)) {  
  8.           v = inflater.inflate(R.layout.home, parent, false);  
  9.           if (landscape && isSw600Dp()) {  
  10.               setupLandButton(v);  
  11.           }  
  12.       } else if (BACK.equals(button)) {  
  13.           v = inflater.inflate(R.layout.back, parent, false);  
  14.           if (landscape && isSw600Dp()) {  
  15.               setupLandButton(v);  
  16.           }  
  17.       } else if (RECENT.equals(button)) {  
  18.           v = inflater.inflate(R.layout.recent_apps, parent, false);  
  19.           if (landscape && isSw600Dp()) {  
  20.               setupLandButton(v);  
  21.           }  
  22.       }  
  23.   
  24. ...  
  25.       return v;  
  26.   }  

6、每個虛擬按鍵都有獨立的佈局文件:

    Home鍵:SystemUI/res/layout/home.xml

    返回鍵:SystemUI/res/layout/back.xml

    最近打開的應用列表鍵:SystemUI/res/layout/recent_apps.xml

以Home鍵爲例,佈局文件如下,都是常見的屬性,不多講了。主要是這個systemui:keyCode,這是個自定義屬性,用於指定這個按鈕的鍵值。這個主要用於點擊事件,詳見第二節。

[html] view plain copy
  1. <com.android.systemui.statusbar.policy.KeyButtonView  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:systemui="http://schemas.android.com/apk/res-auto"  
  4.     android:id="@+id/home"  
  5.     android:layout_width="@dimen/navigation_key_width"  
  6.     android:layout_height="match_parent"  
  7.     android:layout_weight="0"  
  8.     android:src="@drawable/ic_sysbar_home"  
  9.     systemui:keyCode="3"  
  10.     android:scaleType="center"  
  11.     android:contentDescription="@string/accessibility_home"  
  12.     android:paddingStart="@dimen/navigation_key_padding"  
  13.     android:paddingEnd="@dimen/navigation_key_padding"  
  14.     />  


點擊效果的實現流程請見下篇文章

發佈了16 篇原創文章 · 獲贊 21 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章