14

Je migre mes boîtes de dialogue, en utilisant actuellement Activity.showDialog(DIALOG_ID);, pour utiliser le système DialogFragment comme indiqué dans le android reference.Rappel de DialogFragment lors du changement d'orientation

Il y a une question qui se pose au cours de mon développement lors de l'utilisation callbacks pour livrer un événement de retour à l'activité/fragment qui a ouvert la boîte de dialogue:

Voici quelques exemples de code d'un dialogue 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(); 
}} 

Et voici un code d'activité l'appelant:

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

Le code est à peu près ce que vous voyez la référence. Le problème est qu'une fois que vous changez d'orientation, lorsqu'une boîte de dialogue est affichée, elle cesse de fonctionner comme prévu -> En raison du cycle de vie de l'activité, l'activité et la boîte de dialogue sont reconstruites et le dialogue n'a plus le bon référence à la nouvelle activité reconstruite.

j'ai ajouté le code suivant à mes activitées méthode onResume:

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

Faire cela, je reçois le comportement attendu, et la boîte de dialogue envoie des événements de retour à la nouvelle activité Reconstruit lors d'un changement d'orientation est survenue.

Ma question est: Quelle est la «meilleure pratique» pour gérer les rappels entre le DialogFragment qui a été ouvert par un FragmentActivity lors d'un changement d'orientation?

Meilleures salutations

+0

On dirait que vous êtes tombé d'un piège Fragment de nombreux. J'ai eu le même problème que vous et j'ai pu le résoudre en lisant cet excellent article: http://code.hootsuite.com/orientation-changes-on-android/. –

Répondre

6

Oui, c'est un piège courant que je tombe tout le temps moi-même. Tout d'abord permettez-moi de dire que votre solution d'appeler DialogTest.udateListener() dans onResume() semble être tout à fait approprié pour moi.

Une autre façon serait d'utiliser un ResultReceiver qui peut être sérialisé comme 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(); 
}} 

Ensuite, vous pouvez tout gérer dans le récepteur comme ceci:

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

Check out ResultReceiver doesn't survire to screen rotation pour plus de détails. Donc, à la fin, vous avez probablement encore besoin de rewire le ResultReceiver avec votre Activity.La seule différence est que vous déconnectez le Activity du DialogFragment.

+0

Merci pour votre contribution! Pour l'instant j'utiliserai la solution 'onResume()', une fois que j'en aurai besoin. Activité <-> DialogFragment découplage Je vais jeter un coup d'œil à ResultReceiver. – darksaga

+2

Vérifiez Fragment # setRetainInstance (boolean) et lisez la documentation. C'est en fait la solution que nous recherchons vraiment. Alors que l'activité est toujours détruite et recréée, le fragment est conservé et réutilisé. Par conséquent, le rappel DialogTestListener pointe toujours vers l'objet correct et vous n'avez pas besoin de recâbler les fragments après la modification de la configuration. –

+0

Le problème avec cette solution est que si vous activez "Ne pas garder les activités" ouvrir la boîte de dialogue, puis appuyez sur ACCUEIL et ouvrez l'application à partir du lanceur à domicile, la boîte de dialogue sera recréée et vous obtiendrez une exception BadParcelableException, je me bats sur ce. – David

-6

Une autre façon est que vous pouvez arrêter l'activité se recréée. Vous devez dire à Android que vous allez gérer les changements d'orientation vous-même et Android ne recréera pas votre activité. Vous devez ajouter ceci pour votre activité à votre fichier manifeste:

android:configChanges="keyboardHidden|orientation" 

Sinon cela, vous pouvez utiliser la norme onSaveInstanceState() pour sauver votre état et récupérer à l'aide savedInstanceState tel que recommandé par Google.

est ici guide officiel de Google pour elle: http://developer.android.com/guide/components/activities.html#Lifecycle

Traversez si vous avez pas déjà. Cela va vraiment vous aider dans le développement Android.

+0

Je ne veux pas arrêter l'activité détruite et recréée pendant un changement d'orientation, son fonctionnement comme prévu à cet égard. Je veux seulement connaître la meilleure pratique pour manipuler les rappels entre le DialogFragment qui a été ouvert par un FragmentActivity pendant des changements d'orientation. – darksaga

1

D'abord, appelez le setTargetFragment de FragmentParent pour démarrer dialogFragment. Dans dialogFragment, utilisez getTargetFragment pour rappeler le fragment et retourner les données. Toutes les données résultat excute dans onactivityresult de FragmentParent

suivez ce lien: Receive result from DialogFragment

+0

Bien que ce soit correct, il ne fait rien à une question. Parce qu'un transfert de données et la restauration d'un rappel après une rotation sont des choses différentes. Par exemple, dans le rappel, je peux ouvrir une autre activité ou faire une demande. – CoolMind

2

Alors que la solution d'André fonctionne, une meilleure solution est d'obtenir l'activité mis à jour pendant onAttach() dans votre Fragment.

private DialogTestListener mListener; 

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

Avec cette solution, vous aurez pas besoin de passer le Activity dans newInstance() plus. Vous avez juste besoin de vous assurer que le Activity possédant votre Fragment est un DialogTestListener. Vous n'avez également pas besoin de sauvegarder l'état comme dans la solution ResultReceiver.

8

Il existe une meilleure solution au lieu d'utiliser des méthodes statiques et des variables car cela ne fonctionnerait que pour une instance de votre boîte de dialogue. Il est préférable de stocker votre rappel en tant que membre non statique

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

Ensuite, vous devez montrer votre boîte de dialogue avec TAG comme celui-ci mDialogFragment.show(getSupportFragmentManager(), DIALOG_TAG);

Et puis dans la méthode onResume de votre activité, vous pouvez réinitialiser votre écouteur

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

Merci - cette méthode a fonctionné pour moi! Première fois que j'utilise Fragments de dialogue avec un rappel. Je me débattais avec ça pendant un moment - heureusement, j'ai vu ce post. J'y étais presque moi-même mais ce post m'a aidé à trouver une solution +1! –

+0

Jason Long donne la meilleure solution si vous appelez le dialogue d'une activité, cuasodayleo donne la meilleure solution si vous l'appelez à partir d'un fragment, et c'est la meilleure solution si vous avez besoin de faire face aux deux scénarios. – natario

Questions connexes