本章主要介绍了如何使用内嵌在 fragment 中的对话框,以及 fragment 之间如何传递数据
GitHub 地址:
完成第12章
1. 使用 DialogFragment
1.1 AppCompat 兼容库
Google 推出 AppCompat 兼容库是为了让所有Android用户都能体验到新特性。AppCompat兼容库能通过支持库的方式将部分最新系统的特色功能移植到Android旧版本系统中。
应该在 Porject Structure 中添加 appcompat-v7 的依赖。
1.2 创建 DialogFragment
建议将
AlertDialog
封装在DialogFragment
(Fragment的子类)实例中使用。当然,不使用DialogFragment
也可显示AlertDialog
视图,但不推荐这样做。使用FragmentManager
管理对话框,可以更灵活地显示对话框。
如果旋转设备,单独使用的 AlertDialog
会消失,而封装在 fragment 中的 AlertDialog
则不会有此问题(旋转后,对话框会被重建恢复)。
- 首先需要一个针对 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">
<!--旧版系统会使用 calenderViewShown 属性-->
</DatePicker>
- 然后新建一个父类是
DialogFragment
的类DatePickerFragment
,重写其中的onCreateDialog
方法,返回一个AlertDialog
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// 使用 LayoutInflater 引用布局文件创建用于显示 Dialog 的 View
View v = LayoutInflater.from(getActivity())
.inflate(R.layout.dialog_date, null);
return new AlertDialog.Builder(getActivity())
.setView(v) // 设置视图
.setTitle(R.string.date_picker_title) //设置标题
// 设置 OK 按钮,OnClickListener 暂时留空
.setPositiveButton(android.R.string.ok, null)
// 使用 Builder 的 create() 方法创建 Dialog 并返回
.create();
}
在使用 DialogFragment 时,使用成员方法 show 来显示 dialog:
// 在 Fragment 中为 DatePickerFragment 添加一个 Tag
private static final String DIALOG_DATE = "DialogDate";
……
// 在 Fragment 内部获取 FragmentManager
FragmentManager manager = getFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
// 显示对话框
dialog.show(manager, DIALOG_DATE);
2. fragment 之间的数据传递
我们之前实现了 activity 之间以及基于 fragment 的 activity 之间的数据传递。现在需实现同一 activity 托管的两个 fragment 之间的数据传递。
2.1 将数据传递到对话框
显然,要达到目的,只需要在 DatePickerFragment 中建立获取实例的 newInstance 方法,其中需要的参数是传递的信息即可,示例如下
// DatePickerFragment.java
public static DatePickerFragment newInstance(Date date) {
// 新建一个 Bundle 对象用于存放数据
Bundle args = new Bundle();
args.putSerializable(ARG_DATE, date);
DatePickerFragment fragment = new DatePickerFragment();
// 使用 fragment arguments 来传递参数
fragment.setArguments(args);
return fragment;
}
记得把使用 DatePickerFragment 的构造方法的地方改成 newInstance 方法获取实例。
在获得数据之后,要先将 DatePicker 初始化为原本的日期,首先用 Fragment 的 getArguments().getSeriallizable(String key)
方法获取数据,然后用 Calendar 对象取出 date 中的年月日,最后使用 DatePicker 类的 init(int year, int month, int dayOfMonth, OnDateChangedListener listener)
方法 初始化默认日期
2.2 从对话框回传数据
2.2.1 设置目标 fragment
类似于 activity 间的关联,可将 CrimeFragment 设置成 DatePickerFragment 的目标 fragment。 即使是在 CrimeFragment 和 DatePickerFragment 被销毁和重建后,操作系统也会重新关联它们。调用以下 Fragment 方法可建立这种关联:
public void setTargetFragment(Fragment fragment, int requestCode)
该方法有两个参数:目标 fragment 以及请求代码。需要时,目标 fragment 使用请求代码确认是哪个 fragment 在回传数据。
目标 fragment 和请求代码由 FragmentManager 负责跟踪管理,我们可调用设置目标的 fragment 的 getTargetFragment()
和 getTargetRequestCode()
方法获取它们。
2.2.2 传递数据给目标 Fragment
处理由同一 activity 托管的两个 fragment 间的数据返回时,可借用Fragment.onActivityResult(...)
方法。因此,直接调用目标 fragment 的Fragment.onActivityResult(...)
方法,,就能实现数据的回传。该方法恰好有我们需要的如下信息:
- 请求代码:与传入
setTargetFragment(...)
方法相匹配,告诉目标 fragment 返回结果来 自哪里。 - 结果代码:决定下一步该采取什么行动。
- Intent:包含 extra 数据。
所以从 CrimeFragment 中显示 DatePickerFragment,用户选择日期以后,想要回传信息,可以写一个 sendResult 方法,该方法如下:
// DatePickerFragment.java
public static final String EXTRA_DATE =
"com.kniost.criminalintent.date";
……
private void sendResult(int resultCode, Date date) {
// 防止出错
if (getTargetFragment() == null) {
return;
}
Intent intent = new Intent();
// 放置数据到 Intent 中
intent.putExtra(EXTRA_DATE, date);
// 获取目标 fragment,调用其 onActivityResult 方法,其中 RequestCode 是用 getTargetRequestCode 方法获取的,resultCode 是传入参数,intent 包含了数据
getTargetFragment()
.onActivityResult(getTargetRequestCode(), resultCode, intent);
}
在 CrimeFragment 中则应该重写 onActivityResult 方法:
// CrimeFragment.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// 如果一切正常,调用 sendResult 的方法时传入的参数应该就是 Activity.RESULT_OK,所以不会直接 return
if (resultCode != Activity.RESULT_OK) {
return;
}
// 如此判断方便有多个回传时使用
if (requestCode == REQUEST_DATE) {
Date date = (Date) data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date); mDateButton.setText(mCrime.getDate().toString());
}
}
3. 挑战练习
//待完成
GitHub Page: kniost.github.io
简书:http://www.jianshu.com/u/723da691aa42