Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理-程序员宅基地

技术标签: java  移动开发  

Toolbar作为ActionBar使用介绍

本文介绍了在Android中将Toolbar作为ActionBar使用的方法.
并且介绍了在Fragment和嵌套Fragment中使用Toolbar作为ActionBar使用时需要注意的事项.

使用support library的Toolbar

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.

在Activity中使用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的方法.

添加Action Buttons

定义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);
    }
}

添加向上返回的action

添加向上返回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

在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中使用Toolbar

前面已经介绍过, Fragment可以嵌套使用: Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误.
那么在前面的Fragment中再显示一个子Fragment, 并且又带有一个不一样的Toolbar, 还需要哪些处理呢?
首先, java代码中还是需要有:

setHasOptionsMenu(true)
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);

然后根据是否需要菜单按钮, 覆写onCreateOptionsMenu()方法来inflate自己的menu文件即可.
感觉和在普通的Fragment中使用Toolbar作为ActionBar并没有什么区别.
但是如果你的多个Fragment有不同的Toolbar菜单选项, 如果你没有懂得其中的原理, 可能就会出现一些混乱.
下面来解说一下相关的方法.

onCreateOptionsMenu()方法的调用

一旦调用

((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()方法.

onOptionsItemSelected()方法的调用

在Activity和其中的Fragment都有options menu的时候, 需要注意menu item的id不要重复.
以为点击事件的分发也是从Activity开始分发下去的, 如果child fragment中有个选项的id和Activity中一个选项的id重复了, 则在Activity中就会将其处理, 不会继续分发.

有嵌套Fragment时 Back键处理

之前没有嵌套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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33725239/article/details/86222780

智能推荐

条件注解@Conditional_java 不存在才创建 @conditional-程序员宅基地

文章浏览阅读110次。如果没有havingValue,那么会采用prefix+name 或 prefix+value 的值,去和false比较,如果值等于false 那么失败, 如果不等于false 成功。定义:在spring框架中用于根据特定条件决定是否创建或者注册某个bean或配置的注解,他们可以根据运行时环境,配置属性,或其他条件来动态的控制bean的创建或者注册。在上述代码中,表示会去application文件中,读取前缀为:Spring.datasource下,名字为type的key的值,读取到之后,会跟。_java 不存在才创建 @conditional

PDF 参照流/交叉引用流对象(cross-reference stream)的解析方法_php无法解析压缩的交叉引用和对象流的pdf-程序员宅基地

文章浏览阅读3.8k次。采用交叉引用流对象有以下几点好处:1) 压缩后存储的信息更紧凑;2) 可以访问存储在对象流中的压缩对象,并允许以后加入新的参照选项类型。_php无法解析压缩的交叉引用和对象流的pdf

面试只会问八股?你应该好好思考怎样做一场面试_如果面试一直问八股-程序员宅基地

文章浏览阅读517次。八股文并不能很好地测试候选人水平,我们应该思考更有效的面试方法。_如果面试一直问八股

英特尔Realsense学习笔记番外篇一:QWindowsContext: OleInitialize() failed_qwindowscontext: oleinitialize() failed: "com erro-程序员宅基地

文章浏览阅读2k次。导入pyrealsense2以后,使用PyQt5,会有以下警告:QWindowsContext: OleInitialize() failed: "COM error 0xffffffff80010106 RPC_E_CHANGED_MODE (Unknown error 0x080010106)"此警告在使用过程中没有影响程序的正常执行,如果不需要看到waring可以导入warnings库过滤warning。参考文献:1.WindowsContext:OleInitialize()失_qwindowscontext: oleinitialize() failed: "com error 0xffffffff80010106 rpc_e

linux DSA 开发(一)-程序员宅基地

文章浏览阅读6.5k次,点赞2次,收藏32次。linux DSA 开发(一)本文主要是翻译,原文链接如下:https://www.kernel.org/doc/html/latest/networking/dsa/dsa.html纲要本文档描述了**分布式交换机架构 (DSA)**子系统的设计原则、限制、与其他子系统的交互、如何为该子系统开发驱动程序设计原则分布式交换机架构最少是用于支持使用 Linux 的 Marvell 以太网交换机的子系统(MV88E6xxx),但此后也发展为支持其他供应商。这种设计_linux dsa

论文《最长沙堡保存策略》_沙堡论文-程序员宅基地

文章浏览阅读284次。最长沙堡保存策略Longest Sandcastle Preservation StrategyDOI: 10.12677/MOS.2020.93023, PDF, HTML, XML作者: 黄欣怡, 张 馨, 易毅强:湖南农业大学信息与智能科学技术学院,湖南 长沙关键词: 沙堡;液桥模型;毛细力;Young-Laplace方程;Sandcastle; Liquid Bridge Model; Capillary Force; Young-Laplace Equation摘要: 海滩是一个适合休闲_沙堡论文

随便推点

LeetCode #566 - Reshape the Matrix__%#:566-程序员宅基地

文章浏览阅读90次。题目描述:In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a new one with different size but keep its original data.You're given a matrix represented by a tw...__%#:566

UserWarning: Workbook contains no default style, apply openpyxl‘s default warn no default style 解决方案-程序员宅基地

文章浏览阅读6.4w次,点赞15次,收藏11次。本文主要介绍了UserWarning: Workbook contains no default style, apply openpyxl’s default warn(“Workbook contains no default style, apply openpyxl’s default”)解决方案,希望能对新手有所帮助。文章目录1. 问题描述2. 原理详解3. 解决方案 3.1 安装库 3.2 使用正确的API_userwarning: workbook contains no default style, apply openpyxl's default wa

c++中类成员函数的相互调用_c++类的成员函数调用其他成员函数-程序员宅基地

文章浏览阅读8.6k次,点赞4次,收藏23次。c++中类成员函数的相互调用_c++类的成员函数调用其他成员函数

Window Linux双系统安装 超详细教程_windows linux双系统-程序员宅基地

文章浏览阅读3.5w次,点赞71次,收藏615次。Window Linux双系统安装历程今天下午搞了几个小时,可谓是困难重重,但是实际上只是被一个很小的问题困住了,其它地方都很简单。接下来简单讲一下安装的步骤以及遇到的问题。先讲一下设备状况:一台12年买的i3 window7 待报废的lenovo 笔记本一个32g的usb3.0另一台新的win10笔记本电脑磁盘分区首先要在安装双系统的电脑上,划分硬盘中的一个分区来装linxu。点击我的电脑(此电脑)->管理-> 磁盘管理比如这里我们的e盘原本有98.55g,然后还有_windows linux双系统

Unity 模型涂鸦-程序员宅基地

文章浏览阅读2.3k次。首先,要获取鼠标点击处对应的UV坐标,详见API:https://docs.unity3d.com/ScriptReference/RaycastHit-textureCoord.htmlAPI中其实已经重画了图片,但只是一个像素,下面给线设置宽度: /// &lt;summary&gt; /// 画线 /// &lt;/summary&gt; /// ..._unity 模型涂鸦

qt使用q3dsurface绘制三维曲面图-程序员宅基地

文章浏览阅读7k次,点赞22次,收藏86次。在QT中使用Q3Dsurface绘制三维图。项目需要,而大部分教程都是比较简单的绘制,不能满足需求,本教程将三维数组数据绘制成三维曲面,在绘制三维曲面时,我原本以为是给入数据,然后由框架拟合出曲面,其实不是,本质上是一个一个点连接的,需要按顺序连接好节点,即一行一列连接,否则曲面就会看不出形状。_q3dsurface

推荐文章

热门文章

相关标签