toolbar总结及简单封装
Android3.0之后,Google引入了ActionBar,想统一安卓应用的导航栏样式。但由于ActionBar难以定制,很大程度上限制了开发人员,很多开发者放弃了ActionBar的使用,而是采用普通的ViewGroup封装自己的App Bar。后来,2014的Google I/O大会上Material Design横空出世,support library推出了ToolBar控件,一个定制化的ViewGroup,来完善ActionBar的使用。
简单使用
首先在style Theme中隐藏现有的ActionBar,
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
//
</style>
然后在布局中添加v7包中的Toolbar控件(添加依赖:compile 'com.android.support:appcompat-v7:25.3.1')
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:title="个人中心"
app:titleTextColor="@android:color/white" />
最后在Activity代码中使用Toolbar替换ActionBar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
像logo、title、subTitle、navigationIcon等,都可以通过app:xxx属性和java代码来控制,titleTextAppearance、subtitleTextAppearance也可以用来控制标题颜色和大小,如:
<style name="Theme.ToolBar.Base.Title" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">18sp</item>
<item name="android:textColor">@android:color/white</item>
</style>
Options Menu
在menu资源目录下新建一个 menu_activity_base_top_bar.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/action_menu1"
android:title="客户"
android:icon="@android:drawable/ic_menu_guest"
app:showAsAction="always" />
</menu>
在java代码中为Toolbar添加对应的Menu Item,并设置点击事件:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_activity_base_top_bar, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.action_menu1:
//TODO menu1
break;
}
return super.onOptionsItemSelected(item);
}
可以通过修改Toolbar的theme和popupTheme属性来改变menu中按钮的颜色,使之与主题色搭配:
<android.support.v7.widget.Toolbar
......
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</android.support.v7.widget.Toolbar>
也可用使用theme中的actionMenuTextColor属性来设置menu item字体的颜色:
<style name="OverFlowMenuTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:actionMenuTextColor">@android:color/white</item>
<item name="overlapAnchor">false</item>
</style>
相应Toolbar中theme也要改变:
<android.support.v7.widget.Toolbar
//......
app:popupTheme="@style/OverFlowMenuTheme"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</android.support.v7.widget.Toolbar>
一般也是采用这种做法,将toolbar相关属性集中写到一个style中,
<style name="OverFlowMenuTheme" parent="Theme.AppCompat.NoActionBar">
<!-- 设置Menu菜单的背景色 -->
<item name="android:itemBackground">@android:color/white</item>
<!-- 设置Menu菜单的字体颜色 -->
<item name="android:textColorPrimary">@android:color/black</item>
<!-- 设置Menu窗口不覆盖Toolbar视图 -->
<item name="overlapAnchor">false</item>
</style>
Up Enable
在二级界面等Activity中,可以在toolbar左侧设置一个返回按钮:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
然后通过android.R.id.home监听返回按钮的点击事件,比如返回上级:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
}
return super.onOptionsItemSelected(item);
}
也可以通过为Toolbar设置导航图标的点击事件达到这个效果:
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
toolbar左侧默认的图标是一个箭头,我们也可以自定义图标:
toolbar.setNavigationIcon(iconResId);
//或者
app:navigationIcon="@mipmap/ic_launcher"
然后按照上面的两种方式添加其他的点击事件。
标题居中
Toolbar是一个定制化的ViewGroup,所以可以在Toolbar里面放置一个TextView控件作为居中标题来使用,再将Toolbar自己的title隐藏起来就是了:
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:popupTheme="@style/Theme.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" >
<TextView
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:textColor="@android:color/white"
android:layout_gravity="center" />
</android.support.v7.widget.Toolbar>
在TextView中设置style属性,与ActionBar.Title保持一致,然后还要在代码中隐藏Toolbar自己的title,在布局中设置app:title=""是不会起作用的,必须到代码中隐藏。
getSupportActionBar().setDisplayShowTitleEnabled(false);
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));
添加Options Menu时,需要额外调用setHasOptionsMenu(true);方法,确保onCreateOptionsMenu()方法得以调用,并且onCreateOptionsMenu()方法多了一个MenuInflater参数:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.search, menu);
}
简单封装
把title,leftbutton,rightbutton封装到抽象类BaseTopBarActivity中,要使用Toolbar的Activity直接继承BaseTopBarActivity就行了。
先看布局,activity_base_top_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:popupTheme="@style/Theme.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<TextView
android:id="@+id/toolbar_title"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="@android:color/white" />
</android.support.v7.widget.Toolbar>
<FrameLayout
android:id="@+id/viewContent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
FrameLayout中用来放置继承此BaseTopBarActivity的Activity的布局。
BaseTopBarActivity.java
public abstract class BaseTopBarActivity extends AppCompatActivity {
//导航栏
private Toolbar toolbar;
//放置activity的布局
private FrameLayout viewContent;
//标题
private TextView toolBarTitle;
//导航栏左侧点击事件
private OnClickListener onClickListenerTopLeft;
//导航栏右侧icon id
private int menuResId;
//导航栏右侧文字
private String menuStr;
//导航栏右侧点击事件
private OnClickListener onClickListenerTopRight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_top_bar);
toolbar = (Toolbar) findViewById(R.id.toolbar);
viewContent = (FrameLayout) findViewById(R.id.viewContent);
toolBarTitle = (TextView) findViewById(R.id.toolbar_title);
setSupportActionBar(toolbar);
//隐藏toolbar自身的title
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// getSupportActionBar().setDisplayShowTitleEnabled(false);
//设置framelayout中的布局
LayoutInflater.from(BaseTopBarActivity.this).inflate(getContentView(), viewContent);
init(savedInstanceState);
}
protected abstract int getContentView();
protected abstract void init(Bundle savedInstanceState);
/**
* 设置title
* @param title
*/
protected void setTitle(String title) {
if (!TextUtils.isEmpty(title)) {
toolBarTitle.setText(title);
}
}
/**
* 左右图标的点击事件
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onClickListenerTopLeft.onClick();
break;
case R.id.menu1:
onClickListenerTopRight.onClick();
break;
}
return true;
}
/**
* 设置左侧自定义图标和监听事件
* @param iconResId
* @param onClickListener
*/
protected void setTopLeftButton(int iconResId, OnClickListener onClickListener) {
toolbar.setNavigationIcon(iconResId);
this.onClickListenerTopLeft = onClickListener;
}
/**
* 不设置图标的话,左侧默认显示系统的箭头图标。
* @param onClickListener
*/
protected void setTopLeftButton(OnClickListener onClickListener) {
this.onClickListenerTopLeft = onClickListener;
}
protected void setTopRightButton(String menuStr, OnClickListener onClickListener) {
this.setTopRightButton(menuStr, 0, onClickListener);
}
/**
* 设置右侧图标,文字和点击事件
* @param menuStr
* @param menuResId
* @param onClickListener
*/
protected void setTopRightButton(String menuStr, int menuResId, OnClickListener onClickListener) {
this.menuResId = menuResId;
this.menuStr = menuStr;
this.onClickListenerTopRight = onClickListener;
}
/**
* 加载右侧菜单
* @param menu
* @return
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (menuResId != 0 || !TextUtils.isEmpty(menuStr)) {
getMenuInflater().inflate(R.menu.menu_activity_base_top_bar, menu);
}
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (menuResId != 0) {
menu.findItem(R.id.menu1).setIcon(menuResId);
}
if (!TextUtils.isEmpty(menuStr)) {
menu.findItem(R.id.menu1).setTitle(menuStr);
}
return super.onPrepareOptionsMenu(menu);
}
public interface OnClickListener {
void onClick();
}
}
然后在MainActivity中继承BaseTopBarActivity,设置title及其他属性即可。
public class MainActivity extends BaseTopBarActivity {
@Override
protected int getContentView() {
return R.layout.activity_main;
}
@Override
protected void init(Bundle savedInstanceState) {
setTitle("个人中心");
setTopLeftButton(R.mipmap.ic_launcher, new OnClickListener() {
@Override
public void onClick() {
Toast.makeText(MainActivity.this, "讲道理这里应该是返回", Toast.LENGTH_SHORT).show();
}
});
setTopRightButton("button", R.mipmap.ic_launcher, new OnClickListener() {
@Override
public void onClick() {
Toast.makeText(MainActivity.this, "点击了右上角按钮", Toast.LENGTH_SHORT).show();
}
});
}
}