ActionBar vs ToolBar

ToolBar是Android 5.0推出的一个新的导航控件用于取代之前的ActionBar,具有可定制性、灵活性、Material Design风格等优点。官方考虑到仍有一部分用户的手机版本号低于5.0,所以,ToolBar也放进了support v7包内,使得低版本的系统也能使用上ToolBar。

使用Android Studio新创建一个项目的话,默认MainActivity是使用的Toolbar,看一下MainActivity的布局文件

MainActivity layout xml

看一下manifest里面对MainActivity的声明

MainActivity in manifest

MainActivity声明自己的theme是NoActionBar,表示是没有标题栏,而布局文件中又有ToolBar,toolbar就起到了标题栏的作用。

在onCreate的时候设置如下代码,就可以把ActionBar上的所有操作都转移到ToolBar上。

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);       

在这个新创建的项目中,可以看一下res/values/colors.xml里面的内容,有三个color值,其中colorPrimary是标题栏的颜色,colorPrimaryDark是状态栏的颜色。

res value color

在5.0及以上是能设置状态栏的颜色的,在5.0以下状态栏的颜色一般都是黑色。

ActionBar与Theme

1.首先要修改app的主题Theme

MaterialDesign的Theme有:

  • @android:style/Theme.Material (dark version)
  • @android:style/Theme.Material.Light (light version)
  • @android:style/Theme.Material.Light.DarkActionBar

与之对应的Compat Theme有:

  • Theme.AppCompat
  • Theme.AppCompat.Light
  • Theme.AppCompat.Light.DarkActionBar

首先看一下这三个主题的区别

主题Theme.AppCompat.Light.DarkActionBar

theme light darkActionBar

在5.x上是这样的

darkActionBar

看到colorPrimary是标题栏的颜色,看到colorPrimaryDark是状态栏的颜色

主题Theme.AppCompat.Light

在5.x上是这样的。看到是如下图。标题栏上的文字的颜色变了。

light

Theme.AppCompat.NoActionBar

在5.x上是这样的。

noactionbar

在一些定制ROM下,颜色就不一定了。比如说我手边有一台华为Honor,5.x,状态栏始终是绿色的,在任何app下都是。让我一开始的时候忙了半天!😂

Toolbar代替ActionBar

先引入v7包,compile 'com.android.support:appcompat-v7:23.1.1'

设置你自己的应用的主题为 Theme.AppCompat.Light.NoActionBar,如下方所示

1.先找到AndroidManifest.xml里面设置的应用主题

<application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        >
</application>

2.在styles.xml里面设置

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">#23354A</item>
        <item name="colorPrimaryDark">#23354A</item>
        <item name="colorAccent">#4092F1</item>
    </style>

或者设置成这样

 <style name="AppTheme" parent="Theme.AppCompat">
    <item name="windowActionBar">false</item>
    <!-- 使用 API Level 22 编译的話,要拿掉android前綴 -->
    <item name="android:windowNoTitle">true</item>
  </style>

3.在布局文件里面加上Toolbar,如下方所示

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/gray"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/id_toolbar"
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textColor="@color/black"/>
</LinearLayout>

4.在Activity里面设置toolbar代替actionbar

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.id_toolbar);
        setSupportActionBar(toolbar);
    }
}

如果没有设置为NoActionBar主题,直接设置setSupportActionBar(toolbar)的话,会crash,报下面的错:

This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.

5.看一下效果,如下图。额……效果和想象的不一样,标题栏怎么不是上面设置colorPrimary颜色?

toobar replace actionbar

设置Toolbar的样式

有两个方法可以设置Toolbar的样式,一个是在代码里面,一个是在布局文件里面。

1.先讲在代码里面设置Toolbar的样式。例如设置NavigationIcon、设置logo、设置title、设置subTitle,设置toolbar的背景颜色等。还可以使用setTitleTextColorsetTitleTextAppearance设置标题文字颜色或样式,使用setSubtitleTextColorsetSubtitleTextAppearance设置副标题的文字颜色或样式

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
        toolbar.setLogo(R.mipmap.ic_launcher);
        toolbar.setTitle("Title");
        toolbar.setSubtitle("Sub title");
        toolbar.setNavigationIcon(R.drawable.icon_titlebar_navi);
        toolbar.setBackgroundResource(R.color.colorPrimary);
        setSupportActionBar(toolbar);
    }

