dialogo - input dialog fragment android
Devolución de llamada de DialogFragment en cambio de orientación (5)
Estoy migrando mis diálogos, actualmente estoy usando Activity.showDialog(DIALOG_ID);
, para usar el sistema DialogFragment
como se describe en la referencia de Android .
Hay una pregunta que surgió durante mi desarrollo al usar devoluciones de llamada para devolver algún evento a la actividad / fragmento que abrió el diálogo:
Aquí hay un código de ejemplo de un diálogo simple:
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 prácticamente lo que ves en la referencia. El problema es que, una vez que realiza un cambio de orientación, cuando se muestra un cuadro de diálogo, deja de funcionar como se esperaba -> Debido al ciclo de vida de la actividad, tanto la actividad como el cuadro de diálogo se reconstruyen, y el cuadro de diálogo ahora no tiene la configuración adecuada. Referencia a la nueva actividad reconstruida.
Agregué el siguiente código a mis actividades en el método de Reanudar:
@Override
protected void onResume() {
super.onResume();
DialogTest.udateListener(this);
}
Al hacer esto, obtengo el comportamiento esperado y el cuadro de diálogo envía los eventos a la nueva actividad reconstruida cuando se produce un cambio de orientación.
Mi pregunta es: ¿Cuál es la "mejor práctica" para manejar las devoluciones de llamada entre el DialogFragment que fue abierto por un FragmentActivity durante un cambio de orientación?
Atentamente
Hay una mejor solución en lugar de usar métodos y variables estáticos porque funcionaría solo para una instancia de su diálogo. Es mejor almacenar su devolución de llamada como miembro no estático
private DialogTestListener mListener;
public void setListener (DialogTestListener listener){
mListener = listener;
}
Entonces debería mostrar su diálogo usando TAG como este mDialogFragment.show(getSupportFragmentManager(), DIALOG_TAG);
Y luego, en el método onResume
de su actividad, puede restablecer su escucha
protected void onResume() {
super.onResume();
mDialogFragment = (CMFilterDialogFrg) getSupportFragmentManager().findFragmentByTag(DIALOG_TAG);
if(mDialogFragment != null){
mDialogFragment.setListener(yourListener)
}
}
Mientras que la solución de André funciona, una mejor solución es obtener 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, ya no tendrá que pasar la Activity
en newInstance()
. Solo debes asegurarte de que la Activity
posee tu Fragment
sea un DialogTestListener
. Tampoco necesita guardar el estado como en la solución ResultReceiver
.
Otra forma es que puedas detener la recreación de la actividad. Tienes que decirle a Android que manejarás el cambio de orientación y Android no volverá a crear tu actividad. Debe agregar esto para su actividad a su archivo de manifiesto:
android:configChanges="keyboardHidden|orientation"
Si no es así, entonces puedes usar el estándar onSaveInstanceState()
para guardar tu estado y recuperarlo utilizando savedInstanceState
como lo recomienda Google.
Aquí está la guía oficial de Google para ello: http://developer.android.com/guide/components/activities.html#Lifecycle
Revísalo si aún no lo has hecho. Realmente te ayudará en el desarrollo de Android.
Primero, llame a setTargetFragment
desde FragmentParent
para iniciar dialogFragment
. En dialogFragment
use getTargetFragment
para devolver el fragmento de devolución y devolver los datos. El resultado de todos los datos se ejecutará en un onactivityresult
de onactivityresult
de FragmentParent
siga este enlace: Recibir el resultado de DialogFragment
Sí, esta es una trampa común en la que me estoy cayendo todo el tiempo. En primer lugar, permítame decir que su solución de llamar a DialogTest.udateListener()
en onResume()
parece ser completamente apropiada para mí.
Una forma alternativa sería utilizar un ResultReceiver
que se puede serializar como un 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();
}}
Entonces puedes manejar todo en el Receptor de esta manera:
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (getActivity() != null){
// Handle result
}
}
Echa un vistazo a ResultReceiver no sobrevive a la rotación de la pantalla para obtener más detalles. Entonces, al final, es probable que aún deba volver a cablear el ResultReceiver
con su Activity
. La única diferencia es que se desacopla la Activity
del DialogFragment
.