Fragment的進一步使用(三)--- 關於DialogFragment對話框,設備旋轉與fragment,fragment間的通訊 , fragment的Menu

一、DialogFragment對話框的使用

提起android的對話框,更多想到的是AlertDialog,但是android開發原則更多推薦使用DialogFragment,通過FragmentManager來管理,以致可以使用更多的配置選項來顯示對話框。

例如:獨立配置使用AlertDialog會在設備旋轉後消失,這當然不是我們所希望的,但是DialogFragment封裝的AlertDialog則不會出現這個問題。

實現過程:

         1、創建DialogFragment

         2、創建AlertDialog

         3、通過FragmentManager在屏幕上顯示對話框


1)創建DatePickerFragment 繼承DialogFragment,對話框裏面包含一個時間選擇器DatePicker

package com.example.learn_fragment.fragment;

import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.DatePicker;

import com.example.learn_fragment.R;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Created by Administrator on 2016/8/1.
 */

public class DatePickerFragment extends android.support.v4.app.DialogFragment{

    public static final String EXTRA_DATE = "criminalintent.date";
    private Date mDate;

    // fragment與activity通訊的較好方式之一
    public static DatePickerFragment newInstance(Date date){
        Bundle args = new Bundle();
        args.putSerializable(EXTRA_DATE, date);
        DatePickerFragment fragment = new DatePickerFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        // Arguments的方法可以應對設備旋轉,新實例可以從Arguments中獲取已在onDateChanged中保存進Arguments的mDate實例
        // 相比onSaveInstanceState保存DatePickerFragment數據更方便簡單
        // 從另一方面來說,直接保存該DialogFragment更方面,但是DialogFragment在保存實例還存在bug,所以還是通過Arguments來保存較好
        mDate = (Date) getArguments().getSerializable(EXTRA_DATE);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(mDate);
        int year = calendar.get(Calendar.YEAR);
        final int month = calendar.get(Calendar.MONTH);
        final int day = calendar.get(Calendar.DAY_OF_MONTH);

//--------對話框內容
       View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_date, null);

        DatePicker datePicker = (DatePicker) v.findViewById(R.id.dialog_date_datePicker);
        datePicker.init(year, month, day, new DatePicker.OnDateChangedListener() {
            @Override
            public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                mDate = new GregorianCalendar(year, monthOfYear, dayOfMonth).getTime();
                getArguments().putSerializable(EXTRA_DATE, mDate);
            }
        });

        return new AlertDialog.Builder(getActivity())
                .setView(v)
                .setTitle(R.string.date_picker_title)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        sendResult(Activity.RESULT_OK);
                    }
                })
                .create();
<pre name="code" class="java">//--------對話框內容

 } /** * 將日期回傳給目標的Fragment(CrimeFragment) * @param resultCode */ private void sendResult(int resultCode){ if(getTargetFragment() == null){ return; } Intent i = new Intent(); i.putExtra(EXTRA_DATE, mDate); getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, i); }}

dialog_date.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <DatePicker
        android:id="@+id/dialog_date_datePicker"
        android:calendarViewShown="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        ></DatePicker>

</LinearLayout>

2)創建AlertDialog,已經在return那裏創建了。

3)通過FragmentManager在屏幕上顯示對話框

和其他fragment一樣,DialogFragment實例也是由託管activity的FragmentManager管理着的。要將DialogFragment添加給FragmentManager管理並放到屏幕上,可調用fragment實例一下方法:

public void show(FragmentManager manager, String tag)

public void show(FragmentTransaction transaction, String tag)

string 參數可以唯一識別FragmentManager隊列中的DialogFragment。可以按照需要選擇究竟是使用FragmentManager還是FragmentTransaction。如果傳入FragmentManager參數,則事物可以自動創建並提交。這裏選用傳入FragmentManager參數。

通過一個按鈕觸發對話框:

mDateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager fm = getSupportFragmentManager();
                DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
                // 把CrimeFragment設置爲DatePickerFragment的目標Fragment,這樣就可以把數據從DatePickerFragment傳回目標Fragment
                dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
                // 展示dialog(DialogFragment),也將dialog交由fm管理,fragment會自動提交
                dialog.show(fm, DIALOG_DATE);
            }
        });

