requestFeature的实际运用

设置Activity悬浮

通过在styles.xml中设置windowIsFloating属性实现Activity悬浮

true
activity_floating.png

设置Activity能滑动消失

有两种方式:

  1. styles.xml中设置windowSwipeToDismiss属性
true
  1. 在Activity中调用requestWindowFeature方法
requestWindowFeature(Window.FEATURE_SWIPE_TO_DISMISS);
requestFeature的实际运用_第1张图片
activity_swipe_dismiss.gif

000

设置是否显示ActionBar

查看PhoneWindow的generateLayout方法

PhoneWindow#generateLayout

else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
  // Don't allow an action bar if there is no title.
  requestFeature(FEATURE_ACTION_BAR);
}

系统的注释说明了要设置显示ActionBar的前提条件是得设置窗口包含title,即设置Window的FEATURE_NO_TITLE属性为false。

false

在generateLayout方法中当FEATURE_NO_TITLE属性为false的时候会进入下列条件判断:

else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
  // If no other features and not embedded, only need a title.
  // If the window is floating, we need a dialog layout
  if (mIsFloating) {
      TypedValue res = new TypedValue();
      getContext().getTheme().resolveAttribute(
              R.attr.dialogTitleDecorLayout, res, true);
      layoutResource = res.resourceId;
  } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
      layoutResource = a.getResourceId(
              R.styleable.Window_windowActionBarFullscreenDecorLayout,
              R.layout.screen_action_bar);
  } else {
      layoutResource = R.layout.screen_title;
  }
  // System.out.println("Title!");
}

从上述代码可以知道当设置了显示ActionBar时,layoutResource会从windowActionBarFullscreenDecorLayout中取值,如果取不到值,默认为screen_action_bar布局。

了解了什么时候加载的ActionBar的布局,就有一个疑问,这个ActionBar的布局是在什么时候使用到的呢?
在Activity的setContentView方法中初始化了这个ActionBar

Activity#initWindowDecorActionBar

private void initWindowDecorActionBar() {
    Window window = getWindow();
    // Initializing the window decor can change window feature flags.
    // Make sure that we have the correct set before performing the test below.
    window.getDecorView();
    if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
        return;
    }
    mActionBar = new WindowDecorActionBar(this);
    mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
    mWindow.setDefaultIcon(mActivityInfo.getIconResource());
    mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}

从上述代码中可以看到WindowDecorActionBar这个类的构造方法初始化了ActionBar

mDecorToolbar = getDecorToolbar(decor.findViewById(com.android.internal.R.id.action_bar));

mDecorToolbar的类型则可能是DecorToolbar或者ToolbarWidgetWrapper

WindowDecorActionBar#getDecorToolbar

private DecorToolbar getDecorToolbar(View view) {
    if (view instanceof DecorToolbar) {
        return (DecorToolbar) view;
    } else if (view instanceof Toolbar) {
        return ((Toolbar) view).getWrapper();
    } else {
        throw new IllegalStateException("Can't make a decor toolbar out of " +
                view.getClass().getSimpleName());
    }
}

点击com.android.internal.R.id.action_bar这个id查看布局中的ActionBar是什么类型,得知在布局screen_action_bar.xml中ActionBar是ActionBarView类型的,它是DecorToolbar这个接口类型的实现类;
在布局screen_toolbar.xml中ActionBar是Toolbar类型的。

显示默认的ActionBar

有两种方式:

  1. styles.xml中设置windowActionBar属性
false
true
  1. 在Activity中调用requestWindowFeature方法
false
requestWindowFeature(Window.FEATURE_ACTION_BAR);
requestFeature的实际运用_第2张图片
Activity_ActionBar.png

通过hierarchyviewer查看视图树我们可以看到id为action_bar的View在API25中是Toolbar类型的,从之前的分析得知actionbar的布局是由windowActionBarFullscreenDecorLayout属性来决定的,但是我们定义主题的时候并未定义该属性,那么这个属性是在那里定义的呢?

requestFeature的实际运用_第3张图片
hierarchy_actionbar.png

打开此Activity的主题,这里我使用的是创建app时,系统默认使用的主题


看过之前分析AppCompatActivity的setContentView的流程,我们知道它会根据style中定义的AppCompatThemeWindow的属性分别创建subDecormDecor。而根据主题Theme.AppCompat.Light.DarkActionBar搜索继承关系找到Base.V7.Theme.AppCompat.Light主题,其中定义了有关ActionBar的属性:


由于android:windowActionBarwindowActionBar都为true,所以会用abc_screen_toolbar作为subDecor的布局,screen_toolbar作为mDecor的布局,这两个布局中都含有Toolbar,所以界面会显示两个标题。

标题的显示已经很明白了,那么如何使用这个它呢?我们在AppCompatActivity的子类中获取ActionBar是通过getSupportActionBar方法来取代getActionBar的:

AppCompatActivity#getSupportActionBar

getDelegate().getSupportActionBar()

以我使用的API25的模拟器为例,最终在AppCompatDelegateImplN的父类AppCompatDelegateImplBase类中找到了getSupportActionBar方法:

AppCompatDelegateImplBase#getSupportActionBar

// The Action Bar should be lazily created as hasActionBar
// could change after onCreate
initWindowDecorActionBar();
return mActionBar;

initWindowDecorActionBar方法是抽象类,找到该方法的具体实现:

AppCompatDelegateImplV9#initWindowDecorActionBar

  1. 确保创建了subDecor,这里的subDecor其实就是Activity的mDecor
ensureSubDecor();
  1. 创建WindowDecorActionBar
if (mOriginalWindowCallback instanceof Activity) {
    mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback,
            mOverlayActionBar);
} else if (mOriginalWindowCallback instanceof Dialog) {
    mActionBar = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
}
  1. 如果ActionBar不为空,设置显示Home元素
if (mActionBar != null) {
    mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
}

比对一下AppCompatActivity和Activity中对于ActionBar的创建大同小异,都是使用initWindowDecorActionBar创建了WindowDecorActionBar类。

修改默认的ActionBar样式

以AppCompatV7的ActionBar为例,已知当属性windowActionBar为true时,会用abc_screen_toolbar作为subDecor的布局,且WindowDecorActionBar的getDecorToolbar方法会返回ToolbarWidgetWrapper类:

abc_screen_toolbar.xml

ToolbarWidgetWrapper构造方法
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(),
                    null, R.styleable.ActionBar, R.attr.actionBarStyle, 0);

查看res\values\values.xml中的ActionBar这个自定义属性集合:


   
   
   


    
    
    
    
    
    
    




























上面这些是我们ActionBar的属性

根据TypedArray流程分析,xml style的样式是toolbarStyle,在我们的例子中,这个样式在主题Base.V7.Theme.AppCompat.Light中定义:


@style/Widget.AppCompat.Toolbar

而theme style defStyleAttr的样式是actionBarStyle,也在主题Base.V7.Theme.AppCompat.Light中定义:

@style/Widget.AppCompat.Light.ActionBar.Solid

Widget.AppCompat.Light.ActionBar.Solid


由于优先级高的toolbarStyle并没有什么关于ActionBar的属性定义,所以ActionBar的属性大部分定义在actionBarStyle中,所以我们可以这样自定义ActionBar:




custom_actionbar.png

你可能感兴趣的:(requestFeature的实际运用)