本章主要介紹了如何使用內(nèi)嵌在 fragment 中的對(duì)話框,以及 fragment 之間如何傳遞數(shù)據(jù)
GitHub 地址:
完成第12章
1. 使用 DialogFragment
1.1 AppCompat 兼容庫
Google 推出 AppCompat 兼容庫是為了讓所有Android用戶都能體驗(yàn)到新特性。AppCompat兼容庫能通過支持庫的方式將部分最新系統(tǒng)的特色功能移植到Android舊版本系統(tǒng)中。
應(yīng)該在 Porject Structure 中添加 appcompat-v7 的依賴。
1.2 創(chuàng)建 DialogFragment
建議將
AlertDialog封裝在DialogFragment(Fragment的子類)實(shí)例中使用。當(dāng)然,不使用DialogFragment也可顯示AlertDialog視圖,但不推薦這樣做。使用FragmentManager管理對(duì)話框,可以更靈活地顯示對(duì)話框。
如果旋轉(zhuǎn)設(shè)備,單獨(dú)使用的 AlertDialog 會(huì)消失,而封裝在 fragment 中的 AlertDialog 則不會(huì)有此問題(旋轉(zhuǎn)后,對(duì)話框會(huì)被重建恢復(fù))。
- 首先需要一個(gè)針對(duì) dialog 的布局文件,如 DatePicker 作為根元素的 Dialog 用于選擇日期。
<?xml version="1.0" encoding="utf-8"?>
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_date_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:calendarViewShown="false">
<!--舊版系統(tǒng)會(huì)使用 calenderViewShown 屬性-->
</DatePicker>
- 然后新建一個(gè)父類是
DialogFragment的類DatePickerFragment,重寫其中的onCreateDialog方法,返回一個(gè)AlertDialog
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// 使用 LayoutInflater 引用布局文件創(chuàng)建用于顯示 Dialog 的 View
View v = LayoutInflater.from(getActivity())
.inflate(R.layout.dialog_date, null);
return new AlertDialog.Builder(getActivity())
.setView(v) // 設(shè)置視圖
.setTitle(R.string.date_picker_title) //設(shè)置標(biāo)題
// 設(shè)置 OK 按鈕,OnClickListener 暫時(shí)留空
.setPositiveButton(android.R.string.ok, null)
// 使用 Builder 的 create() 方法創(chuàng)建 Dialog 并返回
.create();
}
在使用 DialogFragment 時(shí),使用成員方法 show 來顯示 dialog:
// 在 Fragment 中為 DatePickerFragment 添加一個(gè) Tag
private static final String DIALOG_DATE = "DialogDate";
……
// 在 Fragment 內(nèi)部獲取 FragmentManager
FragmentManager manager = getFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
// 顯示對(duì)話框
dialog.show(manager, DIALOG_DATE);
2. fragment 之間的數(shù)據(jù)傳遞
我們之前實(shí)現(xiàn)了 activity 之間以及基于 fragment 的 activity 之間的數(shù)據(jù)傳遞?,F(xiàn)在需實(shí)現(xiàn)同一 activity 托管的兩個(gè) fragment 之間的數(shù)據(jù)傳遞。
2.1 將數(shù)據(jù)傳遞到對(duì)話框
顯然,要達(dá)到目的,只需要在 DatePickerFragment 中建立獲取實(shí)例的 newInstance 方法,其中需要的參數(shù)是傳遞的信息即可,示例如下
// DatePickerFragment.java
public static DatePickerFragment newInstance(Date date) {
// 新建一個(gè) Bundle 對(duì)象用于存放數(shù)據(jù)
Bundle args = new Bundle();
args.putSerializable(ARG_DATE, date);
DatePickerFragment fragment = new DatePickerFragment();
// 使用 fragment arguments 來傳遞參數(shù)
fragment.setArguments(args);
return fragment;
}
記得把使用 DatePickerFragment 的構(gòu)造方法的地方改成 newInstance 方法獲取實(shí)例。
在獲得數(shù)據(jù)之后,要先將 DatePicker 初始化為原本的日期,首先用 Fragment 的 getArguments().getSeriallizable(String key) 方法獲取數(shù)據(jù),然后用 Calendar 對(duì)象取出 date 中的年月日,最后使用 DatePicker 類的 init(int year, int month, int dayOfMonth, OnDateChangedListener listener) 方法 初始化默認(rèn)日期
2.2 從對(duì)話框回傳數(shù)據(jù)
2.2.1 設(shè)置目標(biāo) fragment
類似于 activity 間的關(guān)聯(lián),可將 CrimeFragment 設(shè)置成 DatePickerFragment 的目標(biāo) fragment。 即使是在 CrimeFragment 和 DatePickerFragment 被銷毀和重建后,操作系統(tǒng)也會(huì)重新關(guān)聯(lián)它們。調(diào)用以下 Fragment 方法可建立這種關(guān)聯(lián):
public void setTargetFragment(Fragment fragment, int requestCode)
該方法有兩個(gè)參數(shù):目標(biāo) fragment 以及請(qǐng)求代碼。需要時(shí),目標(biāo) fragment 使用請(qǐng)求代碼確認(rèn)是哪個(gè) fragment 在回傳數(shù)據(jù)。
目標(biāo) fragment 和請(qǐng)求代碼由 FragmentManager 負(fù)責(zé)跟蹤管理,我們可調(diào)用設(shè)置目標(biāo)的 fragment 的 getTargetFragment() 和 getTargetRequestCode() 方法獲取它們。
2.2.2 傳遞數(shù)據(jù)給目標(biāo) Fragment
處理由同一 activity 托管的兩個(gè) fragment 間的數(shù)據(jù)返回時(shí),可借用Fragment.onActivityResult(...)方法。因此,直接調(diào)用目標(biāo) fragment 的Fragment.onActivityResult(...)方法,,就能實(shí)現(xiàn)數(shù)據(jù)的回傳。該方法恰好有我們需要的如下信息:
- 請(qǐng)求代碼:與傳入
setTargetFragment(...)方法相匹配,告訴目標(biāo) fragment 返回結(jié)果來 自哪里。 - 結(jié)果代碼:決定下一步該采取什么行動(dòng)。
- Intent:包含 extra 數(shù)據(jù)。
所以從 CrimeFragment 中顯示 DatePickerFragment,用戶選擇日期以后,想要回傳信息,可以寫一個(gè) sendResult 方法,該方法如下:
// DatePickerFragment.java
public static final String EXTRA_DATE =
"com.kniost.criminalintent.date";
……
private void sendResult(int resultCode, Date date) {
// 防止出錯(cuò)
if (getTargetFragment() == null) {
return;
}
Intent intent = new Intent();
// 放置數(shù)據(jù)到 Intent 中
intent.putExtra(EXTRA_DATE, date);
// 獲取目標(biāo) fragment,調(diào)用其 onActivityResult 方法,其中 RequestCode 是用 getTargetRequestCode 方法獲取的,resultCode 是傳入?yún)?shù),intent 包含了數(shù)據(jù)
getTargetFragment()
.onActivityResult(getTargetRequestCode(), resultCode, intent);
}
在 CrimeFragment 中則應(yīng)該重寫 onActivityResult 方法:
// CrimeFragment.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// 如果一切正常,調(diào)用 sendResult 的方法時(shí)傳入的參數(shù)應(yīng)該就是 Activity.RESULT_OK,所以不會(huì)直接 return
if (resultCode != Activity.RESULT_OK) {
return;
}
// 如此判斷方便有多個(gè)回傳時(shí)使用
if (requestCode == REQUEST_DATE) {
Date date = (Date) data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date); mDateButton.setText(mCrime.getDate().toString());
}
}
3. 挑戰(zhàn)練習(xí)
//待完成
GitHub Page: kniost.github.io
簡書:http://www.itdecent.cn/u/723da691aa42