Wywołanie zwrotne do fragmentu z DialogFragment


159

Pytanie: W jaki sposób można utworzyć wywołanie zwrotne z DialogFragment do innego fragmentu. W moim przypadku, zaangażowana aktywność powinna być całkowicie nieświadoma DialogFragment.

Rozważ, że mam

public class MyFragment extends Fragment implements OnClickListener

Wtedy w pewnym momencie mogłem to zrobić

DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);

Gdzie wygląda MyDialogFragment

protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
    DialogFragment fragment = new DialogFragment();
    fragment.listener = listener;
    return fragment;
}

Ale nie ma gwarancji, że odbiornik będzie w pobliżu, jeśli DialogFragment zostanie wstrzymana i wznowiona przez cały cykl życia. Jedyne gwarancje we fragmencie to te przekazywane przez Bundle za pośrednictwem setArguments i getArguments.

Istnieje sposób odniesienia się do działania, jeśli powinien to być słuchacz:

public Dialog onCreateDialog(Bundle bundle) {
    OnClickListener listener = (OnClickListener) getActivity();
    ....
    return new AlertDialog.Builder(getActivity())
        ........
        .setAdapter(adapter, listener)
        .create();
}

Ale nie chcę, aby działanie nasłuchiwało wydarzeń, potrzebuję fragmentu. Naprawdę może to być dowolny obiekt Java, który implementuje OnClickListener.

Rozważ konkretny przykład fragmentu, który przedstawia AlertDialog za pośrednictwem DialogFragment. Posiada przyciski Tak / Nie. Jak mogę wysłać te naciśnięcia przycisku z powrotem do fragmentu, który go utworzył?


Wspomniałeś „Ale nie ma gwarancji, że odbiornik będzie w pobliżu, jeśli DialogFragment zatrzyma się i wznowi przez cały cykl życia”. Myślałem, że stan fragmentu zostanie zniszczony podczas onDestroy ()? Musisz mieć rację, ale jestem trochę zdezorientowany, jak teraz używać stanu Fragment. Jak odtworzyć problem, o którym wspomniałeś, słuchacza nie ma w pobliżu?
Sean

Nie rozumiem, dlaczego nie możesz OnClickListener listener = (OnClickListener) getParentFragment();zamiast tego po prostu użyć w DialogFragment, a twój główny fragment implementuje interfejs, tak jak pierwotnie.
kiruwka

Oto odpowiedź na niepowiązane pytanie, ale pokazuje, jak to się robi w czysty sposób stackoverflow.com/questions/28620026/ ...
user2288580

Odpowiedzi:


190

Aktywność zaangażowana jest całkowicie nieświadoma DialogFragment.

Klasa fragmentu:

public class MyFragment extends Fragment {
int mStackLevel = 0;
public static final int DIALOG_FRAGMENT = 1;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        mStackLevel = savedInstanceState.getInt("level");
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("level", mStackLevel);
}

void showDialog(int type) {

    mStackLevel++;

    FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
    Fragment prev = getActivity().getFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    ft.addToBackStack(null);

    switch (type) {

        case DIALOG_FRAGMENT:

            DialogFragment dialogFrag = MyDialogFragment.newInstance(123);
            dialogFrag.setTargetFragment(this, DIALOG_FRAGMENT);
            dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");

            break;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case DIALOG_FRAGMENT:

                if (resultCode == Activity.RESULT_OK) {
                    // After Ok code.
                } else if (resultCode == Activity.RESULT_CANCELED){
                    // After Cancel code.
                }

                break;
        }
    }
}

}

DialogFragment, klasa:

public class MyDialogFragment extends DialogFragment {

public static MyDialogFragment newInstance(int num){

    MyDialogFragment dialogFragment = new MyDialogFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("num", num);
    dialogFragment.setArguments(bundle);

    return dialogFragment;

}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.ERROR)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setPositiveButton(R.string.ok_button,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, getActivity().getIntent());
                        }
                    }
            )
            .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent());
                }
            })
            .create();
}
}

