本章主要介绍了如何使用内嵌在 fragment 中的对话框,以及 fragment 之间如何传递数据
GitHub 地址:
完成第12章,挑战将另开分支完成
Google 推出 AppCompat 兼容库是为了让所有Android用户都能体验到新特性。AppCompat兼容库能通过支持库的方式将部分最新系统的特色功能移植到Android旧版本系统中。
应该在 Porject Structure 中添加 appcompat-v7 的依赖。
建议将
AlertDialog
封装在DialogFragment
(Fragment的子类)实例中使用。当然,不使用DialogFragment
也可显示AlertDialog
视图,但不推荐这样做。使用FragmentManager
管理对话框,可以更灵活地显示对话框。如果旋转设备,单独使用的
AlertDialog
会消失,而封装在 fragment 中的AlertDialog
则不会有此问题(旋转后,对话框会被重建恢复)。
<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">
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);
我们之前实现了 activity 之间以及基于 fragment 的 activity 之间的数据传递。现在需实现同一 activity 托管的两个 fragment 之间的数据传递。
显然,要达到目的,只需要在 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)
方法 初始化默认日期
类似于 activity 间的关联,可将 CrimeFragment 设置成 DatePickerFragment 的目标 fragment。 即使是在 CrimeFragment 和 DatePickerFragment 被销毁和重建后,操作系统也会重新关联它们。调用以下 Fragment 方法可建立这种关联:
public void setTargetFragment(Fragment fragment, int requestCode)
该方法有两个参数:目标 fragment 以及请求代码。需要时,目标 fragment 使用请求代码确认是哪个 fragment 在回传数据。
目标 fragment 和请求代码由 FragmentManager 负责跟踪管理,我们可调用设置目标的 fragment 的 getTargetFragment()
和 getTargetRequestCode()
方法获取它们。
处理由同一 activity 托管的两个 fragment 间的数据返回时,可借用Fragment.onActivityResult(...)
方法。因此,直接调用目标 fragment 的Fragment.onActivityResult(...)
方法,,就能实现数据的回传。该方法恰好有我们需要的如下信息:
setTargetFragment(...)
方法相匹配,告诉目标 fragment 返回结果来 自哪里。所以从 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());
}
}
//待完成