14

Przenoszę moje okna dialogowe, obecnie używające Activity.showDialog(DIALOG_ID);, do korzystania z systemu DialogFragment, jak omówiono w android reference.Wywołanie zwrotne DialogFragment podczas zmiany orientacji

Jest to pytanie, które powstały podczas mojego rozwoju przy użyciu wywołania zwrotne dostarczyć pewne zdarzenie z powrotem do aktywności/fragmentu, który otworzył okno:

Oto niektóre przykładowy kod prostego dialogu:

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

A oto kod wywołujący go:

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

Kod jest prawie taki, jak ten odnośnik. Problem polega na tym, że po dokonaniu zmiany orientacji, kiedy okno dialogowe jest wyświetlane, przestaje działać zgodnie z oczekiwaniami -> Z powodu cyklu życia aktywności, zarówno działanie, jak i okno dialogowe są przebudowywane, a okno dialogowe nie ma właściwego odniesienie do nowej przebudowanej działalności.

I dodaje następujący kod do moich activitys metodę onResume:

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

Spowoduje to, uzyskać oczekiwane zachowanie, a okno dialogowe wysyła zdarzenia z powrotem do nowej aktywności odbudowanej gdy zmiana orientacji wystąpił.

Moje pytanie brzmi: Co to jest "najlepsza praktyka" do obsługi wywołań zwrotnych między DialogFragment, który został otwarty przez FragmentActivity podczas zmiany orientacji?

Pozdrawiam

+0

Wygląda na to, że zgubiłeś jedną z wielu pułapek Fragmentów. Miałem ten sam problem co ty i mogłem to naprawić, czytając ten wspaniały artykuł: http://code.hootsuite.com/orientation-changes-on-android/. –

Odpowiedz

6

Taa, to jest zwykła pułapka, którą sam wpadam cały czas. Przede wszystkim pozwól mi powiedzieć, że twoje rozwiązanie wywoływania DialogTest.udateListener() w onResume() wydaje się być w pełni odpowiednie dla mnie.

Alternatywnym sposobem byłoby użyć ResultReceiver które można odcinkach jako 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(); 
}} 

Następnie można obsługiwać wszystko w odbiorniku jak ten:

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

Wymeldowanie ResultReceiver doesn't survire to screen rotation dla więcej szczegółów. W związku z tym prawdopodobnie nadal trzeba będzie ponownie podłączyć ResultReceiver do swojego urządzenia Activity.Jedyna różnica polega na tym, że odłączysz Activity od DialogFragment.

+0

Thx za wejście! Na razie użyję rozwiązania 'onResume()', gdy tylko będę potrzebował jakiegoś działania Rozłączenie DialogFragment Będę musiał rzucić okiem na ResultReceiver. – darksaga

+2

Sprawdź Fragment # setRetainInstance (boolean) i przeczytaj dokumentację. W rzeczywistości jest to rozwiązanie, którego naprawdę szukamy. Podczas gdy Działanie jest nadal niszczone i odtwarzane, Fragment jest zachowywany i ponownie wykorzystywany. Dlatego też wywołanie zwrotne DialogTestListener wciąż wskazuje poprawny obiekt i nie trzeba przepinać fragmentów po zmianie konfiguracji. –

+0

Problem z tym rozwiązaniem polega na tym, że po aktywowaniu opcji "Nie wypełniaj czynności" otwórz okno dialogowe, a następnie naciśnij przycisk HOME i otwórz aplikację z menu startowego, okno dialogowe zostanie odtworzone, a otrzymasz wyjątek BadParcelableException. na to. – David

-6

Innym sposobem jest zatrzymanie odtwarzania. Musisz powiedzieć Androidowi, że sam poradzisz sobie ze zmianą orientacji, a Android nie odtworzy Twojej aktywności. Trzeba dodać to do swojej działalności do pliku manifestu:

android:configChanges="keyboardHidden|orientation" 

Jeśli nie to, to można użyć standardowego onSaveInstanceState(), aby zapisać swój stan i odzyskać stosując savedInstanceState zalecane przez Google.

Oto oficjalny przewodnik Google dla niego: http://developer.android.com/guide/components/activities.html#Lifecycle

Przejdź przez niego, jeśli nie masz. To naprawdę pomoże Ci w rozwoju Androida.

+0

Nie chcę, aby działanie zostało zniszczone i odtworzone podczas zmiany orientacji, działa zgodnie z oczekiwaniami w tym zakresie. Chcę tylko znać najlepszą praktykę obsługi wywołań zwrotnych między DialogFragment, który został otwarty przez FragmentActivity podczas zmiany orientacji. – darksaga

1

Najpierw zadzwoń pod numer setTargetFragment z FragmentParent, aby rozpocząć dialogFragment. W dialogFragment użyj getTargetFragment do fragmentu wywołania zwrotnego i danych zwracanych. Wszystkie dane skutkować będzie excute w onactivityresult z FragmentParent

poniższy link: Receive result from DialogFragment

+0

Chociaż jest to poprawne, nie robi nic na pytanie. Ponieważ przesyłanie danych i przywracanie wywołania zwrotnego po rotacji są różne. Na przykład w wywołaniu zwrotnym mogę otworzyć inne działanie lub wysłać żądanie. – CoolMind

2

Choć rozwiązanie Andre działa, lepszym rozwiązaniem jest, aby uzyskać zaktualizowaną aktywność podczas onAttach() w swojej Fragment.

private DialogTestListener mListener; 

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

Dzięki takiemu rozwiązaniu nie trzeba będzie zdać Activity w newInstance() więcej. Musisz tylko upewnić się, że Activity posiadający swoje Fragment jest DialogTestListener. Nie musisz także zapisywać stanu, jak w przypadku rozwiązania ResultReceiver.

8

Zamiast korzystania ze statycznych metod i zmiennych istnieje lepsze rozwiązanie, ponieważ działałoby tylko dla jednej instancji okna dialogowego. Lepiej jest przechowywać zwrotnego jako non członka statycznego

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

wówczas okno należy pokazać przy użyciu TAG jak ten mDialogFragment.show(getSupportFragmentManager(), DIALOG_TAG);

a następnie w onResume sposobu aktywności można przywrócić słuchacz

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

Dzięki - ta metoda zadziałała dla mnie! Pierwszy raz dla mnie, używając Fragmentów Dialogu z wywołaniem zwrotnym. Walczyłem z tym przez chwilę - na szczęście zobaczyłem ten post. Byłem już prawie sam, ale ten post pomógł osiągnąć rozwiązanie +1! –

+0

Jason Long daje najlepsze rozwiązanie, jeśli wywołasz okno dialogowe z działania, cuasodayleo daje najlepsze rozwiązanie, jeśli wywołasz je z fragmentu, i jest to najlepsze rozwiązanie, jeśli musisz zmierzyć się z obydwoma scenariuszami. – natario

Powiązane problemy