100
Myślę, że kluczem tutaj jest setTargetFragmenti getTargetFragment. Użycie onActivityResultjest trochę niejasne. Prawdopodobnie lepiej byłoby zadeklarować własną specyficzną metodę w obiekcie wywołującym fragmenty i użyć jej, zamiast ponownie wybierać metodę onActivityResult. Ale w tym momencie to cała semantyka.
eternalmatt

2
zmienna poziomu stosu nie jest używana?
Nazwa wyświetlana

6
czy to przetrwa zmianę konfiguracji - rotację?
Maxrunner

3
Użyłem tego. Uwagi: poziom stosu nie był konieczny, aby przetrwać rotację lub sen. Zamiast onActivityResult, mój fragment implementuje DialogResultHandler # handleDialogResult (interfejs, który utworzyłem). @myCode, byłoby bardzo pomocne, aby pokazać wybraną w oknie dialogowym wartość dodawaną do intencji, a następnie odczytać wewnątrz onActivityResult. Intencje są niejasne dla początkujących.
Chris Betti,

7
@eternalmatt, Twój sprzeciw jest całkowicie uzasadniony, ale myślę, że wartość onActivityResult () polega na tym, że gwarantuje ona istnienie na dowolnym fragmencie, więc każdy fragment może być używany jako element nadrzędny. Jeśli utworzysz własny interfejs i zaimplementujesz go nadrzędny Fragment, wtedy dziecko może być używane tylko z rodzicami, którzy implementują ten interfejs. Podłączenie dziecka do tego interfejsu może powrócić i prześladować Cię, jeśli później zaczniesz szerzej wykorzystywać dziecko. Korzystanie z „wbudowanego” interfejsu onActivityResult () nie wymaga dodatkowego sprzężenia, więc zapewnia nieco większą elastyczność.
Dalbergia

78

Rozwiązanie TargetFragment nie wydaje się najlepszą opcją dla fragmentów okien dialogowych, ponieważ może powstać IllegalStateExceptionpo zniszczeniu i odtworzeniu aplikacji. W tym przypadku FragmentManagernie udało się znaleźć fragmentu docelowego, a otrzymasz IllegalStateExceptionwiadomość taką jak ta:

„Fragment już nie istnieje dla klucza android: target_state: index 1”

Wygląda na Fragment#setTargetFragment()to, że nie jest przeznaczony do komunikacji między dzieckiem a fragmentem rodzica, ale raczej do komunikacji między fragmentami rodzeństwa.

Tak więc alternatywnym sposobem jest tworzenie fragmentów dialogów, takich jak ten, używając ChildFragmentManagerfragmentu nadrzędnego, a nie działań FragmentManager:

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

Korzystając z interfejsu, w onCreatemetodzie DialogFragmentmożna pobrać fragment nadrzędny:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        callback = (Callback) getParentFragment();
    } catch (ClassCastException e) {
        throw new ClassCastException("Calling fragment must implement Callback interface");
    }
}

Pozostaje tylko wywołać metodę wywołania zwrotnego po wykonaniu tych czynności.

Aby uzyskać więcej informacji na temat problemu, możesz sprawdzić link: https://code.google.com/p/android/issues/detail?id=54520


2
Działa to również z onAttach (kontekst kontekstowy) wprowadzonym w api 23.
Santa Teclado

1
TO powinna być akceptowana odpowiedź. Obecnie akceptowana odpowiedź jest błędna, a fragmenty nie powinny być używane w ten sposób.
NecipAllef

3
@AhmadFadli Problem polega na tym, aby uzyskać odpowiedni kontekst (rodzica) do komunikacji między fragmentami. Jeśli zamierzasz użyć fragmentu dialogu jako dziecka aktywności, nie powinno być żadnego zamieszania. Wystarczy FragmentManager of Activity i getActivity (), aby pobrać wywołanie zwrotne.
Oguz Ozcan,

