Android Fragment使用Toolbar

Activity使用Toolbar

一般在Activity里使用Toolbar如下:
toolbar.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?actionBarSize">
</android.support.v7.widget.Toolbar>

xml调用:

1
2
<include
layout="@layout/toolbar" />

Activity一般写到基类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Toolbar initToolbar(int title) {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
return toolbar;
}
public Toolbar initToolbar(CharSequence title) {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
return toolbar;
}

Activity调用:

1
initToolbar("标题");

Fragment使用Toolbar

现在项目需求,每个title不一样,就不能像以上一样写一个toolbar,放在最外层。问题来了,Fragment如何使用Toolbar?像上面一样写吗?No,Fragment没有setSupportActionBar,于是Google,得到:

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
33
34
public class BaseFragment extends Fragment {
Activity mActivity;
AppCompatActivity mAppCompatActivity;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mActivity = getActivity();
}
public Toolbar initToolbar(int title) {
AppCompatActivity mAppCompatActivity = (AppCompatActivity) mActivity;
Toolbar toolbar = (Toolbar) mAppCompatActivity.findViewById(R.id.toolbar);
mAppCompatActivity.setSupportActionBar(toolbar);
ActionBar actionBar = mAppCompatActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
}
return toolbar;
}
public Toolbar initToolbar(CharSequence title) {
mAppCompatActivity = (AppCompatActivity) mActivity;
Toolbar toolbar = (Toolbar) mAppCompatActivity.findViewById(toolbarId);
mAppCompatActivity.setSupportActionBar(R.id.toolbar);
ActionBar actionBar = mAppCompatActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
}
return toolbar;
}
}

这样就解决Fragment使用Toolbar,却抛出了一个问题:Fragment标题错乱。

Fragment标题错乱

当出来这个问题时,我恍然大悟,Fragment里的Toolbar findViewById取的都是MainActivity,include不能共用一个ID,应该加个ID区别一下:

1
2
3
<include
android:id="@+id/program_toolbar"
layout="@layout/toolbar" />

BaseFragment 做相应的修改:

1
2
3
4
5
6
7
8
9
10
11
public Toolbar initToolbar(int toolbarId, int title) {
AppCompatActivity mAppCompatActivity = (AppCompatActivity) mActivity;
Toolbar toolbar = (Toolbar) mAppCompatActivity.findViewById(toolbarId);
mAppCompatActivity.setSupportActionBar(toolbar);
ActionBar actionBar = mAppCompatActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
}
return toolbar;
}

好的,这样标题就没有错乱了,然后又来了新的“需求”:Fragment如何让Toolbar菜单生效?

Fragment让Toolbar菜单生效

在activity中:

1
2
3
4
5
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}

在Fragment中:

1
2
3
4
5
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.pictrue_list, menu);
super.onCreateOptionsMenu(menu,inflater);
}

两者不同的地方在于:
1、一个有返回值(boolean类型),一个没有返回值。
2、Fragment中onCreateOptionsMenu的参数多了一个MenuInflater

想让Fragment中的onCreateOptionsMenu生效必须先调用setHasOptionsMenu方法,否则Toolbar没有菜单。

1
2
3
4
5
6
7
8
9
public class BaseFragment extends Fragment {
Activity mActivity;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mActivity = getActivity();
setHasOptionsMenu(true);
}
}

setHasOptionsMenu方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Report that this fragment would like to participate in populating
* the options menu by receiving a call to {@link #onCreateOptionsMenu}
* and related methods.
*
* @param hasMenu If true, the fragment has menu items to contribute.
*/
public void setHasOptionsMenu(boolean hasMenu) {
if (mHasMenu != hasMenu) {
mHasMenu = hasMenu;
if (isAdded() && !isHidden()) {
mHost.onSupportInvalidateOptionsMenu();
}
}
}

单独使用而不与ActionBar进行关联

多谢@XZoomEye提示,对于Fragment使用Toolbar思维被局限了,在Android Toolbar一文中介绍了单独使用而不与ActionBar进行关联,直接使用ToolBar的inflateMenu方法,Menu的事件也是独立的,需要通过设置ToolBar的setOnMenuItemClickListener来实现,这样做就不会发生标题错乱。



联系作者

我的微信公众号:吴小龙同学,欢迎关注交流,公号回复关键字「1024」有惊喜哦。