Android 7.0 虚拟按键(NavigationBar)源码分析 之 View的创建流程
2017-10-20 09:20
751 查看
最近有个需求是修改虚拟按键的单击和长按效果。所以研究了下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。如果显示,则加载。
2、主要看这个 inflateNavigationBarView 方法,它加载了布局 R.layout.navigation_bar,作为虚拟按键的容器
3、来到 navigation_bar.xml。我们发现,它并没有直接将虚拟按键加入进来,而是添加了一个NavigationBarInflaterView。
4、来到 NavigationBarInflaterView.java 继承自 FrameLayout。
可以看到,在回调方法onFinishInflate()中通过 inflateLayout(getDefaultLayout())。getDefaultLayout()返回了一个字符串:“space,back;home;recent,menu_ime”。用来表示虚拟按键载入的顺序。
将字串传入 inflateLayout()。通过“;”作为分隔符,将字串分为三个部分。
space,back home recent,menu_ime
再通过“,”将字串进行二次分割,分别存入 start,center,end三个数组。start表示NavigationBar左边第一个位置,这里表示后退键在第一个位置,HOME键在中间,应用列表键在最右边。然后通过 inflateButton() 去加载xml布局文件。
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>
6、每个虚拟按键都有独立的布局文件:
Home键:SystemUI/res/layout/home.xml
返回键:SystemUI/res/layout/back.xml
最近打开的应用列表键:SystemUI/res/layout/recent_apps.xml
以Home键为例,布局文件如下,都是常见的属性,不多讲了。主要是这个systemui:keyCode,这是个自定义属性,用于指定这个按钮的键值。这个主要用于点击事件,详见第二节。
点击效果的实现流程请见下篇文章
首先,几个重要的类:
//实现 单个虚拟按键的 自定义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。如果显示,则加载。
protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; ... try { boolean showNav = mWindowManagerService.hasNavigationBar(); if (showNav) { createNavigationBarView(context); } } catch (RemoteException ex) { // no window manager? good luck with that } ... return mStatusBarView; }
2、主要看这个 inflateNavigationBarView 方法,它加载了布局 R.layout.navigation_bar,作为虚拟按键的容器
protected void createNavigationBarView(Context context) { inflateNavigationBarView(context); mNavigationBarView.setDisabledFlags(mDisabled1); mNavigationBarView.setComponents(mRecents, getComponent(Divider.class)); mNavigationBarView.setOnVerticalChangedListener( new NavigationBarView.OnVerticalChangedListener() { @Override public void onVerticalChanged(boolean isVertical) { if (mAssistManager != null) { mAssistManager.onConfigurationChanged(); } mNotificationPanel.setQsScrimEnabled(!isVertical); } }); mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { checkUserAutohide(v, event); return false; }}); } protected void inflateNavigationBarView(Context context) { mNavigationBarView = (NavigationBarView) View.inflate( context, R.layout.navigation_bar, null); }
3、来到 navigation_bar.xml。我们发现,它并没有直接将虚拟按键加入进来,而是添加了一个NavigationBarInflaterView。
<com.android.systemui.statusbar.phone.NavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent" android:background="@drawable/system_bar_background"> <com.android.systemui.statusbar.phone.NavigationBarInflaterView android:id="@+id/navigation_inflater" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.android.systemui.statusbar.phone.NavigationBarView>
4、来到 NavigationBarInflaterView.java 继承自 FrameLayout。
可以看到,在回调方法onFinishInflate()中通过 inflateLayout(getDefaultLayout())。getDefaultLayout()返回了一个字符串:“space,back;home;recent,menu_ime”。用来表示虚拟按键载入的顺序。
将字串传入 inflateLayout()。通过“;”作为分隔符,将字串分为三个部分。
space,back home recent,menu_ime
再通过“,”将字串进行二次分割,分别存入 start,center,end三个数组。start表示NavigationBar左边第一个位置,这里表示后退键在第一个位置,HOME键在中间,应用列表键在最右边。然后通过 inflateButton() 去加载xml布局文件。
protected void onFinishInflate() { super.onFinishInflate(); inflateChildren(); clearViews(); inflateLayout(getDefaultLayout()); } protected String getDefaultLayout() { /// M: BMW @{ if (MultiWindowManager.isSupported()) { return mContext.getString(R.string.config_navBarLayout_float); } /// @} return mContext.getString(R.string.config_navBarLayout); } protected void inflateLayout(String newLayout) { mCurrentLayout = newLayout; if (newLayout == null) { newLayout = getDefaultLayout(); } String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3); String[] start = sets[0].split(BUTTON_SEPARATOR); String[] center = sets[1].split(BUTTON_SEPARATOR); String[] end = sets[2].split(BUTTON_SEPARATOR); // Inflate these in start to end order or accessibility traversal will be messed up. inflateButtons(start, (ViewGroup) mRot0.findViewById(R.id.ends_group), false); inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group), true); inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group), false); inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group), true); addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group)); addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group)); inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group), false); inflateButtons(end, (ViewGroup) mRot90.findViewById(R.id.ends_group), true); }
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>
protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape, int indexInParent) { LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater; float size = extractSize(buttonSpec); String button = extractButton(buttonSpec); View v = null; if (HOME.equals(button)) { v = inflater.inflate(R.layout.home, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (BACK.equals(button)) { v = inflater.inflate(R.layout.back, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } else if (RECENT.equals(button)) { v = inflater.inflate(R.layout.recent_apps, parent, false); if (landscape && isSw600Dp()) { setupLandButton(v); } } ... return v; }
6、每个虚拟按键都有独立的布局文件:
Home键:SystemUI/res/layout/home.xml
返回键:SystemUI/res/layout/back.xml
最近打开的应用列表键:SystemUI/res/layout/recent_apps.xml
以Home键为例,布局文件如下,都是常见的属性,不多讲了。主要是这个systemui:keyCode,这是个自定义属性,用于指定这个按钮的键值。这个主要用于点击事件,详见第二节。
<com.android.systemui.statusbar.policy.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/home" android:layout_width="@dimen/navigation_key_width" android:layout_height="match_parent" android:layout_weight="0" android:src="@drawable/ic_sysbar_home" systemui:keyCode="3" android:scaleType="center" android:contentDescription="@string/accessibility_home" android:paddingStart="@dimen/navigation_key_padding" android:paddingEnd="@dimen/navigation_key_padding" />
点击效果的实现流程请见下篇文章
相关文章推荐
- Android 7.0 虚拟按键(NavigationBar)源码分析(一) 之 View的创建流程
- Android 7.0 虚拟按键(NavigationBar)源码分析 之 点击事件的实现流程
- 源码分析Android中View的绘制流程
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- View视图框架源码分析之一:android是如何创建一个view
- Android应用层View绘制流程与源码分析
- androi View视图框架源码分析之一:android是如何创建一个view的
- Android 6.0 View加载流程源码分析
- Android应用层View绘制流程与源码分析
- android2.3 View视图框架源码分析之一:android是如何创建一个view的?
- android应用程序窗口框架学习(2)-view绘制流程源代码解析-setContentView与LayoutInflater加载解析机制源码分析
- [Android源码分析]蓝牙打开流程分析——jni层之下的偷偷摸摸(Service Record的创建)
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析