1
To powinna być akceptowana odpowiedź. Jest jasno wyjaśniony i szczegółowy, to nie tylko kod rzucany ludziom.
Vince

1
@lukecross ParentFragment to fragment, który tworzy DialogFragment (ten, który wywołuje show ()) Ale wygląda na to, że childFragmentManager nie przetrwa rekonfiguracji / rotacji ekranu ...
iwat0qs

34

Wykonałem te proste kroki, aby to zrobić.

  1. Utwórz interfejs jak w DialogFragmentCallbackInterfaceprzypadku niektórych metod, takich jak callBackMethod(Object data). Do którego dzwonisz, aby przekazać dane.
  2. Teraz możesz zaimplementować DialogFragmentCallbackInterfaceinterfejs w swoim fragmencie, npMyFragment implements DialogFragmentCallbackInterface
  3. W momencie DialogFragmenttworzenia ustaw swój wywołujący fragment MyFragmentjako fragment docelowy, który utworzył DialogFragmentużyj myDialogFragment.setTargetFragment(this, 0)check setTargetFragment (fragment fragmentu, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Pobierz docelowy obiekt fragmentu do swojego DialogFragment, wywołując go getTargetFragment()i rzucając do. DialogFragmentCallbackInterfaceTeraz możesz użyć tego interfejsu do wysłania danych do swojego fragmentu.

    DialogFragmentCallbackInterface callback = 
               (DialogFragmentCallbackInterface) getTargetFragment();
    callback.callBackMethod(Object data);

    To wszystko gotowe! po prostu upewnij się, że zaimplementowałeś ten interfejs w swoim fragmencie.


4
To powinna być najlepsza odpowiedź. Świetna odpowiedź.
Md. Sajedul Karim

Upewnij się również, że używasz tego samego menedżera fragmentów zarówno dla fragmentów źródłowych, jak i docelowych, w przeciwnym razie getTargetFragment nie będzie działać. Więc jeśli używasz childFragmentManager, nie zadziała, ponieważ fragment źródłowy nie jest zatwierdzony przez menedżera fragmentów podrzędnych. Najlepiej jest myśleć o tych 2 fragmentach jako o fragmentach rodzeństwa, a nie o fragmentach rodziców / dzieci.
Thupten

Szczerze mówiąc, lepiej jest używać tylko wzorca fragmentu docelowego podczas komunikacji między dwoma fragmentami rodzeństwa. Nie mając słuchacza, unikasz przypadkowego wycieku fragmentu1 do fragmentu2. Używając fragmentu docelowego, nie używaj listener / callback. Służy tylko onActivityResult(request code, resultcode, intent)do zwracania wyniku do fragment1. Z fragmentu1 setTargetFragment()i z fragmentu2 użyj getTargetFragment(). Używając fragmentu rodzica / dziecka do fragmentacji lub działania do fragmentacji, możesz użyć detektora lub wywołania zwrotnego, ponieważ nie ma żadnego niebezpieczeństwa wycieku rodzica we fragmencie podrzędnym.
Thupten

@Thupten, kiedy mówisz „wyciek”, masz na myśli wyciek pamięci czy „szczegóły implementacji wycieku”? Nie sądzę, aby odpowiedź Vijaya powodowała wyciek pamięci bardziej niż użycie onActivityResulty do zwracania wartości. Oba wzorce zachowają odniesienie do fragmentu docelowego. Jeśli masz na myśli wyciekające szczegóły implementacji, to myślę, że jego wzorzec jest nawet lepszy niż onActivityResult. Metoda wywołania zwrotnego jest jawna (jeśli została nazwana poprawnie). Jeśli wszystko, co otrzymujesz, jest OK i ANULOWANE, pierwszy fragment musi zinterpretować, co to oznacza.
tir38

34

Może trochę późno, ale może pomóc innym ludziom z tym samym pytaniem, co ja.

Możesz użyć setTargetFragmenton Dialogprzed pokazaniem, aw oknie dialogowym możesz zadzwonić, getTargetFragmentaby uzyskać odniesienie.


Oto odpowiedź na inne pytanie, ale dotyczy również Twojego pytania i jest czystym rozwiązaniem: stackoverflow.com/questions/28620026/ ...
user2288580

IllegalStateException to me
luke cross

19

Przewodnik dotyczący komunikacji z innymi fragmentami mówi, że fragmenty powinny komunikować się za pośrednictwem powiązanego działania .

Często chcesz, aby jeden fragment komunikował się z innym, na przykład w celu zmiany treści na podstawie zdarzenia użytkownika. Cała komunikacja między fragmentami odbywa się za pośrednictwem powiązanego działania. Dwa fragmenty nigdy nie powinny komunikować się bezpośrednio.


1
co z wewnętrznymi fragmentami, tj. jak fragment w innym fragmencie powinien komunikować się z fragmentem hosta
Ravi

@Ravi: Każdy fragment powinien komunikować się z działaniem, które jest wspólne dla wszystkich fragmentów, wywołując metodę getActivity () .
Edward Brey

1
@Chris: Jeśli fragmenty wymagają ciągłej komunikacji, zdefiniuj interfejs dla każdego odpowiedniego fragmentu do zaimplementowania. Zadanie działania ogranicza się wtedy do dostarczania fragmentów ze wskaźnikami interfejsu do ich odpowiedników. Następnie fragmenty mogą bezpiecznie komunikować się „bezpośrednio” przez interfejsy.
Edward Brey,

3
Myślę, że wraz z rozszerzeniem zastosowań fragmentów, pierwotna idea nieużywania bezpośredniej komunikacji fragmentów załamuje się. Np. W szufladzie nawigacji każdy bezpośredni podrzędny fragment działania działa z grubsza jako działanie. Tak więc posiadanie fragmentu takiego jak fragment dialogowy komunikujący się poprzez działanie szkodzi czytelności / elastyczności IMO. W rzeczywistości wydaje się, że nie ma żadnego fajnego sposobu na hermetyzację fragmentów dialogowych, aby umożliwić mu pracę zarówno z działaniami, jak i fragmentami w sposób wielokrotnego użytku.
Sam

16
Wiem, że to jest stare, ale na wypadek, gdyby ktoś tu przyszedł, wydaje mi się, że przypadek omawiany w tym dokumencie nie ma zastosowania, gdy jeden fragment „posiada” logikę używaną do określania tworzenia i zarządzania DialogFragment. Dziwne jest tworzenie wiązek połączeń z fragmentu do działania, gdy działanie nie jest nawet pewne, dlaczego tworzony jest dialog lub w jakich warunkach powinno zostać odrzucone. Poza tym DialogFragment jest bardzo prosty i istnieje tylko po to, aby powiadomić użytkownika i potencjalnie uzyskać odpowiedź.
Chris,

12

Powinieneś zdefiniować interfacew swojej klasie fragmentu i zaimplementować ten interfejs w jego aktywności nadrzędnej. Szczegóły opisano tutaj http://developer.android.com/guide/components/fragments.html#EventCallbacks . Kod wyglądałby podobnie do:

Fragment:

public static class FragmentA extends DialogFragment {

    OnArticleSelectedListener mListener;

    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
}

Czynność:

public class MyActivity extends Activity implements OnArticleSelectedListener{

    ...
    @Override
    public void onArticleSelected(Uri articleUri){

    }
    ...
}

1
Myślę, że przejrzałeś dokumenty zbyt szybko. Oba te segmenty kodu są FragmentAi zakłada, że ​​działanie jest fragmentem OnArticleSelectedListener, a nie fragmentem, który go zapoczątkował.
eternalmatt

2
Zastanowiłbym się, co próbujesz zrobić złą praktyką. Wytyczne dla Androida zalecają, aby cała komunikacja między fragmentami odbywała się poprzez działanie (na developer.android.com/training/basics/fragments/ ... ). Jeśli naprawdę chcesz, aby wszystko zostało załatwione w ciągu, MyFragmentmożesz przejść na zwykłyAlertDialog
James McCracken

1
Myślę, że problemem związanym z bezpośrednią rozmową fragmentów jest to, że w niektórych układach nie wszystkie fragmenty mogą zostać załadowane i jak pokazują w przykładzie, może być konieczne przełączenie się we fragmencie. Nie sądzę, aby ta obawa była uzasadniona, gdy mówię o uruchomieniu fragmentu dialogu z fragmentu.

Mam to zaimplementowane do moich działań. Pytanie: czy to rozwiązanie można rozszerzyć tak, aby fragment mógł wywołać wystąpienie tego okna dialogowego?
Bill Mote

1
Jest to dobra praktyka z architektonicznego punktu widzenia i jako taka powinna być akceptowaną odpowiedzią. Korzystanie z onActivityResult prowadzi do architektury spaghetti
Bruno Carrier,

4

Prawidłowym sposobem przypisania słuchacza do fragmentu jest ustawienie go w momencie dołączenia . Problem polegał na tym, że onAttachFragment () nigdy nie został wywołany. Po pewnym dochodzeniu zdałem sobie sprawę, że używałem getFragmentManager zamiast getChildFragmentManager

Oto jak to robię:

MyDialogFragment dialogFragment = MyDialogFragment.newInstance("title", "body");
dialogFragment.show(getChildFragmentManager(), "SOME_DIALOG");

Dołącz go w onAttachFragment:

@Override
public void onAttachFragment(Fragment childFragment) {
    super.onAttachFragment(childFragment);

    if (childFragment instanceof MyDialogFragment) {
        MyDialogFragment dialog = (MyDialogFragment) childFragment;
        dialog.setListener(new MyDialogFragment.Listener() {
            @Override
            public void buttonClicked() {

            }
        });
    }
}

3

Według oficjalnej dokumentacji:

Fragment # setTargetFragment

Opcjonalny cel dla tego fragmentu. Można to wykorzystać na przykład, jeśli ten fragment jest uruchamiany przez inny, a po zakończeniu chce zwrócić wynik do pierwszego. Ustawiony tutaj cel jest zachowywany między instancjami za pośrednictwem FragmentManager # putFragment.

Fragment # getTargetFragment

Zwróć fragment docelowy ustawiony przez setTargetFragment (Fragment, int).

Więc możesz to zrobić:

// In your fragment

public class MyFragment extends Fragment implements OnClickListener {
    private void showDialog() {
        DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
        // Add this
        dialogFrag.setTargetFragment(this, 0);
        dialogFrag.show(getFragmentManager, null);
    }
    ...
}

// then

public class MyialogFragment extends DialogFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Then get it
        Fragment fragment = getTargetFragment();
        if (fragment instanceof OnClickListener) {
            listener = (OnClickListener) fragment;
        } else {
            throw new RuntimeException("you must implement OnClickListener");
        }
    }
    ...
}