效果圖:

設備需旋轉不會消失的dialog




二、設備旋轉與fragment

不重複造輪子,引用自:http://www.gongmingqm10.net/blog/2015/12/16/you-should-know-about-android-rotate/

在 Android 開發或面試過程中,屏幕旋轉是一個容易讓人忽視的知識點。在我之前經歷的項目中,App 通常是爲豎屏狀態設置的,所以通常我們會對每個頁面都設置豎屏方向,這時候我們不需要考慮旋轉屏問題。但是最近項目中,我們的 App 是爲平板設計的,而橫豎屏旋轉是屬於客戶的一個需求,當然平板上橫豎屏的確比較常用。所以就藉此機會研究了下 Android 橫豎屏問題。

Android 橫豎屏

橫豎屏之所以需要引起開發者的注意,是因爲 App 在橫豎屏切換的過程中,頁面會重繪,那麼頁面上已有的數據(比如登錄頁面已經輸入的用戶名)如何保存成爲了一個問題。按照官方的推薦,Activity本身的確有處理旋轉屏事件的函數,但是當一個頁面中需要保存的數據很多的時候(比如很多 EditText),還是手工處理,就顯得有些繁瑣了。下面我們將循序漸進地探索 Android 屏幕旋轉之最佳實踐。

Activity 旋轉中的保存與恢復

在解決問題或者探索最佳實踐的時候,我們可以從簡單的問題入手,慢慢衍生至複雜的情形,最後再抽象出一些比較通用的解決方案。這一步我們單純的探索一個 Activity(沒有 Fragment)的數據保存與恢復。

Activity 的生命週期

Activity 從創建到呈現在用戶面前到消亡,有着自己完整的生命週期:

Activity 生命週期

旋轉屏幕時,打印相關 Log 如下:

Activity 旋轉中的生命週期

從 Log 可以看到,在屏幕旋轉時,原來的 Activity 調用了 onDestroy,隨後重新實例化了一個 MainActivity。重新實例化的 MainActivity 也會經歷 “onCreate –> onStart –> onResume” 的生命週期。

Activity 中窗口保存與恢復

爲了進一步探索 Activity 在旋轉屏過程中的數據保存及恢復的邏輯,我們構造了一個具有用戶名和密碼的登錄界面。當用戶在用戶名中輸入用戶名時,這時候旋轉屏幕,此時期望的操作應該是輸入的用戶名仍然能夠保留:

Activity Login 頁面

在不添加任何額外代碼的情況下,我們可以看到輸入框中的數據在旋轉屏後仍然能夠保留,這些控件基本狀態的值是如何被保留的呢?

Activity 中方法 protected void onSaveInstanceState(Bundle outState) {...} 主要做狀態保存相關的處理,如果我們有需要特地保存的變量等,我們可以在onSaveInstanceState 中保存,保存後的 bundle 以 outState Bundle 的格式保存。當 Activity 再次被初始化時,onCreate(Bundle savedInstanceState) 會將保存的 bundle 傳遞給 Activity 主頁面,Activity 主頁面接收到這些狀態保存的數據後,能夠根據保存中的控件的ID信息,狀態數據等對頁面進行自動的初始化。當我們轉屏時,會主動觸發onSaveInstanceState 被調用。Log 打印如下:

Activity onSaveInstanceState & onRestoreInstanceState

根據 Log 很明確看出在旋轉屏之後,隨着 onPause 的執行,onSaveInstanceState 也被執行。當 Activity 再次初始化時,onCreate(Bundle savedInstanceState) 會傳遞迴一個非空的 savedInstanceState(而當 Activity 第一次初始化時此值爲空),同時onRestoreInstanceState 也會被調用,用來將保存的窗口狀態信息重新應用:

生命週期:onCreate –> onStart –> onResume –> Running 轉屏 –>onPause –> onSaveInstanceState –> onStop –>onDestroy –> onCreate –> onStart –> onRestoreInstanceState –> onResume;

Android savedInstanceState

通過對 savedInstanceState 的查看,發現 savedInstanceState 中包含有 Key 爲 android:viewHierarchyState 的 bundle 數據,此 bundle 數據具體內容如下:

