定制你的专属Toolbar

引言

Toolbar是随着Material Design一起推出的,不过需要注意的是它在support-v7包中,而不是support-design library中。虽然从Android 5.0开始就使用Toolbar了,但是一直没有系统地总结过。正好最近几天有点时间,就讲一下Toolbar的样式定制好了。

1.Theme的选择

由于要用Toolbar代替ActionBar,所以要求Activity的样式中无ActionBar,否则会报错。所以AppTheme一般会选择Theme.AppCompat.Light.NoActionBar这个主题,但是如果想要Dark的主题呢?
其实也可以,就是需要在style中加上以下两句:

1
2
3
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>

有些blog中说只能使用Theme.AppCompat.Light.NoActionBar这个主题,显然是错误的。

如下是Theme.AppCompat.Light.DarkActionBar在Android 4.4.4上的效果:

如下是Theme.AppCompat.Light.NoActionBar的效果:

最后是普通的Theme.AppCompat.NoActionBar的效果:

对应的style如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="NormalNoActionTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="LightNoActionTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

显然,不同的Theme会有不同的效果,实际上可以利用Android Studio中Tools->Android->ThemeEditor来预览主题效果。如下所示:

另外,需要注意的是,如果选择NoActionBar的主题,就需要setSupportActionBar(),否则Toolbar不显示。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
toolbar.setTitle("ToolbarSample");
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.menu_main,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
if(item.getItemId()==android.R.id.home){
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

activity_main.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.whaley.customtoolbarsample.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
/>
</RelativeLayout>

另外,menu_main.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<item
android:id="@+id/action_edit"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
android:orderInCategory="80"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"
/>
<item
android:id="@+id/action_share"
android:orderInCategory="90"
android:icon="@drawable/abc_ic_menu_share_mtrl_alpha"
android:title="Share"
app:showAsAction="always"
/>
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="Settings"
app:showAsAction="never"/>
</menu>

2.AppBarLayout

上面没有讲到的一点是,在使用NormalNoActionTheme和LightNoActionTheme时,Toolbar显示的效果和ThemeEditor中不一样。

LightNoActionTheme对应的ThemeEditor预览效果如下:

NomalNoActionTheme的预览效果如下:

显然,Toolbar的效果和前面图片的效果不一样,那是Android Studio的bug吗?

其实并不是,真正的原因是预览效果默认使用AppBarLayout,实际上Toolbar很少单独使用,都会与AppBarLayout结合使用。

AppBarLayout是support-design library中的成员,需要在build.gradle中加入如下语句:

1
compile 'com.android.support:design:25.0.1'

此时main.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.whaley.customtoolbarsample.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
/>
</android.support.design.widget.AppBarLayout>
</RelativeLayout>

现在,LightNoActionTheme的运行效果如下:

NormalNoActionTheme的运行效果如下:

显然,此时与ThemeEditor中的预览效果就一致了。

2.定制Toolbar

一般来说,在开发一个符合Material Design规范的App时,会先根据整体的设计风格选择一个AppTheme,之后所有的页面都是在这个框架下,以保证风格统一。
其中Theme中各颜色的对应如下:

Toolbar与Theme中的颜色对应如下:

但是,有些页面可能比较特别,或者有特殊用途,不能再按照AppTheme的风格,此时需要定制。幸运的是,目前support包允许对View设置Theme,从而可以方便地实现样式定制。

以上面使用LightNoActionTheme为例,如果想要在LightNoActionTheme下,使MainActivity中Toolbar的样式发生变化,那么可以为Toolbar定制theme和popupTheme,其中popupTheme对应OverflowMenu的样式。

如下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.whaley.customtoolbarsample.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
app:theme="@style/AppTheme.AppBarOverlay"
app:popupTheme="@style/AppTheme.AppBarPopupOverlay"
/>
</android.support.design.widget.AppBarLayout>
</RelativeLayout>

其中AppTheme.AppBarOverlay和AppTheme.AppBarPopupOverlay如下:

1
2
3
4
5
6
7
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
</style>
<style name="AppTheme.AppBarPopupOverlay" parent="ThemeOverlay.AppCompat.Light">
</style>