项目中的逻辑结构图

CrimeFragment 和 DatePickerFragment间的对话

主要思想:

  • 要传递crime 的日期给DatePickerFragment,需新建一个newInstance(Date)方法,然后将Date作为 argument 附加给 fragment。
  • 要将新日期返回给CrimeFragment, 并更新模型层以及对应的视图,需将日期打包为extra并附加到Intent,然后调用CrimeFragment.onActivityResult(…)方法,并传入准备好的Intent参数
CrimeFragment 和 DatePickerFragment间的事件流
CrimeFragment 和 DatePickerFragment间的事件流

argument回顾

在之前Activity 和 Fragment 传值的过程中,我们使用了 Argument,下面我们回顾一下我们是如何使用它的。

Argument使用
Argument使用

为满足以上要求,通常添加名为 newInstance() 的静态方法给 Fragment类。使用该方法,完成fragment实例及Bundle对象的创建,然后将argument 放入bundle中,最后附加给fragment。

托管activity需要fragment实例时,转而调用newInstance() 方法,而非直接调用其构造函数。并且,为满足fragment创建argument的要求,activity可给newInstance()传入任何需要的参数

传值

那么,fragment之间传值同样也需要借助argument

1
2
3
4
5
6
7
8
//DatePickerFragment 中的 newInstance 方法,返回一个DPF实例
public static DatePickerFragment newInstance(Date date){
Bundle args = new Bundle();
args.putSerializable(ARG_DATE, date);
DatePickerFragment fragment = new DatePickerFragment();
fragment.setArguments(args);
return fragment;
}

当然,与之对应,在 CrimeFragment 中创建 dialog 语句也要改成调用 newInstance 的形式

1
2
//CrimeFragment 中点击事件开启DatePickerFragment
DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());

另外,这里的Date不能直接被 DatePickerFragment 所识别,我们需要对 DatePickerFragment 所接收到的 Date 进行处理,当然这不是重点,这里不展开。

返回更新数据

首先,我们要弄清这两个fragment之间的关系。如果是activity的数据回传,我们调用startActivityForResult(…)方法,ActivityManager负责跟踪管理activity父子关系。回传数据后,子activity被销毁,但ActivityManager知道接收数据是哪个activity。

设置目标fragment(关联)

可将CrimeFragment设置成DatePickerFragment的Target fragment。
这样,在CrimeFragment 和 DatePickerFragment 被销毁并重建后,操作系统会重新关联他们。调用以下方法可建立这种关联

1
public void setTargetFragment(Fragment fragment, int requestCode)

在CrimeFragment 中,创建请求代码常量,然后将CrimeFragment设为DatePickerFragment 实例的目标fragment

1
2
3
4
5
6
7
8
9
10
private static final int REQUEST_DATE = 0;

//在onCreatView中设置按钮监听器
@Override
public void onClick(View view) {
FragmentManager manager = getFragmentManager();
DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
dialog.show(manager, DIALOG_DATE);
}

传递数据给目标fragment(传值)

处理有同一个activity托管的两个fragment之间的数据返回时,可借用Fragment.onActivityResult(…)方法。因此,直接调用目标fragment的Fragment.onActivityResult(…)方法,就能实现数据回传。需要以下信息:

  • 请求代码:与传入 setTargetFragment(…) 方法的代码相匹配,告诉目标 fragment 返回结果来自哪里
  • 结果代码:决定下一步该采取什么行动
  • Intent:包含extra数据

我们来看一下具体怎么做的

1
2
3
4
5
6
7
8
9
10
11
12
13

public static final String EXTRA_DATE = "com.example.criminalintent.date";

private void sendResult(int resultCode, Date date){
if(getTargetFragment() == null){
return;
}

Intent intent = new Intent();
intent.putExtra(EXTRA_DATE, date);

getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
}

随后,我们将 DatePickerFragment 的确定按钮点击事件修改为调用sendResult(…)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
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 dialogInterface, int i) {
int year = mDatePicker.getYear();
int month = mDatePicker.getMonth();
int day = mDatePicker.getDayOfMonth();
Date date = new GregorianCalendar(year, month, day).getTime();
sendResult(Activity.RESULT_OK, date);
}
})

最后,在CrimeFragment接收数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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);
mCrime.setDate(date);
updateDate();
}
}

private void updateDate() {
mDateButton.setText(mCrime.getDateStr());
}