czy możesz wyjaśnić swoje?
Yilmaz

W tym przypadku musimy przekazać odniesienie „MyFragment” do „MyialogFragment”, a „Fragment” zapewnia metodę, aby to zrobić. Dodałem opis oficjalnego dokumentu, powinien mówić jaśniej niż ja.
SUPERYAO

2

Miałem podobny problem. Rozwiązanie, które znalazłem to:

  1. Zadeklaruj interfejs w swoim DialogFragment, tak jak wyjaśnił James McCracken powyżej.

  2. Zaimplementuj interfejs w swoim działaniu (nie fragment! To nie jest dobra praktyka).

  3. Z metody wywołania zwrotnego w twoim działaniu, wywołaj wymaganą funkcję publiczną w swoim fragmencie, która wykonuje zadanie, które chcesz wykonać.

W ten sposób staje się procesem dwuetapowym: DialogFragment -> Aktywność, a następnie Aktywność -> Fragment


1

Otrzymuję wynik do Fragment DashboardLiveWall (wywołujący fragment) z Fragment LiveWallFilterFragment (odbierający fragment) W ten sposób ...

 LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,"");

 getActivity().getSupportFragmentManager().beginTransaction(). 
 add(R.id.frame_container, filterFragment).addToBackStack("").commit();

gdzie

