Jak uzyskać istniejące fragmenty przy użyciu FragmentPagerAdapter

99

Mam problem z tym, aby moje fragmenty komunikowały się ze sobą za pomocą Activity, która używa FragmentPagerAdapterklasy pomocniczej, która implementuje zarządzanie zakładkami i wszystkimi szczegółami łączenia się ViewPagerz powiązanymi TabHost. Zaimplementowałem FragmentPagerAdapterdokładnie to samo, co zapewnia przykładowy projekt Android Support4Demos .

Główne pytanie brzmi: jak mogę uzyskać określony fragment, FragmentManagerskoro nie mam ani identyfikatora, ani tagu? FragmentPagerAdaptertworzy fragmenty i automatycznie generuje identyfikator i tagi.

Ismar Slomic
źródło
@ jk2K jak to może być duplikatem pytania zadanego rok później
Davi,
@Dawit zduplikowane jak etykieta, niezwiązane z czasem, późniejsze pytania mają większą
liczbę
Większość odpowiedzi tutaj nie działa w środowisku produkcyjnym, więc sprawdź moją odpowiedź na stackoverflow.com/a/54280113/2413303
EpicPandaForce

Odpowiedzi:

194

Podsumowanie problemu

Uwaga: w tej odpowiedzi zamierzam odwołać się do FragmentPagerAdapterkodu źródłowego. Ale ogólne rozwiązanie powinno również dotyczyć FragmentStatePagerAdapter.

Jeśli to czytasz, prawdopodobnie już wiesz, że FragmentPagerAdapter/ FragmentStatePagerAdapterma tworzyć Fragmentsdla Ciebie ViewPager, ale po odtworzeniu aktywności (czy to z obrotu urządzenia, czy systemu zabijającego aplikację w celu odzyskania pamięci) Fragments, nie zostaną one ponownie utworzone, ale zamiast tego ich wystąpienia pobrane zFragmentManager . Teraz powiedz, że Activitypotrzebujesz odniesienia do nich, Fragmentsaby nad nimi pracować. Nie masz idani tagdla tych utworzonych, Fragmentsponieważ FragmentPagerAdapter ustawisz je wewnętrznie . Problem polega więc na tym, jak uzyskać do nich odniesienie bez tych informacji ...

Problem z obecnymi rozwiązaniami: poleganie na kodzie wewnętrznym

Wiele rozwiązań widziałem na tej i podobnych pytań polegać na coraz odniesienie do istniejącego Fragmentdzwoniąc FragmentManager.findFragmentByTag()i imitując utworzony wewnętrznie tag:"android:switcher:" + viewId + ":" + id . Problem polega na tym, że polegasz na wewnętrznym kodzie źródłowym, który, jak wszyscy wiemy, nie gwarantuje, że pozostanie taki sam na zawsze. Inżynierowie Androida w Google mogliby z łatwością zdecydować o zmianie tagstruktury, która zepsułaby Twój kod, uniemożliwiając znalezienie odniesienia do istniejącego Fragments.

Alternatywne rozwiązanie bez polegania na wewnętrznym tag

Oto prosty przykład, jak uzyskać odwołanie do Fragmentszwróconego przez FragmentPagerAdapterto elementu , który nie opiera się na wewnętrznym tagszestawie Fragments. Kluczem jest nadpisanie instantiateItem()i zapisanie odwołań tam zamiast w getItem().

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

        public CustomPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

lub jeśli wolisz pracować ze tagszmiennymi składowymi / odwołaniami do klasy zamiast ze zmiennymi składowymi Fragments, możesz również pobrać tagszestaw FragmentPagerAdapterw ten sam sposób: UWAGA: nie dotyczy to, FragmentStatePagerAdapterponieważ nie jest ustawiane tagspodczas tworzenia Fragments.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

Zauważ, że ta metoda NIE polega na naśladowaniu wewnętrznego tagzestawu przez FragmentPagerAdapteri zamiast tego używa odpowiednich interfejsów API do ich pobierania. W ten sposób, nawet jeśli tagzmiany w przyszłych wersjach SupportLibrarynadal będą bezpieczne.