1
2
3
4
5
Bundle[{android:views={
      16908290=android.view.AbsSavedState$1@347440f8,         2131492927=android.view.AbsSavedState$1@347440f8,       2131492928=android.view.AbsSavedState$1@347440f8,       2131492929=android.support.v7.widget.Toolbar$SavedState@391b69d1,       2131492930=android.view.AbsSavedState$1@347440f8,       2131492944=TextView.SavedState{138fec36 start=8 end=8 text=gongming},       2131492945=TextView.SavedState{16043737 start=0 end=0 text=},       2131492946=android.view.AbsSavedState$1@347440f8
  },
  android:focusedViewId=2131492944}
]

可以看到 username 的輸入框 EditText,其 ID 以及當前的值都已經保存至 savedInstanceState 中。

那麼,對於 Activity.java 的而言,最重要的保存數據和恢復數據的源代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";

protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if (mWindow != null) {
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if (windowState != null) {
            mWindow.restoreHierarchyState(windowState);
        }
    }
}

這就是爲什麼我們沒有做過多的處理卻可以讓 App 在旋轉屏幕時仍然自動保存恢復輸入框中的文字。

Activity 中數據保存與恢復

既然 Activity 本身對窗口(控件)的狀態信息進行了保存及恢復處理。那麼我們在屏幕切換時最應該關心的就是頁面數據的保存與恢復。頁面數據主要有兩種:

  • API 請求的數據:在橫豎屏切換時對API數據進行保存及恢復能夠防止 API 的重複調用;
  • 頁面中的狀態值:在 Activity 運行過程中被改變的狀態值,在恢復時需要手動保存及恢復,以便不影響狀態值相關的頁面邏輯;

在 Activity 中進行變量的存儲及讀取時,使用 onSaveInstanceState 進行數據的存儲,使用 onRestoreInstanceState 進行數據的讀取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private boolean shouldSaveData = false;

private static final String KEY_SHOULD_SAVE_DATA = "save_data_key";

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putBoolean(KEY_SHOULD_SAVE_DATA, shouldSaveData);
    super.onSaveInstanceState(outState);

    Log.i(TAG, "@@Activity onSaveInstanceState@@" + this.toString());
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    shouldSaveData = savedInstanceState.getBoolean(KEY_SHOULD_SAVE_DATA);
    Log.i(TAG, "@@Activity onRestoreInstanceState@@" + this.toString());
}

Activity 中彈框的狀態保存及恢復

在 Activity 中經常會遇到 Dialog,甚至像下面具有輸入框的 Dialog:

Android Dialog

當用戶點擊 Login 按鈕,彈出彈框需要用戶簽名,這時如果屏幕不小心進行了一次旋轉,那麼這個彈出的 Dialog 便消失,隨之消失的還有用戶輸入了一半的輸入框文字。Activity 在旋轉過程中對於 Dialog 自身的生命週期進行很好的管理,如果爲了達到更好的用戶體驗,轉屏時也需要保存輸入框狀態,那麼此處我們強烈推薦用戶使用DialogFragment 代替 Dialog。

由於 Fragment 也具有生命週期,使用 DialogFragment 之後,我們結合 Activity 與 Fragment 的生命週期,查看整個過程經歷了哪些流程。

Frgament 生命週期

結合 DialogFragment 在 Activity 中旋轉的重新初始化及數據恢復,我們可以看到執行順序如下:

Android Fragment 執行順序

在轉屏時,Activity 和 Fragment 都會重新實例化,並且都通過 onSaveInstanceState 進行狀態保存。值得注意的是不同於 Activity, Fragment 並沒有onRestoreInstanceState 方法,Fragment 的狀態恢復在 onActivityCreated 方法中。查看 DialogFragment 源碼,我們可以看到如下調用:

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
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
  ...
    if (savedInstanceState != null) {
        Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
        if (dialogState != null) {
            mDialog.onRestoreInstanceState(dialogState);
        }
    }
}

...

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (mDialog != null) {
        Bundle dialogState = mDialog.onSaveInstanceState();
        if (dialogState != null) {
            outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
        }
    }
  ...
}

由於 DialogFragment 其實就是展示一個 Dialog,而 DialogFragment 對 Dialog 的狀態保存及恢復使得 Dialog 的狀態得以保存。