public static LiveWallFilterFragment newInstance(Fragment targetFragment,String anyDummyData) {
        LiveWallFilterFragment fragment = new LiveWallFilterFragment();
        Bundle args = new Bundle();
        args.putString("dummyKey",anyDummyData);
        fragment.setArguments(args);

        if(targetFragment != null)
            fragment.setTargetFragment(targetFragment, KeyConst.LIVE_WALL_FILTER_RESULT);
        return fragment;
    }

setResult z powrotem do wywołania fragmentu takiego jak

private void setResult(boolean flag) {
        if (getTargetFragment() != null) {
            Bundle bundle = new Bundle();
            bundle.putBoolean("isWorkDone", flag);
            Intent mIntent = new Intent();
            mIntent.putExtras(bundle);
            getTargetFragment().onActivityResult(getTargetRequestCode(),
                    Activity.RESULT_OK, mIntent);
        }
    }

onActivityResult

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == KeyConst.LIVE_WALL_FILTER_RESULT) {

                Bundle bundle = data.getExtras();
                if (bundle != null) {

                    boolean isReset = bundle.getBoolean("isWorkDone");
                    if (isReset) {

                    } else {
                    }
                }
            }
        }
    }

1

Zaktualizowano:

Stworzyłem bibliotekę opartą na moim głównym kodzie, który generuje te odlewania za pomocą @CallbackFragmenti @Callback.