Nie zapominaj, że w zależności od projektu Activity, nad Fragmentsktórym próbujesz pracować, może jeszcze istnieć lub nie, więc musisz to uwzględnić, nullsprawdzając przed użyciem referencji.

Ponadto, jeśli zamiast tego pracujesz FragmentStatePagerAdapter, nie chcesz przechowywać twardych odniesień do swoich, Fragmentsponieważ możesz ich mieć wiele, a twarde odniesienia niepotrzebnie zatrzymałyby je w pamięci. Zamiast tego zapisuj Fragmentodwołania w WeakReferencezmiennych zamiast standardowych. Lubię to:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}
Tony Chan
źródło
3
To naprawdę dobre rozwiązanie, ale wydaje się, że traci na skuteczności, jeśli nie wiesz, ile fragmentów zostanie przekazanych.
Riot Goes Woof
3
@Zorpix możesz przechowywać utworzone fragmenty w HashMap: map.put (position, createdFragment);
Tom Bevelander,
12
To zasługuje na zaznaczenie! Bardzo sprytny i wszechstronny sposób na osiągnięcie tego. Bardzo mi pomogłeś, dziękuję!
young_souvlaki
2
Na początku to rozwiązanie wyglądało na zbyt skomplikowane, więc je pominąłem. W końcu jednak do tego wróciłem, ponieważ inne odpowiedzi nie były satysfakcjonujące. I nie było tak trudne, jak myślałem.
Suragch
1
nie ma potrzeby nadpisywania czegokolwiek i właściwie nie powinno się nadpisywać instantiateItem. właściwy sposób to zrobić, aby zadzwonić instantiateItem w onCreatesposobie swoją działalność w otoczeniu startUpdatei finishUpdate. Zobacz moją odpowiedź po szczegóły
morgwai
82

Znalazłem odpowiedź na swoje pytanie w oparciu o następujący post: ponowne wykorzystanie fragmentów w fragmentpageradapter

Kilka rzeczy, których się nauczyłem:

  1. getItem(int position)w FragmentPagerAdapterto raczej myląca nazwa tego, co ta metoda faktycznie robi. Tworzy nowe fragmenty, nie zwracając istniejących. W związku z tym należy zmienić nazwę metody na podobną createItem(int position)do zestawu Android SDK. Więc ta metoda nie pomaga nam w zdobywaniu fragmentów.
  2. Opierając się na wyjaśnieniu w poście, wsparcie FragmentPagerAdapterholds odwołuje się do starych fragmentów , powinieneś pozostawić tworzenie fragmentów do, FragmentPagerAdapterco oznacza, że ​​nie masz odniesienia do fragmentów ani ich tagów. Jeśli jednak masz znacznik fragmentu, możesz łatwo pobrać do niego odniesienie z FragmentManagerpliku, wywołując findFragmentByTag(). Potrzebujemy sposobu, aby znaleźć tag fragmentu w danej pozycji na stronie.

Rozwiązanie

Dodaj następującą metodę pomocnika w swojej klasie, aby pobrać znacznik fragmentu i wysłać go do findFragmentByTag()metody.

private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}

UWAGA! Jest to identyczna metoda, której FragmentPagerAdapterużywa się przy tworzeniu nowych fragmentów. Zobacz ten link http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104

Ismar Slomic
źródło
Przy okazji, więcej na ten temat można znaleźć w tym pytaniu i odpowiedziach: stackoverflow.com/questions/6976027/…
Thomas
1
jaki jest parametr wejściowy viewId? Który widok?
Nilzor,
@Nilzor viewId to identyfikator ViewPager.
Dr Jacky
Może zamiast odgadnąć tag, fragment mógłby określić jego aktywność w tagu onAttach()?
dorzecze
4
To nie jest właściwa droga. Odpowiedź @Tony'ego Chana jest najlepsza i poprawna.
Morteza Rastgoo
17

