Обратный вызов DialogFragment при изменении ориентации - программирование
Подтвердить что ты не робот

Обратный вызов 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 во время изменения ориентации?

С наилучшими пожеланиями

4b9b3361

Ответ 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.