如果你从事过Android客户端开发,相信你对ActionBar这套框架并不陌生,或者说你并不了解它,但是你应该时不时的要跟它打交道。抛开ActionBar的实现不说,ActionBar实际上是对Android的TitleBar行为的抽象,这种框架可以适用于这种模式的应用,是对需要的行为视图的抽象。当然或许你也和我一样,对ActionBar的实现效率并不满意,因为你打开它的视图,你会发现它的实现非常的ugly。不过我们庆幸的看到的是,ActionBar在设计的时候就并不是以一个强类型的姿态存在,我们发现它并不是以一个View的方式存在,而跟Fragment一样是一个很单纯的工具类。这种设计正好屏蔽了内部的实现,从而可以让我们对它的实现进行改造。当然ActionBar的改造对我来说并不是文章的重点,如果你对自定义控件已经熟门熟路了,那么相信你阅读完这个系列的文章以后,能更有助于你改造ActionBar。对本章我将从ActionBar生成入口开始讲述。
我们知道我们再定义一个Activity的时候,跟WMS直接挂钩的客户端代理是Window类,当然,我这么说本身不准确。因为Window是间接持有这种代理类,不过这不影响我们对ActionBar的整体理解。对于Window类来说,它跟我们直接打交道是PhoneWindow。我们将调用setContentView的方式来注册我们需要的内部视图,为什么说是内部视图,因为除了我们的视图之外,Window里面还注册有多个的视图装饰。其实这也是装饰模式的一种,甚至很像模板方法。
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
我们发现实际上我们的视图是包含在一个叫做mContentParent的ViewGroup中。而这个对象的生成是定义在Window类中的protected ViewGroup generateLayout(DecorView decor)方法中。
我们知道对于一个Window视图的影响除了Window.LayoutParams外还有Feature。Feature对视图的影响并不直接跟WMS打交道。即使跟WMS打交道也是通过WIndow.LayoutParams类控制,也就是说Feature本身就是一种可有可无的小甜点。在生成mContentParent的时候你会经常看到一些属性匹配代码:
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(Injector.getFloatingWindowWidth(getContext()), WRAP_CONTENT); // Miui Hook
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
requestFeature(FEATURE_ACTION_BAR_OVERLAY);
}
if (!hasSoftInputMode()) {
params.softInputMode = a.getInt(
com.android.internal.R.styleable.Window_windowSoftInputMode,
params.softInputMode);
}
if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = com.android.internal.R.layout.screen_simple;
}
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
方法获取mContentParent,也就是说我们的视图就是包含在这个ActionBar容器中的Content容器中。
我们回到Activity的setConentView方法。我们在设置完我们的视图以后,它会初始化initActionBar();
我前面已经说过了,ActionBar和Fragment本身不属于AndroidUI系统的一部分,因此需要对它进行初始化。ActionBar的实现类是com.android.internal.app.ActionBarImpl,由于ActionBarImpl针对的是Window,因此不论你是Activity或者是Dialog或者是PopupWindow理论上都可以使用ActionBar。这种理论实际上也可以说明一点,就是在同一个界面中出现两个ActionBar是合理的。甚至你在同一个Window里面不同的Fragment中实现自己的一套ActionBar也是可行的。因为它并不纳入在WMS的管理中,好吧,有点扯远了,我们继续前文。
public ActionBarImpl(Activity activity) {
mActivity = activity;
Window window = activity.getWindow();
View decor = window.getDecorView();
init(decor);
if (!mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
mContentView = decor.findViewById(android.R.id.content);
}
}
我们看到ActionBarImpl的初始化主要通过init方法实现。
private void init(View decor) {
mContext = decor.getContext();
mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(
com.android.internal.R.id.action_bar_overlay_layout);
if (mOverlayLayout != null) {
mOverlayLayout.setActionBar(this);
}
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
mContainerView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.action_bar_container);
mTopVisibilityView = (ViewGroup)decor.findViewById(
com.android.internal.R.id.top_action_bar);
if (mTopVisibilityView == null) {
mTopVisibilityView = mContainerView;
}
mSplitView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.split_action_bar);
if (mActionView == null || mContextView == null || mContainerView == null) {
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
"with a compatible window decor layout");
}
mActionView.setContextView(mContextView);
mContextDisplayMode = mActionView.isSplitActionBar() ?
CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
// This was initially read from the action bar style
final int current = mActionView.getDisplayOptions();
final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
if (homeAsUp) {
mDisplayHomeAsUpSet = true;
}
ActionBarPolicy abp = ActionBarPolicy.get(mContext);
setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);
setHasEmbeddedTabs(abp.hasEmbeddedTabs());
}
非子墨:
QQ:1025250620
SINA:http://weibo.com/1752090185/profile?rightmod=1&wvr=5&mod=personinfo