Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar處理

来源:http://www.cnblogs.com/mengdd/archive/2016/06/16/5590634.html
-Advertisement-
Play Games

本文介紹了在Android中將Toolbar作為ActionBar使用的方法. 並且介紹了在Fragment和嵌套Fragment中使用Toolbar作為ActionBar使用時需要註意的事項. ...


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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 彈出層在網頁開發中經常用到,以前都是百度一下複製粘貼,從來沒有仔細看過也沒有理解原理. 直到有一天沒有網路,,,然後突然就想到了.這麼簡單為什麼會複製粘貼這麼久.總結如下: 1、有個大的div背景設個透明度opacity:0.5;filter:alpha(opacity=57),來個absolute ...
  • 常用選擇符的介紹 通配選擇符指選定文檔對象模型(DOM)中的所有類型的單個對象。 *{css代碼規則},其中*表示所有類型,包含body,div,td等 類型選擇符指以文檔對象模型(DOM)作為選擇符,即選擇某個html標記為對象,設置樣式規則。 P{ } Div{ } 包含選擇符 對某個容器層的子 ...
  • 寫在前面 本文翻譯自 Android Studio Tips by Philippe Breault,一共收集了62個 Android Studio 使用小技巧和快捷鍵。 根據這些小技巧的使用場景,本文將這62個小技巧分為常用技巧(1 – 28)、編碼技巧(29 – 49)和調試技巧(50 – 62 ...
  • url支持26個英文字母、數字和少數幾個特殊字元,因此,對於url中包含非標準url的字元時,就需要對其進行編碼。iOS中提供了函數stringByAddingPercentEscapesUsingEncoding對中文和一些特殊字元(下麵已證實包含"%")進行編碼,但是stringByAdding ...
  • 有數值按此方式構成: 5=2^0+2^2; 40=2^3+2^5; 現在需要獲取到冪值0和2或者3和5。int main(int argc, char * argv[]) int main(int argc, char * argv[]) ...
  • 介紹: 在WWDC 2015會議上,蘋果官方公佈了iOS9。除開許多新的特性和增強功能,這次升級也給了開發者們一個機會讓他們的app里的內容能通過Spotlight 搜索功能被髮現和使用。在iOS9中可用的新APIs允許你去索引APP裡面的內容或者界面狀態,通過Spotlight來讓用戶使用。 這些 ...
  • 隨著移動安全越來越火,各種調試工具也都層出不窮,但因為環境和需求的不同,並沒有工具是萬能的。因此,筆者將會在這一系列文章中分享一些自己經常用或原創的調試工具以及手段,希望能對國內移動安全的研究起到一些催化劑的作用。 ...
  • 邊界的時候會看到一個不能翻頁的動畫,可能影響用戶體驗。此外,某些區域性的ViewPager(例如展示廣告或者公告之類的ViewPager),可能需要自動輪播的效果,即用戶在不用滑動的情況下就能夠看到其他頁面的信息。 為此我查閱了網路上現有的一些關於實現這樣效果的例子,但都不是很滿意,經過反覆實驗,在 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...