https://github.com/zeroarst/callbackfragment .

A przykład daje przykład, który wysyła wywołanie zwrotne z fragmentu do innego fragmentu.

Stara odpowiedź:

Zrobiłem BaseCallbackFragmenti adnotację @FragmentCallback. Obecnie się rozszerza Fragment, możesz go zmienić naDialogFragment i będzie działać. Sprawdza implementacje w następującej kolejności: getTargetFragment ()> getParentFragment ()> kontekst (aktywność).

Następnie wystarczy go rozszerzyć i zadeklarować interfejsy w swoim fragmencie i nadać mu adnotację, a fragment podstawowy zrobi resztę. Adnotacja zawiera również parametr mandatoryumożliwiający określenie, czy chcesz wymusić na fragmencie zaimplementowanie wywołania zwrotnego.

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener {
        void onEcho(EchoFragment fragment, String echo);
    }
}

https://gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93


1

Chłopaki Kotlin, zaczynamy!

Więc problem polega na tym, że stworzyliśmy działanie, MainActivityw ramach tego działania utworzyliśmy fragment, FragmentAa teraz chcemy utworzyć fragment okna dialogowego, FragmentAaby go nazwać FragmentB. Jak uzyskać wyniki z FragmentBpowrotem do FragmentAbez przechodzenia przezMainActivity ?

Uwaga:

  1. FragmentAjest fragmentem podrzędnym MainActivity. Do zarządzania utworzonymi w programie fragmentami FragmentAużyjemy tego, childFragmentManagerktóry to robi!
  2. FragmentAjest fragmentem nadrzędnym FragmentB, do którego dostępu FragmentAod wewnątrz FragmentBużyjemy parenFragment.

Mimo, że w środku FragmentA,

class FragmentA : Fragment(), UpdateNameListener {
    override fun onSave(name: String) {
        toast("Running save with $name")
    }

