14

Estoy migrando mis cuadros de diálogo, actualmente usando Activity.showDialog(DIALOG_ID);, para usar el sistema DialogFragment como se describe en el android reference.DialogFragmento de devolución de llamada en el cambio de orientación

Hay una pregunta que surgió durante mi desarrollo al utilizar devoluciones de llamada para ofrecer algún evento de regreso a la actividad/fragmento que abrió el diálogo:

Aquí hay un código de ejemplo de un diálogo sencillo:

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(); 
}} 

Y aquí hay un código de actividad llamándolo:

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"); 
}} 

El código es más o menos lo que ve la referencia. El problema es que una vez que haces un cambio de orientación, cuando se muestra un diálogo, deja de funcionar como se esperaba -> Debido al ciclo de vida de la actividad, ambos, la actividad y el diálogo se reconstruyen, y el diálogo ahora no tiene el correcto referencia a la nueva actividad reconstruida.

I añade el siguiente código a mis activitys método onResume:

@Override 
protected void onResume() { 
    super.onResume(); 
    DialogTest.udateListener(this); 
} 

Hacer esto, consigo el comportamiento esperado y el diálogo envía eventos de nuevo a la nueva actividad reconstruido cuando se produjo un cambio de orientación.

Mi pregunta es: ¿Cuál es el 'mejores prácticas' para manejar las devoluciones de llamada entre el DialogFragment que fue abierto por un FragmentActivity durante un cambio de orientación?

Saludos

+0

Suena como si te hubieras caído en una trampa de muchos Fragmentos. Tuve el mismo problema que tú y pude solucionarlo mientras leía este excelente artículo: http://code.hootsuite.com/orientation-changes-on-android/. –

Respuesta

6

Sí, esta es una trampa común en la que me estoy cayendo todo el tiempo. Antes que nada, déjenme decirles que su solución de llamar al DialogTest.udateListener() en onResume() parece ser totalmente apropiada para mí.

Una forma alternativa sería utilizar un ResultReceiver que puede ser serializado como 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(); 
}} 

A continuación, puede manejar todo en el receptor como esto:

protected void onReceiveResult(int resultCode, Bundle resultData) { 
    if (getActivity() != null){ 
     // Handle result 
    } 
} 

Salida ResultReceiver doesn't survire to screen rotation de más detalles. Así que, al final, es probable que aún necesite volver a conectar el ResultReceiver con su Activity.La única diferencia es que desacopla el Activity del DialogFragment.

+0

Thx por su entrada! Por ahora usaré la solución 'onResume()', una vez que necesite algo de actividad <-> DialogFragment desacoplado Echaré un vistazo al ResultReceiver. – darksaga

+2

Echa un vistazo al Fragmento # setRetainInstance (booleano) y lee la documentación. Esta es realmente la solución que realmente estamos buscando. Si bien la actividad todavía se está destruyendo y recreando, el fragmento se conservará y reutilizará. Por lo tanto, la devolución de llamada DialogTestListener sigue apuntando al objeto correcto y no es necesario volver a cablear los Fragmentos después del cambio de configuración. –

+0

El problema con esta solución es que si activa "No mantener actividades" abra el cuadro de diálogo, luego presione INICIO y abra la aplicación desde el inicio automático, se volverá a crear el cuadro de diálogo y obtendrá una excepción BadParcelableException, estoy luchando en este. – David

-6

Otra forma es que se puede dejar de recibir recrea la actividad. Tienes que decirle a Android que cambiarás la orientación tú mismo y Android no volverá a crear tu actividad. Es necesario agregar esto para su actividad a su archivo de manifiesto:

android:configChanges="keyboardHidden|orientation" 

Si no es esto, entonces usted puede utilizar estándar onSaveInstanceState() para guardar su estado y recuperar usando savedInstanceState según lo recomendado por Google.

Aquí es guía oficial de Google para ello: http://developer.android.com/guide/components/activities.html#Lifecycle

Ir a través de él, si no lo ha hecho. Realmente te ayudará en el desarrollo de Android.

+0

No quiero detener la actividad que se está destruyendo y recreando durante un cambio de orientación, está funcionando como se esperaba al respecto. Solo quiero saber cuál es la mejor práctica para manejar las devoluciones de llamada entre el DialogFragment que fue abierto por FragmentActivity durante los cambios de orientación. – darksaga

1

En primer lugar, llame al setTargetFragment desde FragmentParent para iniciar dialogFragment. En dialogFragment use getTargetFragment para devolver el fragmento y devolver datos. Todos los datos de resultado será excute en onactivityresult de FragmentParent

siga este enlace: Receive result from DialogFragment

+0

Si bien esto es correcto, no hace una pregunta. Porque una transferencia de datos y la restauración de una devolución de llamada después de la rotación son cosas diferentes. Por ejemplo, en la devolución de llamada puedo abrir otra actividad o hacer una solicitud. – CoolMind

2

mientras que la solución de André trabaja, una mejor solución es conseguir que la actividad actualizada durante onAttach() en su Fragment.

private DialogTestListener mListener; 

@Override 
public void onAttach(Activity activity) { 
    super.onAttach(activity); 
    mListener = (DialogTestListener) activity; 
} 

Con esta solución, usted no tendrá que pasar el Activity en newInstance() más. Solo necesita asegurarse de que el Activity que posee su Fragment es un DialogTestListener. Tampoco necesita guardar el estado como en la solución ResultReceiver.

8

Hay una mejor solución en lugar de utilizar métodos estáticos y variables porque funcionaría solo para una instancia de su cuadro de diálogo. Es mejor almacenar su devolución de llamada como miembro no estática

private DialogTestListener mListener; 
public void setListener (DialogTestListener listener){ 
    mListener = listener; 
} 

Entonces usted debe mostrar su diálogo mediante TAG como esto mDialogFragment.show(getSupportFragmentManager(), DIALOG_TAG);

Y luego, en onResume método de su actividad puede restablecer su oyente

protected void onResume() { 
    super.onResume(); 
    mDialogFragment = (CMFilterDialogFrg) getSupportFragmentManager().findFragmentByTag(DIALOG_TAG); 
    if(mDialogFragment != null){ 
     mDialogFragment.setListener(yourListener) 
    } 
} 
+0

Gracias - ¡este método funcionó para mí! La primera vez que utilizo los Fragmentos de diálogo con una devolución de llamada. Estuve luchando con esto por un tiempo, afortunadamente vi esta publicación. Casi estuve allí, pero esta publicación ayudó a alcanzar una solución +1. –

+0

Jason Long ofrece la mejor solución si llama al diálogo desde una Actividad, cuasodayleo ofrece la mejor solución si la llama desde un Fragmento, y esta es la mejor solución si necesita enfrentar ambos escenarios. – natario

Cuestiones relacionadas