nie musisz nadpisywać instantiateItemani polegać na zgodności z wewnętrzną makeFragmentNamemetodą poprzez ręczne tworzenie znaczników fragmentów.
instantiateItemjest metodą publiczną , więc możesz i faktycznie powinieneś ją wywoływać w onCreatemetodzie swojej aktywności otoczonej wywołaniami startUpdatei finishUpdatemetodami, jak opisano wPagerAdapter javadoc :

Wywołanie metody PagerAdapter startUpdate (ViewGroup) wskazuje, że wkrótce nastąpi zmiana zawartości ViewPager. Co najmniej jedno wywołanie instantiateItem (ViewGroup, int) i / lub destruItem (ViewGroup, int, Object) nastąpi, a koniec aktualizacji zostanie zasygnalizowany przez wywołanie finishUpdate (ViewGroup).

W ten sposób, przy okazji, możesz przechowywać odniesienia do instancji swoich fragmentów na lokalnych zmiennych, jeśli potrzebujesz. Zobacz przykład:

public class MyActivity extends AppCompatActivity {

    Fragment0 tab0; Fragment1 tab1;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        tab0 = (Fragment0) adapter.instantiateItem(viewPager, 0);
        tab1 = (Fragment1) adapter.instantiateItem(viewPager, 1);
        adapter.finishUpdate(viewPager);
    }

    class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

        @Override public int getCount() {return 2;}

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
    }
}

instantiateItemnajpierw spróbuje pobrać odniesienia do istniejących instancji fragmentów z pliku FragmentManager. Tylko jeśli jeszcze nie istnieją, utworzy nowe za pomocągetItem metody z twojego adaptera i " " je w FragmentManagercelu wykorzystania w przyszłości.

Ważne jest, aby pamiętać, że nawet jeśli nie musisz uzyskiwać referencji do swoich fragmentów, nadal powinieneś wywoływać instantiateItemwszystkie swoje karty otoczone startUpdate/ finishUpdatew swojej onCreatemetodzie w następujący sposób:

    adapter.startUpdate(viewPager);
    // ignoring return values of the below 2 calls, just side effects matter:
    adapter.instantiateItem(viewPager, 0);
    adapter.instantiateItem(viewPager, 1);
    adapter.finishUpdate(viewPager);

Jeśli nie zrobić tak, to ryzykujesz, że instancje fragment nigdy nie będzie zobowiązana do FragmentManager: gdy czynność staje się pierwszym planie instantiateItembędzie wywoływana automatycznie, aby uzyskać swoje fragmenty, ale startUpdate/ finishUpdate może nie (w zależności od szczegółów implementacji) i co w zasadzie do to rozpocząć / zatwierdzić FragmentTransaction.
Może to spowodować, że odniesienia do utworzonych instancji fragmentów zostaną bardzo szybko utracone (na przykład podczas obrócenia ekranu) i odtworzone znacznie częściej niż to konieczne. W zależności od tego, jak „ciężkie” są Twoje fragmenty, może to mieć niebagatelne konsekwencje dla wydajności. Ponadto w takich przypadkach fragmenty są przechowywane na lokalnych varach z jakiegokolwiek powodu się nie powieść, a tym samym utworzy i użyje nowych, podczas gdy twoje vary nadal będą odnosić się do starych.
mogą wystąpićstarzeją się: jeśli platforma Android spróbuje je zFragmentManager

morgwai
źródło
1
W niektórych przypadkach może to być najlepsze rozwiązanie. Ale co się stanie, jeśli FragmentManger zabije fragment i ponownie go przeliczy?
woltran
1
@woltran FragmentManagernie może po prostu losowo zabić ( zniszczyć to właściwe słowo tutaj) twojego Fragment(pomyśl, co by się stało, gdyby zdecydował się zabić Fragmentaktualnie wyświetlany;)). Zasadniczo cykl życia a Fragmentjest powiązany z jego Activity(szczegóły na github.com/xxv/android-lifecycle ) -> a Fragmentmożna zniszczyć tylko wtedy, gdy Activityzostało zniszczone. W takim przypadku, gdy do nawigacji użytkownika z powrotem na dany Activityjej onCreatezostanie wywołana ponownie i nowa instancja Fragmentzostanie utworzona.
morgwai
To jest PRAWDZIWA odpowiedź
MJ Studio
Czy naprawdę powinieneś tworzyć fragmenty zamiast polegać na ich tworzeniu, na przykład, gdy użytkownik przewija ViewPager?
Yar
@Yar tak, naprawdę powinieneś. Podany przeze mnie fragment dokumentacji jasno to określa, a sekcja „Dodatkowe informacje” wyjaśnia dlaczego.
morgwai
11

