一、简介
Android3.0之后,Google引入了ActionBar,想要统一安卓应用的导航栏样式。但是由于ActionBar难以定制,很大程度上限制了开发人员。较为常见的实现是使用普通的ViewGroup来封装自己的APP Bar,或者使用JakeWharton大神的ActionBarSherlock库。
自2014年Google I/O上Material Design横空出世后,市场上的应用又逐步趋向了样式的风格统一,support library中很快就出来了ToolBar控件,一个定制化的ViewGroup,来完善ActionBar的使用。
ToolBar是ActionBar的替代品,对于像我这样的菜鸟级Android开发者来说,对怎样使用ToolBar还是感到比较困惑,那今天就来学一下如何使用吧。
1.1去掉默认的ActionBar
在styles.xml中新建一个主题AppTheme.Base
:
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
并将这个style应用到application中,这样就可以去除掉所有Activity中默认的ActionBar了。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme.Base">
</application>
1.2修改ToolBar默认的文字颜色
在styles.xml中新建相应的style:
<style name="AppTheme.Toolbar">
<!--这个是API21以后才有的属性-->
<item name="android:colorControlNormal">@color/controlNormal</item>
<item name="android:textColorPrimary">@color/toolbarColor</item>
</style>
<style name="AppTheme.Toolbar.Popup">
<item name="android:textColorPrimary">@android:color/black</item>
</style>
然后将style应用到ToolBar上(注意在builde.gradle文件中添加support.v7包的依赖:compile ‘com.android.support:appcompat-v7:24.2.1’)
<android.support.v7.widget.Toolbar
android:id="@+id/tl_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:theme="@style/AppTheme.Toolbar"
app:popupTheme="@style/AppTheme.Toolbar.Popup"
app:elevation="4dp">
</android.support.v7.widget.Toolbar>
1.3如何加上返回按钮并实现返回:导航返回按钮的图标,还可以通过.setNavigationOnClickListener()
方法或者app:navigationIcon
属性来修改。
//设置ToolBar
private void setupToolBar() {
setSupportActionBar(mTlBar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);//启动返回按钮
}
//实现返回功能
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: //android.R.id.home是Android内置home按钮的id
finish();
break;
}
return super.onOptionsItemSelected(item);
}
二、Options Menu的用法
2.1与ActionBar的用法一致在res/menu
目录下新建一个search.xml文件,并且添加menu的相应内容:
<?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/search"
android:title="搜索"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="collapseActionView"
/>
</menu>
2.2在Activity中添加ToolBar对应的Menu Item,并设置点击事件:
//为ToolBar添加Menu Item
Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search, menu);
return super.onCreateOptionsMenu(menu);
}
//实现ToolBar的点击事件
Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: //android.R.id.home是Android内置home按钮的id
finish();
break;
case R.id.search://搜索Item的ID
Toast.makeText(this, "你要搜索啥??", Toast.LENGTH_SHORT).show();
break;
}
return super.onOptionsItemSelected(item);
}
2.3如果溢出按钮的颜色与主题色不一样,可以通过修改ToolBar的theme和popupTheme属性来改变。
<android.support.v7.widget.Toolbar
android:id="@+id/tl_bar"
app:elevation="4dp"
app:popupTheme="@style/AppTheme.Toolbar.Popup"
app:theme="@style/AppTheme.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary">
</android.support.v7.widget.Toolbar>
也可以使用theme中的actionMenuTextColor属性设置Menu Item的字体颜色。
<item name="android:actionMenuTextColor">@android:color/black</item>
一般情况下,都是将ToolBar相关的属性集中卸载一个style中。
<style name="AppTheme.Toolbar.Popup" >
<!--设置menu菜单的背景色-->
<item name="android:itemBackground">@color/homeTabText</item>
<!--设置Menu菜单的字体颜色-->
<item name="android:textColorPrimary">@android:color/black</item>
<!--设置Menu窗口不覆盖ToolBar视图-->
<item name="overlapAnchor">false</item>
</style>
2.4也可以在Java代码中也可以获取Menu Item对象,然后通过如下方法设置Item的显示与隐藏:
//获取Menu Item
MenuItem item = menu.findItem(R.id.search);
//设置Menu Item隐藏
item.setVisible(false);
//清除所有MenuItem
menu.clear();
三、ActionMenuView
ToolBar默认是将Menu内容显示在右边的,那么怎样可以将其显示在左边或者中间呢?不妨试试ActionMenuView。
ActionMenuView是将原来位于ToolBar中的Menu内容移到自己的名下,将一系列的Menu Item包括在其中,再将其搁置于ToolBar容器中。这样可以更方便的管理和显示Menu内容。所以原本独立的ToolBar控件,就有了一个Child。
3.1将Menu显示在ToolBar中间
<android.support.v7.widget.Toolbar
android:id="@+id/tl_bar"
app:elevation="4dp"
app:popupTheme="@style/AppTheme.Toolbar.Popup"
app:theme="@style/AppTheme.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary">
<android.support.v7.widget.ActionMenuView
android:id="@+id/amv_search"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</android.support.v7.widget.ActionMenuView>
</android.support.v7.widget.Toolbar>
3.2在Activity中,将Menu资源文件加载到ActionMenuView对象中去:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search, mAmvSearch.getMenu());
return super.onCreateOptionsMenu(menu);
}
3.3Menu Item的点击事件方法不需要改动,只需要将ActionMenu对象的点击事件设置转移就可以了:
mAmvSearch.setOnMenuItemClickListener(new ActionMenuView.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return onOptionsItemSelected(item);
}
});
3.4也可以利用getMenu()方法获取到Menu对象,动态添加或删除Menu Item,甚至加载另外一个Menu资源文件:
mAmvSearch.getMenu().clear();
getMenuInflater().inflate(R.menu.search, mAmvOther.getMenu());
3.5还可以利用ActionMenuView控制Menu Item的位置。
- Menu Item间距问题。Item的默认宽度是56dp,可以通过style中的属性进行修改,并将样式设置给Activity主题就可以了。
<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:actionButtonStyle">@style/MyActionButtonStyle</item>
</style>
<style name="MyActionButtonStyle" parent="@android:style/Widget.Holo.ActionBar">
<!--设置Menu Item的宽度-->
<item name="android:minWidth">72dip</item>
<!--设置Menu Item的padding值-->
<item name="android:padding">0dp</item>
</style>
- Menu Item图标高亮的问题。可以使用两套图标,在点击时修改图标资源。也可以借助ColorFilter使用一套图标也能做到,还可以减少APK文件大小。
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.option_walk:
item.getIcon().setColorFilter(ContextCompat.getColor(this,
android.R.color.darker_gray), PorterDuff.Mode.MULTIPLY);
break;
}
return super.onOptionsItemSelected(item);
}
四、actionLayout
通常情况下,ToolBar中的Menu Item只显示为一个Icon,通过标签定义title和icon属性即可。当然也有一些特殊情况,需要自定义Menu Item的内容,常见的就是搜索页面。
4.1定义一个layout文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/et_search"
android:hint="搜索你想要的"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
4.2通过actionLayout属性引入到标签中
<?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/search"
android:title="搜索"
app:actionLayout="@layout/menu_search"
app:showAsAction="always"
/>
</menu>
4.3注意事项
- menu_search布局文件的内容必须以RelativeLayout作为根容器布局,否则,actionLayout对应的视图宽度不足以填充满ToolBar的宽度,显示效果如同layout_width属性为wrap_content一样;
- actionLayout属性必须使用app作为命名空间,如果使用android的话,会导致menuitem对象通过getActionView()始终获取的对象为null。
五、ToolBar Shadow
根据Material Design设计规范,ToolBar是存在于页面Content之上的,有一个类似阴影效果的视觉差。
但是,无论是android:elevation属性,还是使用APPBarLayout包裹ToolBar,都只能兼容5.0及以上版本的系统。要想兼容一下的版本,就必须要自己实现阴影效果。
自己实现阴影效果有两种选择:
- 使用.9背景图;
- 使用
<gradient>
标签定制一个阴影渐变效果。
5.1新建xml阴影形状,res/drawable/shadow.xml文件
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@android:color/transparent"
android:endColor="#40000000"
android:angle="90" />
</shape>
5.2和ToolBar一起使用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/tl_bar"
app:elevation="4dp"
app:popupTheme="@style/AppTheme.Toolbar.Popup"
app:theme="@style/AppTheme.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"/>
<View
android:layout_width="match_parent"
android:layout_height="4dp"
android:background="@drawable/shadow" />
</LinearLayout>
为了保持不同设备APP体验的一致性,推荐使用这种方式来实现ToolBar阴影效果。
六、标题居中
ToolBar就是一个定制化的ViewGroup,所以可以在ToolBar里面放置一个TextView控件作为居中的标题来使用,再将ToolBar的Title隐藏起来即可实现ToolBar标题居中的效果。
<android.support.v7.widget.Toolbar
android:id="@+id/tl_bar"
app:elevation="4dp"
app:popupTheme="@style/AppTheme.Toolbar.Popup"
app:theme="@style/AppTheme.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary">
<TextView
android:id="@+id/amv_search"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
</TextView>
</android.support.v7.widget.Toolbar>
TextView设置了style属性,与ActionBar.Title保持一致,然后还要去掉ToolBar中自有的Title,在布局中使用的app:title=""就不会起作用了,会显示ActionBar的标题。隐藏ActionBar的标题的代码如下:
getSupportActionBar().setDisplayShowTitleEnabled(false);//设置不显示Title
七、Fragment中使用
有的时候,还需要在Fragment中使用ToolBar,比如Activity中不同的Tab显示不同的Fragment,同时每个Tab的ToolBar标题、Menu均不同,这时在Activity中使用同一个ToolBar就相当不方便了。我们可以在每个Fragment的布局中添加各自的ToolBar,然后在Fragment中单独控制。
与Activity中使用ToolBar有所不同的是,替换ActionBar时,需要给setSupportActionBar方法添加作用对象:
((AppCompatActivity)getActivity()).setSupportActionBar((Toolbar) mContentView.findViewById(R.id.tb_toolbar));
八、仿知乎主界面的实现
为了加深对ToolBar的理解,来使用ToolBar来实现知乎主页的效果。
1.xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/tl_bar"
app:elevation="4dp"
app:popupTheme="@style/AppTheme.Toolbar.Popup"
app:theme="@style/AppTheme.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary">
</android.support.v7.widget.Toolbar>
</LinearLayout>
2.menu菜单
<?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_search"
android:icon="@drawable/ic_search_white_24dp"
android:title="搜索"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_notification"
android:icon="@drawable/ic_notifications_active_white_24dp"
android:title="通知"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="设置"
app:showAsAction="never" />
<item
android:id="@+id/action_about"
android:orderInCategory="101"
android:title="关于"
app:showAsAction="never" />
</menu>
3.style
<style name="AppTheme.Toolbar">
<!--这个是API21以后才有的属性-->
<item name="android:colorControlNormal">@color/controlNormal</item>
<item name="android:textColorPrimary">@color/toolbarColor</item>
</style>
<style name="AppTheme.Toolbar.Popup" >
<!--设置menu菜单的背景色-->
<item name="android:itemBackground">@color/homeTabText</item>
<!--设置Menu菜单的字体颜色-->
<item name="android:textColorPrimary">@android:color/black</item>
<!--设置Menu窗口不覆盖ToolBar视图(true为覆盖ToolBar视图)-->
<item name="overlapAnchor">false</item>
</style>
4.Activity
public class IOSDetailActivity extends BaseActivity {
@BindView(R.id.tl_bar)
Toolbar mTlBar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ios_detail);
ButterKnife.bind(this);
mTlBar.inflateMenu(R.menu.ios_menu);
mTlBar.setNavigationIcon(R.drawable.ic_list_white_24dp);
mTlBar.setTitle("首页");
}
}
网友评论