Введение
В этой публикации я покажу, как можно передавать события из DialogFrament в вызывающий Fragment минуя Activity.
В официальном Guide по Dialogs есть раздел PassingEvents. В нем рассказано, как вернуть результат работы DialogFragment в вызывающую Activity. Для этого создается дополнительный интерфейс. Activity реализует этот интерфейс, а у DialogFrament есть ссылка на Activity.
Если инициатором вызова DialogFragment является другой Fragment, то в таком подходе нам придется сначала отправить результат в Activity, а потом уже из Activity в заинтересованный в данных Fragment. Отсюда и минусы:
- Activity знает о деталях реализации фрагмента (сегодня это дополнительный диалог, а завтра можем реализовать все на одном фрагменте);
- дополнительный код (больше кода = больше возможностей для ошибок).
К счастью, у класса Fragment есть 3 метода, которые позволят реализовать передачу событий из одного фрагмента в другой фрагмент минуя Activity. Это:
- void setTargetFragment(Fragment fragment, int requestCode)
- Fragment getTargetFrament
- int getTargetRequestCode()
Суть метода
Вызывающий фрагмент:
- с помощью setTargetFragment устанавливает себя в качестве targetFrament и устанавливает requestCode;
- реализует метод onActivityResult в котором обрабатывает requestCode и resultCode, а также имеет доступ к дополнительным данным через intent.
Вызываемый фрагмент:
- c помощью getTargetFrament получает ссылку на вызывающий фрагмент;
- с помощью getTargetRequestCode получает код с которым он был вызыван;
- вызывает onActivityResult вызывающего фрагмента и передает результаты своего выполнения через resultCode и Intent (для дополнительных).
Ниже пример кода. Для простоты оставил только актуальные для статьи части.
Вызывающий фрагмент:
public class HostFragment extends Fragment {
private static final int REQUEST_WEIGHT = 1;
private static final int REQUEST_ANOTHER_ONE = 2;
public void openWeightPicker() {
DialogFragment fragment = new WeightDialogFragment();
fragment.setTargetFragment(this, REQUEST_WEIGHT);
fragment.show(getFragmentManager(), fragment.getClass().getName());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case REQUEST_WEIGHT:
int weight = data.getIntExtra(WeightDialogFragment.TAG_WEIGHT_SELECTED, -1)
//используем полученные результаты
//...
break;
case REQUEST_ANOTHER_ONE:
//...
break;
//обработка других requestCode
}
updateUI();
}
}
}
Вызываемый фрагмент:
public class WeightDialogFragment extends DialogFragment {
//тэг для передачи результата обратно
public static final String TAG_WEIGHT_SELECTED = "weight";
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(..., null);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//отправляем результат обратно
Intent intent = new Intent();
intent.putExtra(TAG_WEIGHT_SELECTED, mNpWeight.getValue());
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent);
}
});
return builder.create();
}
}
Заключение
Перечисленные в начале статьи минусы в таком подходе отсутствуют. Только два взаимодействующих элемента знают друг о друге. Для остальных это уже неизвестные детали реализации.
Мне бы хотелось узнать, о таком варианте реализации еще при изучении официального гида по диалогам. Я же узнал позже благодаря StackOverflow.