Sposób, w jaki to zrobiłem, polega na zdefiniowaniu tablicy haszowania słabych referencji w następujący sposób:

protected Hashtable<Integer, WeakReference<Fragment>> fragmentReferences;

Następnie napisałem metodę getItem () w następujący sposób:

@Override
public Fragment getItem(int position) {

    Fragment fragment;
    switch(position) {
    case 0:
        fragment = new MyFirstFragmentClass();
        break;

    default:
        fragment = new MyOtherFragmentClass();
        break;
    }

    fragmentReferences.put(position, new WeakReference<Fragment>(fragment));

    return fragment;
}

Następnie możesz napisać metodę:

public Fragment getFragment(int fragmentId) {
    WeakReference<Fragment> ref = fragmentReferences.get(fragmentId);
    return ref == null ? null : ref.get();
}

Wydaje się, że działa dobrze i uważam, że jest trochę mniej skomplikowany niż

"android:switcher:" + viewId + ":" + position

sztuczka, ponieważ nie polega na sposobie implementacji FragmentPagerAdapter. Oczywiście, jeśli fragment został wydany przez FragmentPagerAdapter lub nie został jeszcze utworzony, getFragment zwróci wartość null.

Jeśli ktoś zauważy coś złego w tym podejściu, komentarze są mile widziane.

personne3000
źródło
int fragmentIdpowinien zostać zmieniony naint position
lmaooooo
7
Stosowałem bardzo podobne podejście. Ale kończy się to niepowodzeniem, gdy pager jest tworzony z pakietu saveState. np .: Aktywność przechodzi w tło i wraca na pierwszy plan po wywołaniu onSavedStateInstance (). W takim przypadku metody getItem () nie zostaną wywołane.
Anoop
Jaki jest powód tworzenia własnej mapy, skoro w FragmentManager jest już taka, która jest zawsze up2date? Zobacz moją odpowiedź po szczegóły.
morgwai,
również fakt, że fragment został zniszczony, nie gwarantuje, że nie ma do niego silnych odniesień (chociaż jest to prawdopodobne, ale NIE gwarantowane), w takim przypadku mapa nadal będzie zawierała przestarzałe fragmenty.
morgwai
1
Nie będzie to działać poprawnie po odtworzeniu fragmentów przez system.
EpicPandaForce
10

Stworzyłem tę metodę, która działa dla mnie, aby uzyskać odniesienie do bieżącego fragmentu.

public static Fragment getCurrentFragment(ViewPager pager, FragmentPagerAdapter adapter) {
    try {
        Method m = adapter.getClass().getSuperclass().getDeclaredMethod("makeFragmentName", int.class, long.class);
        Field f = adapter.getClass().getSuperclass().getDeclaredField("mFragmentManager");
        f.setAccessible(true);
        FragmentManager fm = (FragmentManager) f.get(adapter);
        m.setAccessible(true);
        String tag = null;
        tag = (String) m.invoke(null, pager.getId(), (long) pager.getCurrentItem());
        return fm.findFragmentByTag(tag);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } 
    return null;
}
Pepijn
źródło
Miło tylko pamiętać o utworzeniu metody i pola poza metodą dla lepszej wydajności
Marcos Vasconcelos
2

rozwiązanie zasugerowane przez @ personne3000 jest fajne, ale ma jeden problem: gdy aktywność przechodzi w tło i zostaje zabita przez system (aby uzyskać wolną pamięć), a następnie przywrócona, fragmentReferencesbędzie pusta, ponieważgetItem nie byłoby nazywa.

Poniższa klasa radzi sobie z taką sytuacją:

public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {

    public static final String FRAGMENT_SAVE_PREFIX = "holder";
    private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.

    public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentManager = fm;
    }

    private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();

    protected void holdFragment(F fragment) {
        holdFragment(holder.size(), fragment);
    }

    protected void holdFragment(int position, F fragment) {
        if (fragment != null)
            holder.put(position, new WeakReference<F>(fragment));
    }

    public F getHoldedItem(int position) {
        WeakReference<F> ref = holder.get(position);
        return ref == null ? null : ref.get();
    }

    public int getHolderCount() {
        return holder.size();
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
        super.restoreState(state, loader);
        Bundle bundle = (Bundle) state;
        for (String key : bundle.keySet()) {
            if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
                int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
                Fragment f = fragmentManager.getFragment(bundle, key);
                holdFragment(index, (F) f);
            }
        }
    }

    @Override
    public Parcelable saveState() {
        Bundle state = (Bundle) super.saveState();
        if (state == null)
            state = new Bundle();

        for (int i = 0; i < holder.size(); i++) {
            int id = holder.keyAt(i);
            final F f = getHoldedItem(i);
            String key = FRAGMENT_SAVE_PREFIX + id;
            fragmentManager.putFragment(state, key, f);
        }
        return state;
    }
}
netimen
źródło
1

Główną przeszkodą w uzyskaniu uchwytu do fragmentów jest to, że nie można polegać na getItem (). Po zmianie orientacji odwołania do fragmentów będą miały wartość null i funkcja getItem () nie będzie ponownie wywoływana.

Oto podejście, które nie polega na implementacji FragmentPagerAdapter w celu pobrania tagu. Zastąp instancję instantiateItem (), która zwróci fragment utworzony przez getItem () lub znaleziony w menedżerze fragmentów.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object value =  super.instantiateItem(container, position);

    if (position == 0) {
        someFragment = (SomeFragment) value;
    } else if (position == 1) {
        anotherFragment = (AnotherFragment) value;
    }

    return value;
}
Adam
źródło
0

Zobacz ten wpis dotyczący zwracania fragmentów z FragmentPagerAdapter. Czy polega na tym, że znasz indeks swojego fragmentu - ale zostanie to ustawione w getItem () (tylko podczas tworzenia instancji)

Społeczność
źródło
0

