转载

Material适配2 - 高级篇

继续Material系列,先从Toolbar讲起

ActionBar --> Toolbar

在使用ActionBar的时候,一堆的问题:这个文字能不能定制,位置能不能改变,图标的间距怎么控制神马的,由此暴露出了ActionBar设计的不灵活。

在上一篇中,我们只是简单使用了AppCompatActivity,他使用的仍然是ActionBar

官方在21以后提供了ToolBar。

Toolbar之所以灵活,是因为它其实就是一个ViewGroup,我们在使用的时候和普通的组件一样,在布局文件中声明。

主题使用

使用Toolbar时,如果单纯的当作控件来使用,主题是不需要单独设置的。

但是如果想用他来替代ActionBar,

那么需要配置为 Theme.AppCompat.NoActionBar 主题,

或者在主题中加入

<item name="windowActionBar">false</item> <item name="windowNoTitle">true</item>

(两个都必须有,上一篇已经提到没有 windowNoTitle 时会报错)

这里推荐使用第一种方式。

常用的配置

Toolbar因为经常被用来替代ActionBar,所以一般项目里都会抽取出来,以便include。

可能有人会说,既然还是用来替换ActionBar,那我项目里直接不动ActionBar不就完了?

对,一般情况下是没有问题的,但是有些界面需要ActionMode的时候,你就被迫要换成Toolbar了。

这是后话,我们这篇会谈到。

先来看Toolbar常用代码:

include_toolbar.xml

<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.Toolbar  android:id="@+id/toolbar"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:background="?attr/colorPrimary"  app:popupTheme="@style/ThemeOverlay.AppCompat.Light"  app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"  android:minHeight="?attr/actionBarSize" /> 

配置中需要注意的是theme和popupTheme,我们来仔细看下,先从View的theme说起。

View的theme

Android 5.0引入一个全新的特性,允许你对view设置theme,这种设置会影响控件及其包含的子控件。

使用AppCompat v22.1.x 后,也可以给你 layout 里的任意视图设置主题。

只要使用 android:theme 这个属性就好,新版本的兼容库可以在 compat 和 framework 之间无缝地切换功能。

实现原理

这是因为有 ContextThemeWrapper 类,这个类API v1的时候就有了。

他包裹(wrap)一个存在的Context(这里指你的Activity),之后覆盖(overlay)一个新的主题在当前Context的主题之上,这也是为什么叫ThemeOverlay。

Toolbar常用的ThemeOverlay

  • ThemeOverlay.AppCompat.Light.ActionBar
  • ThemeOverlay.AppCompat.Dark.ActionBar

android:theme 与 app:theme

在AppCompat v21里,提供了一个快速方便的方法设置Toolbar的主题,使用app:theme。

而新版本22.1.x中,AppCompat 允许对 Toolbar 使用 android:theme 代替 app:theme

最好的一点是:它会自动继承父视图的theme ,并且兼容所有APIv11以上的设备。

示例:

<Toolbar     android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">      <!-- This TextView inherits its theme from the parent Toolbar -->     <TextView android:text="I'm light!" />  </Toolbar>

对于运行 API v10 甚至更老的设备来说,你也可以使用 android:theme 属性, 不过它不会继承父视图theme。

这就意味着你要么重新考虑你的布局,要么为每一个子视图都设置上 android:theme 属性。(这样做效率真的很低)

总结一下:

  • 兼容 API 11 以上,推荐使用 android:theme
  • 如果兼容更老的版本,推荐继续使用 app:theme

app:popupTheme

有时候我们有需求:

ActionBar文字是白的,ActionBar Overflow弹出的是白底黑字

让ActionBar文字是白的,那么对应的theme肯定是Dark。

可是让ActionBar弹出的是白底黑字,那么需要Light主题。

这时候popupTheme就派上用场了。

<android.support.v7.widget.Toolbar  android:id="@+id/toolbar"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:background="?attr/colorPrimary"  app:popupTheme="@style/ThemeOverlay.AppCompat.Light"  android:minHeight="?attr/actionBarSize" />

注意:

使用 app:popupTheme="@style/ThemeOverlay.AppCompat.Light" 而不是 android:popupTheme

作为ActionBar使用

@Override public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.blah);      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);     setSupportActionBar(toolbar); }

独立使用

@Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.blah);  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);  // Set an OnMenuItemClickListener to handle menu item clicks  toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {   @Override   public boolean onMenuItemClick(MenuItem item) {    // Handle the menu item    return true;   }  });  // Inflate a menu to be displayed in the toolbar  toolbar.inflateMenu(R.menu.your_toolbar_menu); } 

一般使用疑问

1. 没有了splitActionBarWhenNarrow,用两个Toolbar模拟是否可以?

不可以,这种方式是有问题的。

两个Toolbar放在布局中后,下面的Toolbar不能顶到最左边。

stackoverflow : How to center action menu on toolbar 中有详细的描述。

问题中给出了 SplitToolbar 的解决方案,但我尝试后发现这种解决方案仍然有轻微的偏移。

2. 使用Toolbar后,NavigationIcon不垂直居中?

Toolbar的layout_height属性,要用“?attr/actionBarSize”而不是“?android:attr/actionBarSize”,替换后可解决NavigationIcon不垂直居中的问题。原因是系统的actionBarSize比AppCompat中的要小。使用“?android:attr/actionBarSize”调用了较小的那个。

ActionMode配置

以前ActionMode都是从Activity启动,使用Material主题后应该使用toolbar启动。(相应的,这个界面需要改写成NoActionBar,然后使用Toolbar)

代码示例:

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.startActionMode(mActionModeCallback)

同时需要在主题中配置

<item name="windowActionModeOverlay">true</item>

ActionMode背景色替换

<!--action Mode背景--> <item name="actionModeBackground">@color/theme_color_action_mode</item>

如何实现和Inbox一样的ActionMode

Material适配2 - 高级篇

可以看到,ActionMode开启时,顶部的Status Bar颜色也跟着改变了

这种功能Theme中并没有提供属性来修改。但是联想到入门篇提到的代码设置status bar颜色,这里就不难实现了。

代码共享下:

private int mOldStatusBarColor = -1; private void setActionModeStatusBarColor(int colorResId) {  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {   mOldStatusBarColor = mActivity.getWindow().getStatusBarColor();   setStatusBarColorCore(mActivity.getResources().getColor(colorResId));  } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setStatusBarColorCore(int color) {  mActivity.getWindow().setStatusBarColor(color); } private void resetStatusBarColor() {  if (mOldStatusBarColor != -1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){   setStatusBarColorCore(mOldStatusBarColor);   mOldStatusBarColor = -1;  } } 

开启时调用set,销毁时调用reset即可。

P.S. : 上面的计数可以通过setTitle来完成。

网上提供的错误方式(已踩坑,请绕行)

保持Activity调起,使用 android:windowActionModeOverlay 属性。

看似让ActionMode浮在了ActionBar上,但其实存在很大问题。

这种方式在4.4以下会使用Holo风格(overflow图标可以看出来,不是三个原点,是三个方块),且ActionMode比ActionBar小一些(可以看到蓝色底边是ActionBar)

Material适配2 - 高级篇

其他Material适配必备贴

常用效果及实现

正文到此结束
Loading...