Fragment 狀態的保存及恢復

有了上面對 DialogFragment 轉屏時狀態保存及恢復的研究,那麼在一個普通的 Fragment(DialogFragment 是一種特殊的 Fragment) 中狀態保存及恢復又是怎樣的呢?

實際上通過 DialogFragment 我們可以知道保存狀態值還是通過 onSaveInstanceState 方法,而 onActivityCreated 中則可以獲取狀態值。

在轉屏時,我們會有很多特殊的考慮。所以如果你的 App 需要支持橫豎屏切換,你可以留意如下幾點:

1. Dialog 轉屏消失問題

Dialog 轉屏消失在現實中是一個很常見的情形,對應的解決方案就是利用 DialogFragment 來替代 Dialog。這樣旋轉屏幕時彈起的 Dialog 就不會消失。

2. Fragment 保存組件信息的坑

最近在項目中發現,有時候放置在 Fragment 中的 ListView 轉屏後不能自動回到轉屏之前的位置。後來發現導致原因是 Activity 的 Layout 在添加 Fragment 時候沒有指定 id 或 tag。於是該 Fragment 在 Activity 重繪時不能被系統當作 “同一個” Fragment,所以旋轉時控件的一些基本狀態信息沒辦法恢復。

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/home_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:name="net.gongmingqm10.androidrotate.HomeFragment" />

其中的 android:id="@+id/home_fragment" 是重點。一旦 Fragment 的狀態保存出現問題,可先確認 Fragment 是不是設置了 id 或 tag。

3. 使用 setRetainInstance

關於 Fragment,我們發現 setRetainInstance 方法經常被用到,那麼這個方法的作用是什麼呢?我們看看官方的解釋:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Control whether a fragment instance is retained across Activity
 * re-creation (such as from a configuration change).  This can only
 * be used with fragments not in the back stack.  If set, the fragment
 * lifecycle will be slightly different when an activity is recreated:
 * <ul>
 * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
 * will be, because the fragment is being detached from its current activity).
 * <li> {@link #onCreate(Bundle)} will not be called since the fragment
 * is not being re-created.
 * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
 * still be called.
 * </ul>
 */

結合方法名以及方法的解釋,可以知道一旦我們設置 setRetainInstance(true),意味着在 Activity 重繪時,我們的 Fragment 不會被重複繪製,也就是它會被“保留”。爲了驗證其作用,我們發現在設置爲true 狀態時,旋轉屏幕,Fragment 依然是之前的 Fragment。而如果將它設置爲默認的 false,那麼旋轉屏幕時 Fragment 會被銷燬,然後重新創建出另外一個 fragment 實例。並且如官方所說,如果 Fragment 不重複創建,意味着 Fragment 的onCreateonDestroy 方法不會被重複調用。所以在旋轉屏 Fragment 中,我們經常會設置setRetainInstance(true),這樣旋轉時 Fragment 不需要重新創建。

如果你的 App 恰好可以不做轉屏,那麼你可以很省事的在 Manifest 文件中添加標註,強制所有頁面使用豎屏/橫屏。如果你的頁面不幸的需要支持橫豎屏切換,那麼你在預估工作量或者給客戶報價時一定要考慮到。雖然加入轉屏支持不會導致工作量翻倍,但是卻有可能引起許多問題。尤其當頁面有很多業務邏輯,有狀態值的時候。所以我們在項目開發過程中,應該知道什麼時候需要考慮狀態保存,當狀態保存出現問題時,應該怎麼解決之。


三、fragment間的通訊(由同一個activity託管下的兩個fragment之間的通訊)


要傳遞CrimeFragment的記錄日期到DatePickerFragment,將這個日期作爲DatePickerFragment的初始化日期。有一種簡單較好的方法是在DatePickerFragment實現一個newInstance()方法 , (DatePickerFragment.java)

    public static DatePickerFragment newInstance(Date date){
        Bundle args = new Bundle();
        args.putSerializable(EXTRA_DATE, date);
        DatePickerFragment fragment = new DatePickerFragment();
        fragment.setArguments(args);
        return fragment;
    }

然後將Date作爲argument,在DatePickerFragment創建時附加給DatePickerFragment


回傳日期:從DatePickerFragment回傳給CrimeFragment

1、設置目標fragment

類似於activity間的關聯,可將CrimeFragment設置爲DatePickerFragment的目標fragment。調用一下方法:

public void setTargetFragment(Fragmetn fragment, int requestCode)

該方法接受目標fragment以及一個識別請求碼。目標fragment可使用該識別請求碼通知是哪一個fragment在返回數據信息。

目標fragment以及識別請求碼有FragmentManager負責跟蹤記錄,我們可以調用fragment(設置目標fragment的fragment)的getTargetFragment() 和 getTargetRequestCode() 方法獲取它們。

CrimeFragment.java

這是由一個按鈕啓動的DatePickerFragment

      mDateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
                // 把CrimeFragment設置爲DatePickerFragment的目標Fragment,這樣就可以把數據從DatePickerFragment傳回目標Fragment
                dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
                // 展示dialog(DialogFragment),也將dialog交由fm管理,fragment會自動提交
                dialog.show(fm, DIALOG_DATE);
            }
        });

