本文介绍了在Android中将Toolbar作为ActionBar使用的方法.
并且介绍了在Fragment和嵌套Fragment中使用Toolbar作为ActionBar使用时需要注意的事项.
Android的ActionBar每个版本都会做一些改变, 所以原生的ActionBar在不同的系统上看起来可能会不一样.
使用support library版本的Toolbar可以让你的应用在多种设备类型上保持一致. support library中总是包含了最新的features.
Android从5.0 (API Level 21)开始提供Material Design, 使用v7版本的Toolbar后, 在任何Android 2.1(API Level 7)以上的机器上都可以看到Material Design风格的Toolbar.
1.首先项目gradle中添加:
compile ‘com.android.support:appcompat-v7:23.4.0‘
2.确保Activity继承AppCompatActivity
3.在application设置中使用NoActionBar的主题:
<application
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
/>
4.把Toolbar写在布局中
<android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
5.在Activity里面把Toolbar设置成为ActionBar
首先把Toolbar find出来, 然后调用setSupportActionBar方法
把Toolbar设置为自己的ActionBar即可.
public class ToolbarDemoActivity extends AppCompatActivity {
@BindView(R.id.toolbar)
Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar_demo);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
}
}
然后就可以随意使用啦, 用getSupportActionBar可以获取ActionBar类型的对象, 从而使用ActionBar的方法.
定义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_android"
android:icon="@drawable/ic_android_black_24dp"
android:title="@string/action_android"
app:showAsAction="always" />
<item
android:id="@+id/action_favourite"
android:icon="@drawable/ic_favorite_black_24dp"
android:title="@string/action_favourite"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_settings"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>
然后在代码中inflate和处理它的点击事件:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "onCreateOptionsMenu()");
getMenuInflater().inflate(R.menu.menu_activity_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_android:
Log.i(TAG, "action android selected");
return true;
case R.id.action_favourite:
Log.i(TAG, "action favourite selected");
return true;
case R.id.action_settings:
Log.i(TAG, "action settings selected");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
添加向上返回parent的action:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar_demo);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
// add a left arrow to back to parent activity,
// no need to handle action selected event, this is handled by super
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
然后只需要在manifest中指定parent:
<activity
android:name=".toolbar.ToolbarDemoActivity"
android:parentActivityName=".MainActivity"></activity>
在Fragment中使用Toolbar的步骤和Activity差不多.
在Fragment布局中添加一个Toolbar, 然后find它, 然后调用Activity的方法来把它设置成ActionBar:
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
注意此处有一个强转, 必须是AppCompatActivity才有这个方法.
但是此时运行到Fragment之后, 发现Toolbar上的文字和按钮全是Activity传过来的, 这是因为只有Activity的onCreateOptionsMenu()被调用了, 但是Fragment的并没有被调用.
在Fragment中加上这句:
setHasOptionsMenu(true);
此时Fragment的onCreateOptionsMenu()回调会被调到了, 但是inflate出的按钮和Activity中的actions加在一起显示出来了.
因为Activity的onCreateOptionsMenu()会在之前调用到.
于是Fragment中的写成这样:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Log.e(TAG, "onCreateOptionsMenu()");
menu.clear();
inflater.inflate(R.menu.menu_parent_fragment, menu);
}
即先clear()一下, 这样按钮就只有Fragment中设置的自己的了, 不会有Activity中的按钮.
前面已经介绍过, Fragment可以嵌套使用: Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误.
那么在前面的Fragment中再显示一个子Fragment, 并且又带有一个不一样的Toolbar, 还需要哪些处理呢?
首先, java代码中还是需要有:
setHasOptionsMenu(true)
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
然后根据是否需要菜单按钮, 覆写onCreateOptionsMenu()方法来inflate自己的menu文件即可.
感觉和在普通的Fragment中使用Toolbar作为ActionBar并没有什么区别.
但是如果你的多个Fragment有不同的Toolbar菜单选项, 如果你没有懂得其中的原理, 可能就会出现一些混乱.
下面来解说一下相关的方法.
一旦调用
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
就会导致ActivityonCreateOptionsMenu()方法的调用, 而Activity会根据其中Fragment是否设置了setHasOptionsMenu(true)来调用Fragment的
onCreateOptionsMenu()方法, 调用顺序是树形的, 按层级调用, 中间如果有false则跳过.
假设当前Activity, Parent Fragment和Child Fragment中都设置了自己的Toolbar为ActionBar.
在打开Child fragment的时候, onCreateOptionsMenu()的调用顺序是.
Activity -> Parent -> Child. 此时parent和child fragment都设置了setHasOptionsMenu(true).
关于这个, 还有以下几种情况:
- 如果Parent的`setHasOptionsMenu(false)`, Child为true, 则Parent的`onCreateOptionsMenu()`不会调用, 打开Child的时候Activity -> Child.
- 如果Child的`setHasOptionsMenu(false)`, Parent为true, 则打开Child的时候仍然会调用Activity和Parent的onCreateOptionsMenu()方法.
- 如果Parent和Child都置为false, 打开Parent和Child Fragment的时候都会调用Activity的onCreateOptionsMenu()方法.
仅仅是child Fragment的show() hide()的切换, activity和parent Fragment的onCreateOptionsMenu()也会重新进入.
这一点我还没有想明白, 是项目中遇到的, 初步推测可能是menu的显隐变化invalidate了menu, 改天有空再试试.
上面的机制常常是导致Toolbar上面的按钮混淆错乱的原因.
举个例子:
如果我们现在Activity和Parent Fragment有不同的Toolbar按钮, 但是Child只有文字, 没有按钮.
很显然我们不需要给child写menu文件, 也不需要覆写child里的onCreateOptionsMenu()方法.
但是此时不管怎样, parent的onCreateOptionsMenu()方法都会被调用, 这样我们打开child的时候, toolbar上就神奇地出现了parent里的按钮.
这种情况如何解决呢?
可以在parent中加一个条件, 当没有child fragment的时候才做inflate的工作:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Log.e(TAG, "onCreateOptionsMenu()");
menu.clear();
if (getChildFragmentManager().getBackStackEntryCount() == 0) {
inflater.inflate(R.menu.menu_parent_fragment, menu);
}
}
另外, 除了setSupportActionBar()之外, 如果我们想主动触发 onCreateOptionsMenu()方法的调用, 可以用
invalidateOptionsMenu()方法.
在Activity和其中的Fragment都有options menu的时候, 需要注意menu item的id不要重复.
以为点击事件的分发也是从Activity开始分发下去的, 如果child fragment中有个选项的id和Activity中一个选项的id重复了, 则在Activity中就会将其处理, 不会继续分发.
之前没有嵌套Fragment的情况下, 只要将Fragment加入到Back Stack中, 那么按下Back键的时候pop动作是系统自动做好的.
虽然在添加child fragment的时候将其加入到back stack中, 但是按back键的时候仍然是将parent fragment弹出, 只剩下Activity.
这是因为back键只检查第一层Fragment的back stack, 对于child fragment, 需要在其parent中自己处理.
比如这样处理:
在Activity中
@Override
public void onBackPressed() {
Fragment fragment = getSupportFragmentManager().findFragmentById(android.R.id.content);
if (fragment instanceof ToolbarFragment) {
if (((ToolbarFragment) fragment).onBackPressed()) {
return;
}
}
super.onBackPressed();
}
其中ToolbarFragment是直接加在Activity中作为parent fragment的.
在parent fragment中(即ToolbarFragment中):
public boolean onBackPressed() {
return getChildFragmentManager().popBackStackImmediate();
}
本文Demo地址: Demo on github
其中的: ToolbarDemoActivity即为Toolbar Demo.
本文地址: Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理
Developer Android:
Training AppBar
v7.widget.Toolbar Reference
v7.app.ActionBar
Guides: action bar menu items and fragments
Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理
原文:http://www.cnblogs.com/Free-Thinker/p/6118267.html