2.给Toolbar添加几个menu。定义一个menu_mainactivity.xml。如果只设置了title而没有设置icon,那么会显示文字。此处设置app:showAsAction命名空间为app而不是android,是因为showAsAction属性是在Support v7包里面的,不是原生SDK内部的,故不能使用android作为命名空间。always表示一定要显示在标题栏上,ifRoom表示有空间就显示在标题栏上,never表示不显示在标题栏。不显示在标题栏上的menu,会显示在overflow window中。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item android:id="@+id/menu_phone"
        android:title="打电话"
        android:icon="@drawable/icon_menu_phone"
        app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_add"
          android:title="添加"
          android:icon="@drawable/icon_menu_add"
          app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_cancel"
          android:title="删除"
          android:icon="@drawable/icon_menu_cancel"
          app:showAsAction="always">
    </item>
</menu>

3.在代码里面设置toolbar的menu文件。在Activity里面重写下面这两个方法。

加载菜单

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_mainactivity, menu);
        return super.onCreateOptionsMenu(menu);
    }

设置对menu选中的监听

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.menu_phone) {
            Toast.makeText(MainActivity.this, "按了打电话", Toast.LENGTH_SHORT).show();
            return true;
        }
        if (id == R.id.menu_add) {
            Toast.makeText(MainActivity.this, "按了添加", Toast.LENGTH_SHORT).show();
            return true;
        }
        if (id == R.id.menu_cancel) {
            Toast.makeText(MainActivity.this, "按了删除", Toast.LENGTH_SHORT).show();
            return true;
        }
        if (id == R.id.menu_search) {
            Toast.makeText(MainActivity.this, "按了查找", Toast.LENGTH_SHORT).show();
            return true;
        }
        if (id == R.id.menu_checkbus) {
            Toast.makeText(MainActivity.this, "按了公交", Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

toolbar with menu

看上面的图,最左边是我设置的naviIcon,然后绿色机器人是设置的Logo,然后是title,subtitle,然后是三个menu。

如果menu很多的话,就不能都设置app:showAsAction="always"属性,这个属性表示是否在标题栏上显示。假如说我有五个menu如下,都设置了显示。那么会变成这个样子,menu把标题都挤没了

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item android:id="@+id/menu_phone"
        android:title="打电话"
        android:icon="@drawable/icon_menu_phone"
        app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_add"
          android:title="添加"
          android:icon="@drawable/icon_menu_add"
          app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_cancel"
          android:title="删除"
          android:icon="@drawable/icon_menu_cancel"
          app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_search"
          android:title="查找"
          android:icon="@drawable/icon_menu_search"
          app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_checkbus"
          android:title="公交"
          android:icon="@drawable/icon_menu_bus"
          app:showAsAction="always">
    </item>
</menu>

too many menu

在显示menu的时候,要注意展示方式,建议使用ifRoom,假如说把后面四个menu都改成ifRoom的展现方式,那么展现的结果是如下图所示。

menu xml set menu ifRoom

menu-ifRoom

menu-ifRoom-other-menu-only-show-text

可以看到,后面的那些放不下的menu都被缩起来了。点进去看一下,发现几个不满的是:

  1. 希望overflow window的位置挪到标题栏下边,紧贴着标题栏。
  2. 那个显示overflow window的三个点的颜色是黑色,看着挺违和,想换换。
  3. overflow window整个的颜色是白的,希望和标题栏颜色一致。
  4. overflow window里面只显示了文字。希望能文字和图标一起显示

对于第一个问题,即overflow的三个点的颜色,有两个个办法:

  • 第一个办法是
        Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
        toolbar.setLogo(R.mipmap.ic_launcher);
        toolbar.setTitle("Title");
        toolbar.setSubtitle("Sub title");
        toolbar.setNavigationIcon(R.drawable.icon_titlebar_navi);
        toolbar.setBackgroundResource(R.color.colorPrimary);
        toolbar.setOverflowIcon(getResources().getDrawable(R.drawable.icon_more, null));
        setSupportActionBar(toolbar);

即代码里面toolbar.setOverflowIcon(getResources().getDrawable(R.drawable.icon_more, null));设置OverflowIcon,使用一个图标特换系统的图标。如下图,我特意找了个横着的三点,以示区分。

setOverflowIcon

  • 第二个办法是

在Theme里面设置<item name="android:textColorSecondary">#FFFFFF</item>这个能改变那三个点的颜色

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:textColorSecondary">#FFFFFF</item>
    </style>  

set textColorSecondary to replace system overflow button color

但是在Theme里面设置textColorSecondary,不仅会影响到Toolbar的颜色,还会影响到其他地方的文字颜色。所以不推荐第二种方法。

第二个问题是overflow window的背景色,我希望和标题栏一样。方法如下:

1.定义一个Theme,里面设置背景颜色。这里设置的是android:colorBackground,如果设置popupBackground,则没什么效果。

    <style name="MyPopupTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:colorBackground">@color/colorPrimary</item>
    </style>

2.将刚刚定义的Theme作为的主题

toolbar.setPopupTheme(R.style.MyPopupTheme);

效果如下:

set background color of popupwindow

第三个问题是overflow window的位置,希望在标题栏下面。方法如下:

1.在刚刚定义的Theme里面,(其实上面的Theme不继承任何theme也行的),设置overlapAnchor为false。那么就到下面去了。

    <style name="MyPopupTheme">
        <item name="android:colorBackground">@color/colorPrimary</item>
        <item name="overlapAnchor">false</item>
    </style>

2.仍然是在代码中写上面的toolbar.setPopupTheme(R.style.MyPopupTheme);

效果如下:

overlapAnchor false

在api 21以下,overlapAnchor不需要加android前缀。在21及以上需要加前缀

在自定义的这个MyPopupTheme里面还可以设置overflow window的menu的样式,你如说设置颜色、字体大小等。

    <style name="MyPopupTheme">
        <item name="android:colorBackground">@color/colorPrimary</item>
        <item name="overlapAnchor">false</item>
        <item name="android:textColor">#FFFFFF</item>
        <item name="android:textSize">20sp</item>
    </style>

对于第四个问题,在overflow里面也同时显示图片和文字

在网上找了找,大部分建议的解决办法是,menu里面套menu。注意:套在里面一层menu里的item,不能写成的形式,这样会报错。应该写成的形式

假如说menu是这样的形式,那么效果如下图:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item android:id="@+id/menu_phone"
        android:title="打电话"
        android:icon="@drawable/icon_menu_phone"
        app:showAsAction="always">
    </item>
    <item android:id="@+id/menu_add"
          android:title="添加"
          android:icon="@drawable/icon_menu_add"
          app:showAsAction="ifRoom">
    </item>
    <item android:id="@+id/menu_cancel"
          android:title="删除"
          android:icon="@drawable/icon_menu_cancel"
          app:showAsAction="ifRoom">
    </item>
    <item
        android:id="@+id/menu_test"
        android:title="test"
        app:showAsAction="ifRoom">
        <menu>
            <item android:id="@+id/menu_search"
                  android:title="查找"
                  android:icon="@drawable/icon_menu_search"
                  app:showAsAction="ifRoom"/>
            <item android:id="@+id/menu_checkbus"
                  android:title="公交"
                  android:icon="@drawable/icon_menu_bus"
                  app:showAsAction="ifRoom"/>
        </menu>
    </item>
</menu>

overflow menu contain menus

点开那个“test”menu之后是这样的。可以看到下一层的menu有icon展现

overflow menus icons show

如果不给“test”这个menu添加title和icon的话,是不是子项就会挪上来呢?然而并不会。。。只留下一个空空的带有展开三角形的menu。展开后的情形在没有父title和icon的情况下,倒是不展示了。

remove title and icon of menu

show submenu when supermenu removed title and icon

那么加入把所有的menu都包含在一个item里面,那么显示overflow menu的icon的愿望是不是可以实现了呢?试一下。

最外面的壳是一个menu,图标采用的是上面的横着的三点。里面是各个menu子项。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item
        android:id="@+id/menu_test"
        android:title="test"
        android:icon="@drawable/icon_more"
        app:showAsAction="ifRoom">
        <menu>
            <item android:id="@+id/menu_phone"
                  android:title="打电话"
                  android:icon="@drawable/icon_menu_phone"
                  app:showAsAction="always"/>
            <item android:id="@+id/menu_add"
                  android:title="添加"
                  android:icon="@drawable/icon_menu_add"
                  app:showAsAction="ifRoom"/>
            <item android:id="@+id/menu_cancel"
                  android:title="删除"
                  android:icon="@drawable/icon_menu_cancel"
                  app:showAsAction="ifRoom"/>
            <item android:id="@+id/menu_search"
                  android:title="查找"
                  android:icon="@drawable/icon_menu_search"
                  app:showAsAction="ifRoom"/>
            <item android:id="@+id/menu_checkbus"
                  android:title="公交"
                  android:icon="@drawable/icon_menu_bus"
                  app:showAsAction="ifRoom"/>
        </menu>
    </item>
</menu>

效果如下。可以看到每个icon都显示出来了。

all menu in one menu show icons

这样虽然overflow里面的menu都有icon了,可是和我的初衷不符。我希望能在标题栏上显示的就在标题栏显示,在overflow上显示的也能展示icon。这个以后再研究。

Toolbar的标题居中显示

看上面几个图,现在的标题都是靠左的,想要标题居中,除了不使用Toolbar/actionbar自定义标题栏之外,还可以这样:

Toolbar其实是个ViewGroup,在里面是可以放入子视图的。放一个TextView,让它居中。

	<android.support.v7.widget.Toolbar
        android:id="@+id/id_toolbar"
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="我是标题"
            android:textColor="@color/white"/>
    </android.support.v7.widget.Toolbar>

textView center in Toolbar

看到原来的title和subtitle还是显示着,此处需要将它隐藏,那么就真正实现了标题居中。

在代码里面,setSupportActionBar之后加入setDisplayShowTitleEnabled,就ok了。title和subtitle就都不会显示。

        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayShowTitleEnabled(false);

setDisplayShowTitleEnabled


AppBarLayout 、Toolbar、TabLayout

AppBarLayoutTabLayout都是design这个包里面的,所以使用的时候先要在gradle里面引入compile 'com.android.support:design:24.1.0'。否则会报找不到AppBarLayout和TabLayout.

之前写过一篇选项卡视图TabLayout,这里再回忆一下

  • 1.首先在gradle里面引入compile 'com.android.support:design:24.1.0'

  • 2.此时定义这样的布局文件。先不把TabLayout放到AppBarLayout里面。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/gray"
    android:orientation="vertical">
    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        >
        <android.support.v7.widget.Toolbar
            android:id="@+id/id_toolbar"
            android:layout_height="?attr/actionBarSize"
            android:layout_width="match_parent">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="我是标题"
                android:textColor="@color/white"/>
        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.AppBarLayout>
    <android.support.design.widget.TabLayout
        android:id="@+id/id_tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"/>
</LinearLayout>
  • 3.在代码里面定义5个tab。
        TabLayout tabLayout = (TabLayout) findViewById(R.id.id_tablayout);
        tabLayout.addTab(tabLayout.newTab().setText("我是Tab1"));
        tabLayout.addTab(tabLayout.newTab().setText("我是Tab2"));
        tabLayout.addTab(tabLayout.newTab().setText("我是Tab3"));
        tabLayout.addTab(tabLayout.newTab().setText("我是Tab4"));
        tabLayout.addTab(tabLayout.newTab().setText("我是Tab5"));

default tablayout with 5 tabs

设置tab的文字颜色、指示条的颜色、指示条的高度,将tab扩展成9个tab,看看效果如下:

        tabLayout.setSelectedTabIndicatorColor(Color.WHITE);
        tabLayout.setSelectedTabIndicatorHeight(20);
        // 被选中是是白色,未被选中是是黑色
        tabLayout.setTabTextColors(Color.BLACK, Color.WHITE); 

htc-tablayout-with-more-tab-and-set-indicatorcolor-height-textcolor

看到tab多了之后,tab挤得很小,此时可以设置tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);,那么tab就可以滚动。效果如下: