Obecnie mam fragment w nakładce. Służy do logowania się do usługi. W aplikacji na telefon każdy krok, który chcę pokazać w nakładce, to własne ekrany i czynności. Istnieją 3 części procesu logowania, a każda z nich miała własne działanie, które zostało wywołane za pomocą metody startActivityForResult ().
Teraz chcę zrobić to samo, używając fragmentów i nakładki. Nakładka pokaże fragment odpowiadający każdej czynności. Problem polega na tym, że te fragmenty są hostowane w działaniu w Honeycomb API. Mogę uruchomić pierwszy fragment, ale potem muszę uruchomićActivityForResult (), co nie jest możliwe. Czy jest coś podobnego do startFragmentForResult (), w którym mogę wykopać nowy fragment i czy po zakończeniu zwrócić wynik do poprzedniego fragmentu?
źródło
onActivityResult
jeśli chcesz.Jeśli chcesz, istnieje kilka metod komunikacji między fragmentami,
setTargetFragment(Fragment fragment, int requestCode) getTargetFragment() getTargetRequestCode()
Możesz oddzwonić za pomocą tych.
Fragment invoker = getTargetFragment(); if(invoker != null) { invoker.callPublicMethod(); }
źródło
setTargetFragment()
.setTargetFragment
jest obecnie przestarzałe. ZobaczsetResultListener
w stackoverflow.com/a/61881149/2914140 tutaj.Możemy po prostu udostępniać ten sam ViewModel między fragmentami
SharedViewModel
import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.ViewModel class SharedViewModel : ViewModel() { val stringData: MutableLiveData<String> by lazy { MutableLiveData<String>() } }
FirstFragment
import android.arch.lifecycle.Observer import android.os.Bundle import android.arch.lifecycle.ViewModelProviders import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup class FirstFragment : Fragment() { private lateinit var sharedViewModel: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) activity?.run { sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_first, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedViewModel.stringData.observe(this, Observer { dateString -> // get the changed String }) } }
SecondFragment
import android.arch.lifecycle.ViewModelProviders import android.os.Bundle import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGrou class SecondFragment : Fragment() { private lateinit var sharedViewModel: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) activity?.run { sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_first, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) changeString() } private fun changeString() { sharedViewModel.stringData.value = "Test" } }
źródło
Niedawno Google właśnie dodał nową możliwość, dzięki
FragmentManager
którejFragmentManager
może działać jako centralny magazyn wyników fragmentów. Możemy łatwo przekazywać dane między fragmentami.Początkowy fragment.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Use the Kotlin extension in the fragment-ktx artifact setResultListener("requestKey") { key, bundle -> // We use a String here, but any type that can be put in a Bundle is supported val result = bundle.getString("bundleKey") // Do something with the result... } }
Fragment, dla którego chcemy odzyskać wynik.
button.setOnClickListener { val result = "result" // Use the Kotlin extension in the fragment-ktx artifact setResult("requestKey", bundleOf("bundleKey" to result)) }
Fragment pochodzi z oficjalnych dokumentów Google. https://developer.android.com/training/basics/fragments/pass-data-between#kotlin
Na podstawie danych tej odpowiedzi funkcja ta jest nadal
alpha
aktualna. Możesz to wypróbować, korzystając z tej zależności.androidx.fragment:fragment:1.3.0-alpha05
źródło
Moje 2 centy.
Przełączam się między fragmentami, zamieniając stary fragment na nowy za pomocą funkcji ukryj i pokaż / dodaj (istniejący / nowy). Więc ta odpowiedź jest dla deweloperów, którzy używają fragmentów tak jak ja.
Następnie używam tej
onHiddenChanged
metody, aby wiedzieć, że stary fragment został przełączony z powrotem na nowy. Zobacz kod poniżej.Przed opuszczeniem nowego fragmentu ustawiłem wynik w globalnym parametrze, o który będzie pytał stary fragment. To bardzo naiwne rozwiązanie.
@Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); if (hidden) return; Result result = Result.getAndReset(); if (result == Result.Refresh) { refresh(); } } public enum Result { Refresh; private static Result RESULT; public static void set(Result result) { if (RESULT == Refresh) { // Refresh already requested - no point in setting anything else; return; } RESULT = result; } public static Result getAndReset() { Result result = RESULT; RESULT = null; return result; } }
źródło
getAndReset()
metoda?onResume()
wywoływany na pierwszym fragmencie, gdy drugi jest odrzucany?W swoim fragmencie możesz wywołać metodę getActivity (). To da ci dostęp do aktywności, która utworzyła fragment. Stamtąd możesz wywołać metodę dostosowywania, aby ustawić wartości lub przekazać wartości.
źródło
Istnieje biblioteka Androida - FlowR, która umożliwia uruchamianie fragmentów wyników.
Rozpoczynanie fragmentu dla wyniku.
Flowr.open(RequestFragment.class) .displayFragmentForResults(getFragmentId(), REQUEST_CODE);
Obsługa prowadzi do fragmentu wywołującego.
@Override protected void onFragmentResults(int requestCode, int resultCode, Bundle data) { super.onFragmentResults(requestCode, resultCode, data); if (requestCode == REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { demoTextView.setText("Result OK"); } else { demoTextView.setText("Result CANCELED"); } } }
Ustawienie wyniku w pliku Fragment.
Flowr.closeWithResults(getResultsResponse(resultCode, resultData));
źródło
Rozwiązanie wykorzystujące interfejsy (i Kotlin). Podstawową ideą jest zdefiniowanie interfejsu wywołania zwrotnego, zaimplementowanie go w swoim działaniu, a następnie wywołanie go z fragmentu.
Najpierw utwórz interfejs
ActionHandler
:interface ActionHandler { fun handleAction(actionCode: String, result: Int) }
Następnie zadzwoń do tego od swojego dziecka (w tym przypadku twojego fragmentu):
companion object { const val FRAGMENT_A_CLOSED = "com.example.fragment_a_closed" } fun closeFragment() { try { (activity as ActionHandler).handleAction(FRAGMENT_A_CLOSED, 1234) } catch (e: ClassCastException) { Timber.e("Calling activity can't get callback!") } dismiss() }
Na koniec zaimplementuj to u swojego rodzica, aby otrzymać oddzwonienie (w tym przypadku Twoja aktywność):
class MainActivity: ActionHandler { override fun handleAction(actionCode: String, result: Int) { when { actionCode == FragmentA.FRAGMENT_A_CLOSED -> { doSomething(result) } actionCode == FragmentB.FRAGMENT_B_CLOSED -> { doSomethingElse(result) } actionCode == FragmentC.FRAGMENT_C_CLOSED -> { doAnotherThing(result) } } }
źródło
Najłatwiejszym sposobem przekazania danych z powrotem jest użycie metody setArgument (). Na przykład masz fragment1, który wywołuje fragment2, który wywołuje fragment3, fragment1 -> framgnet2 -> fargment3
We fragmencie 1
public void navigateToFragment2() { if (fragmentManager == null) return; Fragment2 fragment = Fragment2.newInstance(); String tag = "Fragment 2 here"; fragmentManager.beginTransaction() .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) .add(R.id.flContent, fragment, tag) .addToBackStack(null) .commitAllowingStateLoss(); }
We fragmencie 2 nazywamy fragment3 jak zwykle
private void navigateToFragment3() { if (fragmentManager == null) return; Fragment3 fragment = new Fragment3(); fragmentManager.beginTransaction() .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) .replace(R.id.flContent, fragment, tag) .addToBackStack(null) .commit(); }
Kiedy zakończyliśmy nasze zadanie we fragmencie3, teraz nazywamy to:
FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); if (fragmentManager == null) return; fragmentManager.popBackStack(); Bundle bundle = new Bundle(); bundle.putString("bundle_filter", "data"); fragmentManager.findFragmentByTag("Fragment 2 here").setArguments(bundle);
Teraz we fragmencie2 możemy łatwo wywołać argumenty
@Override public void onResume() { super.onResume(); Bundle rgs = getArguments(); if (args != null) String data = rgs.getString("bundle_filter"); }
źródło
Inną rzeczą, którą możesz zrobić w zależności od architektury, jest użycie udostępnionego ViewModel między fragmentami. Więc w moim przypadku FragmentA jest formularzem, a FragmentB jest widokiem wyboru elementu, w którym użytkownik może wyszukać i wybrać element, przechowując go w ViewModel. Kiedy wrócę do Fragmentu, informacje są już zapisane!
źródło