Udało mi się rozwiązać ten problem, używając identyfikatorów zamiast tagów. (Używam zdefiniowanego FragmentStatePagerAdapter, który używa moich niestandardowych fragmentów, w których nadpisuję metodę onAttach, gdzie gdzieś zapisujesz identyfikator:

@Override
public void onAttach(Context context){
    super.onAttach(context);
    MainActivity.fragId = getId();
}

A potem po prostu uzyskujesz łatwy dostęp do fragmentu w działaniu:

Fragment f = getSupportFragmentManager.findFragmentById(fragId);
user2740905
źródło
0

Nie wiem, czy to najlepsze podejście, ale nic innego nie zadziałało. Wszystkie inne opcje, w tym getActiveFragment, zwróciły wartość null lub spowodowały awarię aplikacji.

Zauważyłem, że podczas obracania ekranu fragment był dołączany, więc użyłem go do wysłania fragmentu z powrotem do ćwiczenia.

We fragmencie:

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

Następnie w ćwiczeniu:

@Override
public void setListFrag(MyListFragment lf) {
    if (mListFragment == null) {
        mListFragment = lf;
    }
}

I wreszcie w działaniu onCreate ():

if (savedInstanceState != null) {
    if (mListFragment != null)
        mListFragment.setListItems(items);
}

Takie podejście łączy rzeczywisty widoczny fragment z działaniem bez tworzenia nowego.

TacoEater
źródło
0

Nie jestem pewien, czy moja metoda była poprawna lub najlepsza, ponieważ jestem stosunkowo początkującym użytkownikiem Java / Android, ale zadziałała (jestem pewien, że narusza zasady obiektowe, ale żadne inne rozwiązanie nie działało w moim przypadku użycia).

Miałem działanie hostingowe, które używało ViewPager z FragmentStatePagerAdapter. Aby uzyskać odniesienia do fragmentów, które zostały utworzone przez FragmentStatePagerAdapter, stworzyłem interfejs wywołania zwrotnego w klasie fragmentu:

public interface Callbacks {
    public void addFragment (Fragment fragment);
    public void removeFragment (Fragment fragment);
}

W działalności hostingowej zaimplementowałem interfejs i stworzyłem LinkedHasSet do śledzenia fragmentów:

public class HostingActivity extends AppCompatActivity implements ViewPagerFragment.Callbacks {

    private LinkedHashSet<Fragment> mFragments = new LinkedHashSet<>();

    @Override
    public void addFragment (Fragment fragment) {
        mFragments.add(fragment);
    }

    @Override
    public void removeFragment (Fragment fragment) {
        mFragments.remove(fragment);
    }
}

W klasie ViewPagerFragment dodałem fragmenty do listy w onAttach i usunąłem je w onDetach:

public class ViewPagerFragment extends Fragment {

    private Callbacks mCallbacks;

    public interface Callbacks {
        public void addFragment (Fragment fragment);
        public void removeFragment (Fragment fragment);
    } 

    @Override
    public void onAttach (Context context) {
        super.onAttach(context);
        mCallbacks = (Callbacks) context;
        // Add this fragment to the HashSet in the hosting activity
        mCallbacks.addFragment(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // Remove this fragment from the HashSet in the hosting activity
        mCallbacks.removeFragment(this);
        mCallbacks = null;
    }
}

W ramach działania hostingu będziesz teraz mógł używać mFragments do iteracji przez fragmenty, które obecnie istnieją w FragmentStatePagerAdapter.

sbearben
źródło
0

Ta klasa radzi sobie bez polegania na tagach wewnętrznych. Ostrzeżenie: Dostęp do fragmentów należy uzyskiwać za pomocą metody getFragment, a nie metody getItem.

public class ViewPagerAdapter extends FragmentPagerAdapter {

    private final Map<Integer, Reference<Fragment>> fragments = new HashMap<>();
    private final List<Callable0<Fragment>> initializers = new ArrayList<>();
    private final List<String> titles = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    void addFragment(Callable0<Fragment> initializer, String title) {
        initializers.add(initializer);
        titles.add(title);
    }

    public Optional<Fragment> getFragment(int position) {
        return Optional.ofNullable(fragments.get(position).get());
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment =  initializers.get(position).execute();
        return fragment;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.put(position, new WeakReference<>(fragment));
        return fragment;
    }

    @Override
    public int getCount() {
        return initializers.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}
Alex Miragall
źródło
-5

Po prostu spróbuj tego kodu,

public class MYFragmentPAdp extends FragmentPagerAdapter {

    public MYFragmentPAdp(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return 2;
    }

     @Override
     public Fragment getItem(int position) {
         if (position == 0)
             Fragment fragment = new Fragment1();
         else (position == 1)
             Fragment fragment = new Fragment2();
         return fragment;
     }
}
Najib Ahmed Puthawala
źródło
Najib, jak wyjaśniłem w mojej odpowiedzi poniżej, getItem () tworzy nowy fragment zamiast zwracać istniejące, jak można by się spodziewać, że podana nazwa get and not create . Zobacz moje rozwiązanie w tym samym poście.
Ismar Slomic
Fragment fragment = new YourCustomFragmentClass (); napisz tutaj sprawdź to.
Najib Ahmed Puthawala
Nadal nie rozumiem, jak to zmienia fakt, że tworzysz nowy fragment zamiast
pobierać
Nadal inicjalizujesz i wracasz tylko do swojego niestandardowego fragmentu, na przykład jako Fragment fragment = new YourFragment (); fragment powrotu;
Najib Ahmed Puthawala