    // call this function somewhere in a clickListener perhaps
    private fun startUpdateNameDialog() {
        FragmentB().show(childFragmentManager, "started name dialog")
    }
}

Oto fragment okna dialogowego FragmentB.

class FragmentB : DialogFragment() {

    private lateinit var listener: UpdateNameListener

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            listener = parentFragment as UpdateNameListener
        } catch (e: ClassCastException) {
            throw ClassCastException("$context must implement UpdateNameListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            val binding = UpdateNameDialogFragmentBinding.inflate(LayoutInflater.from(context))
            binding.btnSave.setOnClickListener {
                val name = binding.name.text.toString()
                listener.onSave(name)
                dismiss()
            }
            builder.setView(binding.root)
            return builder.create()
        } ?: throw IllegalStateException("Activity can not be null")
    }
}

Oto interfejs łączący oba.

interface UpdateNameListener {
    fun onSave(name: String)
}

Otóż ​​to.


1
Śledziłem ten dokument: developer.android.com/guide/topics/ui/dialogs i nie zadziałał. Dziękuję bardzo. Mam nadzieję, że ten fragment z rodzicami za każdym razem działa zgodnie z oczekiwaniami :)
UmutTekin

1
nie zapomnij ustawić słuchacza na null wewnątrz onDetach :)
BekaBot

@BekaBot Dzięki za komentarz. Zrobiłem rozeznanie i okazuje się, że nie ma potrzeby zamykania słuchaczy. stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

Rozwiązałem to w elegancki sposób z RxAndroid. Odbierz obserwatora w konstruktorze DialogFragment i zasubskrybuj obserwowalne i wypchnij wartość, gdy wywoływane jest wywołanie zwrotne. Następnie w swoim fragmencie utwórz wewnętrzną klasę Observer, utwórz instancję i przekaż ją do konstruktora DialogFragment. Użyłem WeakReference w obserwatorze, aby uniknąć wycieków pamięci. Oto kod:

BaseDialogFragment.java

import java.lang.ref.WeakReference;

import io.reactivex.Observer;

public class BaseDialogFragment<O> extends DialogFragment {

    protected WeakReference<Observer<O>> observerRef;

    protected BaseDialogFragment(Observer<O> observer) {
        this.observerRef = new WeakReference<>(observer);
   }

    protected Observer<O> getObserver() {
    return observerRef.get();
    }
}

DatePickerFragment.java

public class DatePickerFragment extends BaseDialogFragment<Integer>
    implements DatePickerDialog.OnDateSetListener {


public DatePickerFragment(Observer<Integer> observer) {
    super(observer);
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // Use the current date as the default date in the picker
    final Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    int day = c.get(Calendar.DAY_OF_MONTH);

    // Create a new instance of DatePickerDialog and return it
    return new DatePickerDialog(getActivity(), this, year, month, day);
}

@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        if (getObserver() != null) {
            Observable.just(month).subscribe(getObserver());
        }
    }
}

MyFragment.java

//Show the dialog fragment when the button is clicked
@OnClick(R.id.btn_date)
void onDateClick() {
    DialogFragment newFragment = new DatePickerFragment(new OnDateSelectedObserver());
    newFragment.show(getFragmentManager(), "datePicker");
}
 //Observer inner class
 private class OnDateSelectedObserver implements Observer<Integer> {

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(Integer integer) {
       //Here you invoke the logic

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
}

Możesz zobaczyć kod źródłowy tutaj: https://github.com/andresuarezz26/carpoolingapp


1
Zabawne w Androidzie jest coś, co nazywa się cyklem życia. Podstawowy fragment lub fragment okna dialogowego musi być w stanie zachować stan (i ich połączenie) przez zdarzenia cyklu życia. Nie można serializować wywołań zwrotnych ani obserwatorów, dlatego występuje tutaj ten sam problem.
GDanger
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.