转眼又到月末了,一直呢想写些什么,但是没有时间。人呐,还是要勤快点的,懒是不行的。想了想,最近一直在搞 ActionBar 的项目,不如就乘机分析一波,也算是一种总结吧。
一、布局
先从整个布局来分析吧,话不多说,先上图。
整体图.png最近简书的图片不支持调整尺寸,大家就将就点看吧。
我们看到的每一个界面的根布局都是 DecorView,接下来就是 ActionBarOverlayLayout,紧接着,就会在 ActionBarOverlayLayout 放如图所示的布局。
1、最上面是 ActionBar 的区域。
2、中间是 Content 的区域,就是我们平时在 onCreate() 里添加的 setContentView(R.layout.xxxx) 的内容,会在 Content 区域显示,也就是我们自定义的布局。
3、最下面是 SplitActionBar 区域,也就是分离式 ActionBar 布局,姑且先这么叫它吧。
鉴于之前啰啰嗦嗦的介绍方式,我决定要改变一下现状,简单粗暴,先扔结果,最后再分析这是怎么来的。
上面的布局嘛,肯定是由 xml 控制的,那么这个布局文件到底是何方神圣呢?
frameworks/base/core/res/res/layout/screen_action_bar.xml
对,就是这个布局,在 PhoneWindow 中加载。众所周知,PhoneWindow 是继承自抽象类 Window 的。
直接看一下 xml 里面究竟是怎么样定义的?
<com.android.internal.widget.ActionBarOverlayLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/decor_content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false"
android:theme="?attr/actionBarTheme">
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.android.internal.widget.ActionBarContainer
android:id="@+id/action_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
android:touchscreenBlocksFocus="true"
android:keyboardNavigationCluster="true"
android:gravity="top">
<com.android.internal.widget.ActionBarView
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/actionBarStyle" />
<com.android.internal.widget.ActionBarContextView
android:id="@+id/action_context_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/actionModeStyle" />
</com.android.internal.widget.ActionBarContainer>
<com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/actionBarSplitStyle"
android:visibility="gone"
android:touchscreenBlocksFocus="true"
android:keyboardNavigationCluster="true"
android:gravity="center"/>
</com.android.internal.widget.ActionBarOverlayLayout>
从布局当中可以看出,ActionBarView 和 ActionBarContextView 父布局容器是 ActionBarContainer,是 layout_alignParentTop = true,且 ActionBarContextView 初始可见状态是 gong。为什么是 gong 呢?因为这个 ActionBarContextView 代表的是我们众所周知的 ActionMode 模式。通常情况下,进入一个 Activity 首先看到的是 ActionBar ,接着通过 startActionMode() 函数进入 ActionMode 模式。id 为 split_action_bar 的 ActionBarContainer 是分离式 ActionBar,位于屏幕最底部的菜单,初始可见状态也为 gone。只有当在 onCreate() 函数里添加 getWindow().setUiOptions(ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW),或者在清单配置文件里 <application> 或 <activity> 元素中添加uiOptions="splitActionBarWhenNarrow" 属性才会出现底部菜单栏。最后,content 即为我们自定义的布局。
简单的回顾一下,进入 ActionMode 的具体操作。
首先写一个MyActionModeCallback 继承自 ActionMode.Callback。
private class MyActionModeCallback implements ActionMode.Callback {
ActionMode mActionMode;
@Override public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.setTitle("ACTION MODE");
mode.setSubtitle(null);
mode.setTitleOptionalHint(false);
MenuItem actionItem1 = menu.add("DISABLED").setEnabled(false);
actionItem1.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// action mode default mode is SHOW_AS_ACTION_IF_ROOM.
menu.add("STOP").setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.add("STOP").setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
return true;
}
@Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getTitle().equals("STOP")) {
if (mActionMode != null) {
mActionMode.finish();
}
}
return true;
}
@Override public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
}
代码很简单,重写了 ActionMode.Callback 四个方法:
1.onCreateActionMode():在初始创建的时候调用,在这里创建 ActionMode 模式下需要显现的菜单项,setTitle() 设置标题为 ACTION MODE,setTitleOptionalHint() 设置为 false 时,系统可以在缺少空间时选择截断标题而不是完全隐藏标题。如果为 true 时,则反之。
2.onPrepareActionMode():在创建之后准备绘制的时候调用,这个方法在提高了 build.gradle 的“compileSdkVersion”和“buildToolsVersion”后可能不会被调用到。
3.onActionItemClicked():当点击 ActionMode 菜单选项的时候调用,当点击 STOP 菜单项时退出 ActionMode 模式。
4.onDestroyActionMode():当退出 ActionMode 时调用,在这里将 mActionMode 置为 null。
public void startActionMode() {
if (mActionMode == null) {
ActionMode.Callback cb = new MyActionModeCallback();
mActionMode = startActionMode(cb);
}
}
最后,可以在适当的地方调用调用 startActionMode(cb) 函数,将进入 ActionMode 模式。将我们自定义的 MyActionModeCallback 的对象 cb 作为参数传进去,这个 startActionMode() 函数是 Activity 类的方法。
说了一些题外话,我们继续回到刚刚的布局分析上来。在此之前,我们先了解几个重要类的含义。
ActionMenuView 决定子菜单 item 和 overflow 的大小、摆放、位置,添加的逻辑不在这里。
ActionBarView 子控件摆放位置,包括 title、HomeView 的添加。
ActionBarContextView 控制 ActionMode 子菜单的添加、尺寸、位置。
ActionBarContainer 控制 split、ActionBar、Tab 的显示。
MenuItemImpl 控制具体是什么样的 Item。
MenuBuilder 控制什么时候添加,添加了多少。
BaseMenuPresenter 将多个 item 添加到父容器内,包括序列,基类。
ActionMenuPresenter 控制菜单和标题的间距、动画、如何放置以及最小尺寸。
网友评论