Обратный вызов DialogFragment при изменении ориентации
Я переношу свои диалоги, используя Activity.showDialog(DIALOG_ID);
, чтобы использовать систему DialogFragment
, как описано в ссылке для Android.
Возникает вопрос, возникший во время моего развития при использовании обратных вызовов для доставки некоторого события обратно в действие/фрагмент, открывший диалог:
Вот пример кода простого диалога:
public class DialogTest extends DialogFragment {
public interface DialogTestListener {
public void onDialogPositiveClick(DialogFragment dialog);
}
// Use this instance of the interface to deliver action events
static DialogTestListener mListener;
public static DialogTest newInstance(Activity activity, int titleId, int messageId) {
udateListener(activity);
DialogTest frag = new DialogTest();
Bundle args = new Bundle();
args.putInt("titleId", titleId);
args.putInt("messageId", messageId);
frag.setArguments(args);
return frag;
}
public static void udateListener(Activity activity) {
try {
// Instantiate the NoticeDialogListener so we can send events with it
mListener = (DialogTestListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString() + " must implement DialogTestListener");
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int titleId = getArguments().getInt("titleId");
int messageId = getArguments().getInt("messageId");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// dialog title
builder.setTitle(titleId);
// dialog message
builder.setMessage(messageId);
// dialog negative button
builder.setNegativeButton("No", new OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}});
// dialog positive button
builder.setPositiveButton("Yes", new OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogPositiveClick(DialogTest.this);
}});
// create the Dialog object and return it
return builder.create();
}}
И вот какой код операции вызывает его:
public class SomeActivity extends FragmentActivity implements DialogTestListener {
private EditText mUserName;
@Override
public void onCreate(Bundle savedInstanceState) {
// setup ui
super.onCreate(savedInstanceState);
setContentView(R.layout.ui_user_edit);
// name input
mUserName = (EditText) findViewById(R.id.userEdit_editTextName);
}
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
Log.d(TAG, this.toString());
mUserName.setText(mUserName.getText() + "1");
}
private void showDialog() {
DialogTest test = DialogTest.newInstance(SomeActivity.this, R.string.someTitleText, R.string.someMessageText);
test.show(getSupportFragmentManager(), "testDialog");
}}
Код в значительной степени соответствует тому, что вы видите в ссылке. Проблема заключается в том, что после изменения ориентации, когда отображается диалог, он перестает работать, как ожидалось, → Из-за жизненного цикла активности, как действие, так и диалог перестраиваются, и в диалоговом окне теперь нет надлежащего ссылка на новую восстановленную деятельность.
Я добавил следующий код в свою активность onResume:
@Override
protected void onResume() {
super.onResume();
DialogTest.udateListener(this);
}
Выполняя это, я получаю ожидаемое поведение, и диалоговое окно отправляет события обратно в новую восстановленную активность при изменении ориентации.
Мой вопрос:
Какова "наилучшая практика" для обработки обратных вызовов между диалоговым окном DialogFragment, который был открыт FragmentActivity во время изменения ориентации?
С наилучшими пожеланиями
Ответы
Ответ 1
Да, это обычная ловушка, в которую я все время падаю. Прежде всего позвольте мне сказать, что ваше решение вызова DialogTest.udateListener()
в onResume()
кажется мне полностью подходящим.
Альтернативным способом было бы использовать ResultReceiver
, который можно сериализовать как Parcelable
:
public class DialogTest extends DialogFragment {
public static DialogTest newInstance(ResultReceiver receiver, int titleId, int messageId) {
DialogTest frag = new DialogTest();
Bundle args = new Bundle();
args.putParcelable("receiver", receiver);
args.putInt("titleId", titleId);
args.putInt("messageId", messageId);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int titleId = getArguments().getInt("titleId");
int messageId = getArguments().getInt("messageId");
ResultReceiver receiver = getArguments().getParcelable("receiver");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// dialog title
builder.setTitle(titleId);
// dialog message
builder.setMessage(messageId);
// dialog negative button
builder.setNegativeButton("No", new OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
receiver.sendResult(Activity.RESULT_CANCEL, null);
}});
// dialog positive button
builder.setPositiveButton("Yes", new OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
receiver.sendResult(Activity.RESULT_OK, null);
}});
// create the Dialog object and return it
return builder.create();
}}
Тогда вы можете обрабатывать все в приемнике, как это:
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (getActivity() != null){
// Handle result
}
}
Отъезд ResultReceiver не выдерживает поворота экрана для получения более подробной информации. Поэтому, в конце концов, вам, вероятно, еще нужно переустановить ResultReceiver
с помощью Activity
. Единственное отличие состоит в том, что вы отделяете Activity
от DialogFragment
.
Ответ 2
Существует лучшее решение вместо использования статических методов и переменных, поскольку оно будет работать только для одного экземпляра вашего диалога. Лучше хранить ваш обратный вызов как нестационарный член
private DialogTestListener mListener;
public void setListener (DialogTestListener listener){
mListener = listener;
}
Затем вы должны показать свое диалоговое окно с помощью TAG, например, mDialogFragment.show(getSupportFragmentManager(), DIALOG_TAG);
И затем в onResume
метод вашей деятельности вы можете reset ваш слушатель
protected void onResume() {
super.onResume();
mDialogFragment = (CMFilterDialogFrg) getSupportFragmentManager().findFragmentByTag(DIALOG_TAG);
if(mDialogFragment != null){
mDialogFragment.setListener(yourListener)
}
}
Ответ 3
В то время как решение André работает, лучшим решением является получение обновленной активности в onAttach()
в Fragment
.
private DialogTestListener mListener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mListener = (DialogTestListener) activity;
}
С помощью этого решения вам больше не нужно будет передавать Activity
в newInstance()
. Вам просто нужно убедиться, что Activity
владеющий вашим Fragment
является DialogTestListener
. Вам также не нужно сохранять состояние, как в решении ResultReceiver
.
Ответ 4
Сначала вызовите setTargetFragment
из FragmentParent
, чтобы запустить dialogFragment
. В dialogFragment
используйте getTargetFragment
для обратного вызова и возврата данных. Все данные будут вызывать в onactivityresult
FragmentParent
перейдите по этой ссылке:
Получить результат из DialogFragment
Ответ 5
Другим способом является то, что вы можете остановить воссоздание активности. Вы должны сказать Android, что сами справитесь с изменением ориентации, и андроид не воссоздает вашу деятельность. Вы должны добавить это для своей деятельности в файл манифеста:
android:configChanges="keyboardHidden|orientation"
Если это не так, вы можете использовать стандартный onSaveInstanceState()
для сохранения состояния и восстановления с помощью savedInstanceState
, как рекомендовано Google.
Вот официальное руководство Google для него:
http://developer.android.com/guide/components/activities.html#Lifecycle
Пройдите через него, если вы еще этого не сделали. Это действительно поможет вам в разработке Android.