ToolBar 是 Android5.0 推出的导航控件,官方在某些程度上认为 ActionBar 限制了 android app 的开发与设计的弹性,所以 ToolBar 的出现可以说是用来代替 Action_Bar。的确对比 Action_Bar,ToolBar 的使用灵活的多,我们可以将我们想要的控件任意的添加在 ToolBar 里面,得到缤纷的效果。
官方考虑到仍有一部分用户的手机版本号低于5.0,所以,ToolBar 也放进了 support v7 包内,如果使用的是的 Android5.0 以下,那么可以在配置文件中引入它的包:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
}
不过如今的手机已经基本上都是 Android5.0 以上的了,我就不使用兼容包里的 ToolBar 了,其实使用方法都是差不多的。
因为新建工程都会默认使用有 ActionBar 的主题,但 ToolBar 是不能和 ActionBar 共用的,所以我们要想使用 ToolBar,就必须去掉 ActionBar:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
style>
去掉 ActionBar 的方法很多,我们可以直接换一个没有 ActionBar 的样式,还可以在样式里添加 item,设置隐藏 ActionBar:
要注意的是,使用 API Level 22以上编译的话,要拿掉前綴字:
<item name="windowActionBar">falseitem>
<item name="windowNoTitle">trueitem>
要把 ActionBar 隐藏起来很简单的修改就可以做到啦。
我们的 ToolBar 是一个控件,所以我们对它的使用可以放在 xml 布局文件里:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:text="标题"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Toolbar>
RelativeLayout>
我们在内部放了一个 TextView,这是与 ActionBar 最大的不同,因为 ToolBar 实际上是一个 ViewGroup,支持在其内部放入子View。
我们给 ToolBar 加了个 TextView,虽然成功显示出来了,但可能会问这跟 TextView 有什么不一样,一点都没有导航栏的样子,不过这只是最简单的用法,它还有很多的功能和属性要介绍。
看上去与在界面上添加了一个 TextView 没什么两样,就是因为我们 ToolBar 的颜色与界面的背景色一样,这样就看不出在 TextView 外还有一个 ViewGroup,所以我们要想让它像是导航栏,首先来改变 ToolBar 的颜色。
我们要在 styles.xml 中给样式的属性赋值,然后在布局中把值赋给控件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Toolbar
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:text="标题"
android:textSize="20sp"
android:textColor="#FFFFFF"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Toolbar>
RelativeLayout>
android:background="?attr/colorPrimary"
用这种引用方式就能把我们设置的颜色传给 ToolBar 了,其它的控件也能使用这个方式。
我们可以看到无论是 ToolBar 还是状态栏的颜色都被我们改变了,这就是我们设置的那几个颜色属性的功劳了。刚刚是直接就设置了,现在来说说它们各个的功能:
colorPrimaryDark:状态栏底色,在样式 (styles) 或是主题 (themes) 里进行设定。
App bar 底色:这个设定分为二部分,若我们仍是使用 ActionBar ,则直接在样式里设定 colorPrimary 参数即可;若是采用 toolbar 的话,则要在布局中设定 ToolBar 控件的 background 属性,就像上面一样。
navigationBarColor:导航栏底色。
windowBackground:主视窗底色。
colorAccent:对应EditText编辑时、RadioButton选中、CheckBox等选中时的颜色。
textColorPrimary:如果想要改变toolbar的title、subtitle以及menu中文字的颜色,可以利用这个属性,不过 ToolBar 要想用同样要在控件中设置属性
<item name="android:textColorPrimary">#FFFFFF
android:textColor="?android:attr/textColorPrimary"
ToolBar 有许多可以设置的属性,我们可以设置它的 title、subtitle、logo 还有导航栏图标等等,这既可以在 xml 中设置也可以用代码完成:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Toolbar
android:title="Title"
android:subtitle="SubTitle"
android:navigationIcon="@mipmap/list"
android:subtitleTextColor="?android:attr/textColorPrimary"
android:logo="@mipmap/ic_launcher"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:text="标题"
android:textSize="20sp"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Toolbar>
RelativeLayout>
可以看到我们用 ToolBar 给我们的组件添加了不少东西(如果是 support_v7,需要用 app 来引用属性),现在再用代码来实现一下:
public class MainActivity extends AppCompatActivity {
private Toolbar mToolBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolBar = (Toolbar) findViewById(R.id.id_toolbar);
// 主标题,默认为app_label的值
mToolBar.setTitle("Title");
// 副标题
mToolBar.setSubtitle("SubTitle");
mToolBar.setSubtitleTextColor(Color.parseColor("#FFFFFF"));
//App Logo
mToolBar.setLogo(R.mipmap.ic_launcher);
//侧边栏的按钮
mToolBar.setNavigationIcon(R.mipmap.list);
//取代原本的actionbar,设置导航图标要在setActionBar方法之后
setActionBar(mToolBar);
}
}
这里稍微提一下,我们这里使用的不是 support_v7 的 ToolBar,所以在导包的时候要注意,而且这里使用的是 setActionBar() 而不是 setSupportActionBar() 方法。
用过 ActionBar 的朋友都知道,我们经常要 res/menu 目录下编写我们的导航栏选项,这无疑是导航栏中相当重要的部分,既然用 ToolBar 替代了 ActionBar,那当然就不得不提了。
首先我们还是要编写 menu_main.xml 文件,Android Studio 取消了默认生成 menu 文件夹,我们新建一个即可:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
<item android:id="@+id/action_search"
android:title="Search"
android:icon="@mipmap/toolbar_search_icon"
android:showAsAction="always"/>
<item android:id="@+id/action_notifications"
android:title="notifications"
android:icon="@mipmap/toolbar_add_icon"
android:showAsAction="always"/>
menu>
我们在 ToolBar 里增添两项,关于 showAsAction 的介绍我在博客Android控件–RecyclerView做过说明,有兴趣的朋友可以看看,在这里就不说明了。
这里用了这样一个属性 android:showAsAction=”always”,大家可能会看到 Android Studio 对这行报错,因为它不建议使用这个,但其实这个并不影响运行,之所以用这个是因为我们使用的不是 support v7 包的 ToolBar,所以不能用下面的方式:
xmlns:app="http://schemas.android.com/apk/res-auto"
app:showAsAction=""
app 是自定义的命名空间,它适用于 support v7 包,用这个我们加的这两个 item 就不会显示在 ToolBar 上,而是在溢出列表里,当然如果使用的是 support v7 下的 ToolBar,就要用这个啦。
然后自然就是要重写 onCreateOptionsMenu(),把设置好的菜单添加过去 :
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
因为我们用得是 always,如果是 always 或者是 ifroom,那么有三个黑点的 overflow 溢出列表就不会出现在 ToolBar 中,似乎默认就是出现两个 item,列表 item 就不会出现。我们把 add 的 item 设为 never。
菜单有了,就要为 item 添加点击事件才能实现想要的功能,这样菜单才会有实际用途,添加点击事件也很方便:
mToolBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.tool_search :
Toast.makeText(MainActivity.this, "search click", Toast.LENGTH_SHORT).show();
break;
case R.id.tool_add :
Toast.makeText(MainActivity.this, "add click", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
});
ToolBar 还有一个比较特殊的点击,那就是导航栏的图标的点击事件,我来介绍一下:
//设置NavigationIcon的点击事件,需要放在setActionBar()之后设置才会生效,
//因为setActionBar里面也会调用setNavigationOnClickListener,会覆盖我们设置的
mToolBar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "click", Toast.LENGTH_SHORT).show();
}
});
在上面我们知道溢出列表的图标的样式,有时候我们并不满意这个图标,就像微信一样它把 overflow 的图标变成了我们上面用过的“+”,所以我们来讲讲怎么变换 overflow 的图标。
overflow 是系统给我们的 item,所以我们要想修改那就要到 style.xml 文件中变更我们的样式:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
。。。
<item name="android:actionOverflowButtonStyle">@style/MyOverflowStyle
style>
<style name="MyOverflowStyle" parent="@android:style/Widget.Holo.Light.ActionButton.Overflow">
<item name="android:src">@mipmap/toolbar_add_icon
style>
我们将我们制定好的 overflow 的样式加入 APP 引用的样式里去,AppTheme 就是系统默认引用样式的名字。这里要使用的是 mipmap,因为这里引用图片不会做处理,而在 mipmap 里的图片会适应我们 Android 机的分辨率,大家可以引用 drawable 和 mipmap 里的图片做比较。
这样就设置了我们自己的图片为 overflow 的图标啦。
我们在上面实现了 overflow 的更换图标以及显示,但我们看到因为设置的字体是白色的,而背景色同样是白的,所以我们很难看清它显示的是什么,并且列表展开的位置也不是很好,我们应该让它显示在 ToolBar 下才好,所以现在就来改变这些属性。
这个背景色是系统给我们的,所以我们要改也可以在 style.xml 中重写它的样式:
<style name="ToolbarPopupTheme" parent="@style/ThemeOverlay.AppCompat.Dark">
<item name="android:colorBackground">#000000item>
style>
设置好样式后,在 ToolBar 里有个 popupTheme,就是给我们用来设置菜单的样式的:
android:popupTheme="@style/ToolbarPopupTheme"
字体的大小颜色对于美观来说也是至关重要的,在很多时候就会用上:
<item name="android:textSize">18spitem>
<item name="android:textColor">#bcbcbc
直接在使用的 style 中加上对 Text 的属性修改即可。
我们用 overlapAnchor 这个属性就可以修改它的位置,使它处于Toolbar之下,这样会变得更美观:
把它设为 false 意味着我们不要溢出列表覆盖我们的 ToolBar。
在上面的说明中我们可以看到,系统提供给我们的 overflow 列表设定起来并不是特别容易,而且有时也无法满足我们的需求,这个时候我们可以舍弃系统的 overflow,自己自定义个弹出窗口,这就要用到 PopupWindow,它用来制作这种 layout 是很灵活的。
我们先来为 PopupWindow 写一个布局,action_toolbar_overflow.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#1b1c1d"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:id="@+id/ll_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="item1"
android:textColor="#ffffff"
android:textSize="20sp" />
LinearLayout>
<LinearLayout
android:id="@+id/ll_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="item2"
android:textColor="#ffffff"
android:textSize="20sp" />
LinearLayout>
<LinearLayout
android:id="@+id/ll_item3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="item3"
android:textColor="#ffffff"
android:textSize="20sp" />
LinearLayout>
LinearLayout>
我们既然弃用了系统的,那我们应该再设置一个图标设置点击弹出窗口,就用之前的 add 的 item:
<item android:id="@+id/tool_overflow"
android:icon="@mipmap/toolbar_add_icon"
android:showAsAction="always"
android:title="overflow" />
然后就用代码设置到我们的 Activity 里:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Toolbar mToolBar;
private PopupWindow mPopupWindow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolBar = (Toolbar) findViewById(R.id.id_toolbar);
setSupportActionBar(mToolBar);
mToolBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.tool_search :
Toast.makeText(MainActivity.this, "search click", Toast.LENGTH_SHORT).show();
break;
case R.id.tool_overflow :
popUpMyOverflow();
break;
}
return true;
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
/**
* 弹出自定义的popWindow
*/
public void popUpMyOverflow() {
//获取状态栏高度
Rect frame = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
//状态栏高度+toolbar的高度
int yOffset = frame.top + mToolBar.getHeight();
if (null == mPopupWindow) {
//初始化PopupWindow的布局
View popView = getLayoutInflater().inflate(R.layout.action_overflow_popwindow, null);
//popView即popupWindow的布局,ture设置focusable.
mPopupWindow = new PopupWindow(popView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, true);
//必须设置BackgroundDrawable后setOutsideTouchable(true)才会有效
mPopupWindow.setBackgroundDrawable(new ColorDrawable());
//点击外部关闭。
mPopupWindow.setOutsideTouchable(true);
//设置一个动画。
mPopupWindow.setAnimationStyle(android.R.style.Animation_Dialog);
//设置Gravity,让它显示在右上角。
mPopupWindow.showAtLocation(mToolBar, Gravity.RIGHT | Gravity.TOP, 0, yOffset);
//设置item的点击监听
popView.findViewById(R.id.ll_item1).setOnClickListener(this);
popView.findViewById(R.id.ll_item2).setOnClickListener(this);
popView.findViewById(R.id.ll_item3).setOnClickListener(this);
} else {
mPopupWindow.showAtLocation(mToolBar, Gravity.RIGHT | Gravity.TOP, 0, yOffset);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ll_item1:
Toast.makeText(this, "item1", Toast.LENGTH_SHORT).show();
break;
case R.id.ll_item2:
Toast.makeText(this, "item2", Toast.LENGTH_SHORT).show();
break;
case R.id.ll_item3:
Toast.makeText(this, "item3", Toast.LENGTH_SHORT).show();
break;
}
//点击PopWindow的item后,关闭此PopWindow
if (null != mPopupWindow && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
}
}
}
里面大都是 PopUpWindow 的基本方法调用,有了注解知道方法的作用就知道为什么使用了,没什么好说明的,所以就直接看看效果吧:
可以看到它对比系统组件的灵活性,如果好好设计肯定会得到十分美观的效果。
大家肯定都用过微信,微信的搜索图标点击后会出现搜索栏:
现在我们就来看看怎么实现这个功能:
<item android:id="@+id/tool_search"
android:actionViewClass="android.widget.SearchView"
android:icon="@mipmap/toolbar_search_icon"
android:showAsAction="ifRoom|collapseActionView"
android:title="search" />
我们只要给它设置使用 SearchView,如果用得是 Support_v7 包下的 ToolBar,那就要用:
app:actionViewClass="android.support.v7.widget.SearchView"
showAsAction 也要设置为 ifRoom|collapseActionView,我们的 SearchView 才可以展开。
要想在 ToolBar 获得 SearchView 的实例,我们可以这样做:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem item = menu.findItem(R.id.tool_search);
SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(item);
mSearchView.setIconifiedByDefault(false);
return true;
}
setIconifiedByDefault() 方法可以让我们在点击图标跳转到别的 Activity 时使用 SearchView 为展开状态。
点开 SearchView 的源码,我们可以找到搜索图标的 ID,如果你对图标不满意,可以进行修改:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem item = menu.findItem(R.id.tool_search);
SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(item);
int search_mag_icon_id = mSearchView.getContext().getResources().
getIdentifier("android:id/search_mag_icon", null, null);
// 获取搜索图标
ImageView mSearchViewIcon = (ImageView) mSearchView.findViewById(search_mag_icon_id);
mSearchViewIcon.setImageResource(R.mipmap.toolbar_search_icon);
mSearchView.setIconifiedByDefault(false);
return true;
}
是 Support_v7 包的用如下代码即可:
ImageView imageView = (ImageView) mSearchView.findViewById(R.id.search_mag_icon);
imageView.setImageResource(R.mipmap.toolbar_search_icon);
这个箭头的修改我并没找到方法,大家要注意的是,如果你的样式是 Light 的话,这两个系统的图标就会是黑色的,想要白色的图标,就要使用没有 light 的样式,如:
Theme.AppCompat.NoActionBar
不过这时候 WindowBackground 就变成黑色的了,我们还要在其中添加 item 进行修改:
<item name="android:windowBackground">@color/windowBackgrounditem>
<color name="windowBackground">#FFFFFF
所以说对于系统所给的样式的修改是比较让人烦恼的。
结束语:本文仅用来学习记录,参考查阅。