2、傳遞數據給目標fragment

建立CrimeFragment與DatePickerFragment間的聯繫後,需將日期數據返回給CrimeFragment。返回的日期數據將作爲extra附加給Intent。

使用的是DatePickerFragment的類方法將intent傳入CrimeFragment.onActivityResult(int , int , Intent)方法。

(不要忘記這兩個fragment是由同一個activity託管的)

Activity.onActivityResult(...)方法是ActivityManager在子activity銷燬後調用的父activity方法。處理activity間的數據返回時,無需親自動手,ActivityManager會自動調用Activity.onActivityResult(...)方法。父activity接收到Activity.onActivityResult(...)方法的調用後,其FragmentManager會調用對應fragment的Fragment.onActivityResult(...)方法。

所以,在處理由同一個activity託管的兩個fragment間的數據返回時,可借用Fragment.onActivityResult(...)方法。因此,直接調用目標fragment的Fragment.onActivityResult(...)方法,即可實現數據的回傳。該方法有我們需要的信息:

 1)、一個與setTargetFragment(...)方法匹配的識別請求碼,用以告知目標fragment返回結果來自哪裏。

 2)、一個決定下一步該採取什麼行動的結果代碼

 3)、一個含有extra數據信息的Intent

在DatePickerFragment新建一個方法:

    /**
     * 將日期回傳給目標的Fragment(CrimeFragment)
     * @param resultCode
     */
    private void sendResult(int resultCode){
        if(getTargetFragment() == null){
            return;
        }
        Intent i = new Intent();
        i.putExtra(EXTRA_DATE, mDate);
        getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, i);
    }

調用該方法的時機當然是在選好日期後按確定按鈕的時候:

        return new AlertDialog.Builder(getActivity())
                .setView(v)
                .setTitle(R.string.date_picker_title)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        sendResult(Activity.RESULT_OK);
                    }
                })
                .create();

響應DatePickerFragment對話框(CrimeFragment.java):

    /**
     * 處理DataPickerFragment回傳的數據
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(resultCode != Activity.RESULT_OK)
            return;
        if(requestCode == REQUEST_DATE){
            Date date = (Date) data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = formatter.format(date);
            mCrime.setDate(date);
            mDateButton.setText(dateString);
        }
    }


四、Fragment的Menu(右上角的菜單選項)

Activity類提供了管理選項菜單的回調函數onCreateOptionsMenu(Menu)方法

Fragment也有自己的一套選項菜單回調函數:

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)

public boolean onOptionsItemSelected(MenuItem item)
使用方法跟activity的相差無幾,也是配合menu.xml來使用。不過,Fragment的onCreateOptionsMenu(Menu, MenuInflater)方法是由FragmentManager負責調用的。因此,當activity接收到來自操作系統的onCreateOptionsMenu(...)方法回調請求時,我們必須明確告訴FragmentManager:其管理的fragment應接收onCreateOptionsMenu(...)方法的調用指令。要通知FragmentManager,需要調用一下方法:

public void setHasOptionsMenu(boolean hasMenu)

並且是在fragment的onCreate(...)方法中調用,setHasOptionsMenu(true).


以上如有錯誤,同行有責任指正哦~,感激不盡




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章