So erhalten Sie vorhandene Fragmente bei Verwendung von FragmentPagerAdapter

99

Ich habe Probleme damit, dass meine Fragmente über die Activity, die die verwendet FragmentPagerAdapter, als Hilfsklasse miteinander kommunizieren , die die Verwaltung von Registerkarten und alle Details zum Verbinden von a ViewPagermit zugeordneten implementiert TabHost. Ich habe FragmentPagerAdaptergenau so implementiert, wie es vom Android-Beispielprojekt Support4Demos bereitgestellt wird .

Die Hauptfrage ist, wie ich ein bestimmtes Fragment erhalten kann, FragmentManagerwenn ich weder ID noch Tag habe. FragmentPagerAdaptererstellt die Fragmente und generiert automatisch die ID und Tags.

Ismar Slomic
quelle
@ jk2K wie kann dies ein Duplikat einer Frage sein, die 1 Jahr später gestellt wurde
Davi
@Dawit Duplikat wie ein Etikett, nicht zeitbezogen, spätere Fragen haben höhere Ansichten
jk2K
Die meisten Antworten hier funktionieren nicht in der Produktion. Überprüfen Sie daher meine Antwort unter stackoverflow.com/a/54280113/2413303
EpicPandaForce

Antworten:

193

Zusammenfassung des Problems

Hinweis: In dieser Antwort werde ich auf FragmentPagerAdapterden Quellcode verweisen . Die allgemeine Lösung sollte aber auch für gelten FragmentStatePagerAdapter.

Wenn Sie dies lesen Sie wahrscheinlich bereits wissen , dass FragmentPagerAdapter/ FragmentStatePagerAdaptersollte schaffen Fragmentsfür Ihre ViewPager, sondern auf Aktivität Erholung (ob von einem Gerät Rotation oder dem System zu töten Ihre App wieder Speicher) diese Fragmentswerden nicht wieder angelegt, sondern ihre Instanzen aus demFragmentManager . Sagen Sie nun, dass Sie Activityeinen Verweis auf diese Fragmentsbenötigen, um daran arbeiten zu können. Sie haben kein idoder tagfür diese erstellt, Fragmentsweil Sie FragmentPagerAdapter sie intern festlegen . Das Problem ist also, wie man ohne diese Informationen einen Verweis auf sie erhält ...

Problem mit aktuellen Lösungen: Verlassen Sie sich auf internen Code

Viele der Lösungen, die ich für diese und ähnliche Fragen gesehen habe, basieren darauf, einen Verweis auf das Vorhandene zu erhalten Fragment, FragmentManager.findFragmentByTag()indem das intern erstellte Tag"android:switcher:" + viewId + ":" + id aufgerufen und nachgeahmt wird : . Das Problem dabei ist, dass Sie sich auf internen Quellcode verlassen, der bekanntlich nicht für immer gleich bleibt. Die Android-Ingenieure bei Google könnten leicht entscheiden, die tagStruktur zu ändern, die Ihren Code beschädigen würde, sodass Sie keinen Verweis auf den vorhandenen finden können Fragments.

Alternative Lösung ohne interne tag

Hier ist ein einfaches Beispiel dafür, wie Sie einen Verweis auf das Fragmentszurückgegebene erhalten FragmentPagerAdapter, das nicht von der internen tagsMenge auf dem abhängt Fragments. Der Schlüssel besteht darin instantiateItem(), Referenzen dort zu überschreiben und zu speichern, anstatt in 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();
        }
    }
}

oder wenn Sie arbeiten lieber mit tagsstatt Klasse Membervariablen / Verweis auf die FragmentsSie kann auch den greifen tagsSatz von FragmentPagerAdapterauf die gleiche Weise: HINWEIS: dies gilt nicht , FragmentStatePagerAdapterda es nicht gesetzt ist , tagswenn seine Erstellung 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;
}

Beachten Sie, dass diese Methode NICHT auf der Nachahmung des internen tagSatzes durch beruht FragmentPagerAdapterund stattdessen geeignete APIs zum Abrufen verwendet. Auf diese Weise sind Sie auch dann sicher, wenn Sie tagÄnderungen in zukünftigen Versionen von vornehmen SupportLibrary.


Vergessen Sie nicht, dass das Design Activity, an dem FragmentsSie arbeiten möchten , je nach Design möglicherweise noch vorhanden ist oder nicht. Sie müssen dies berücksichtigen, indem Sie nullÜberprüfungen durchführen, bevor Sie Ihre Referenzen verwenden.

Wenn Sie stattdessen mit arbeiten FragmentStatePagerAdapter, möchten Sie keine harten Verweise auf Ihre behalten, Fragmentsda Sie möglicherweise viele davon haben und harte Verweise sie unnötigerweise im Speicher behalten würden. Speichern Sie stattdessen die FragmentReferenzen in WeakReferenceVariablen anstelle von Standardreferenzen. So was:

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
quelle
3
Dies ist eine wirklich gute Lösung, aber sie scheint ihre Wirksamkeit zu verlieren, wenn Sie nicht wissen, wie viele Fragmente übergeben werden.
Riot Goes Woof
3
@Zorpix Sie können die Erstellungsfragmente in einer HashMap speichern: map.put (position, createdFragment);
Tom Brinkkemper
12
Dies verdient das Häkchen! Sehr clevere und umfassende Möglichkeit, dies zu erreichen. Du hast mir sehr geholfen, danke!
young_souvlaki
2
Anfangs sah diese Lösung zu kompliziert aus, deshalb habe ich sie übersprungen. Ich bin jedoch endlich darauf zurückgekommen, weil die anderen Antworten nicht zufriedenstellend waren. Und es war nicht so schwer wie ich dachte.
Suragch
1
Sie müssen nichts überschreiben und sollten eigentlich nicht überschreiben instantiateItem. der richtige Weg , dies zu tun ist, rief instantiateItem in onCreateMethode Ihrer Aktivität von umgeben startUpdateund finishUpdate. Siehe meine Antwort für Details
Morgwai
82

Ich habe eine Antwort auf meine Frage gefunden, die auf dem folgenden Beitrag basiert: Wiederverwenden von Fragmenten in einem Fragmentpageradapter

Einige Dinge, die ich gelernt habe:

  1. getItem(int position)in der FragmentPagerAdapterist ziemlich irreführend Name dessen, was diese Methode tatsächlich tut. Es werden neue Fragmente erstellt und keine vorhandenen zurückgegeben. In diesem Sinne sollte die Methode in etwas wie createItem(int position)im Android SDK umbenannt werden. Diese Methode hilft uns also nicht, Fragmente zu erhalten.
  2. Basierend auf der Erläuterung in der Post- Unterstützung FragmentPagerAdapterholds Verweis auf alte Fragmente sollten Sie die Erstellung der Fragmente dem überlassen FragmentPagerAdapterund in diesem Sinne keinen Verweis auf die Fragmente oder deren Tags haben. Wenn Sie jedoch ein Fragment-Tag haben, können Sie den Verweis darauf einfach FragmentManagerdurch Aufrufen abrufen findFragmentByTag(). Wir brauchen eine Möglichkeit, das Tag eines Fragments an einer bestimmten Seitenposition herauszufinden.

Lösung

Fügen Sie Ihrer Klasse die folgende Hilfsmethode hinzu, um das Fragment-Tag abzurufen und an die findFragmentByTag()Methode zu senden .

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

HINWEIS! Dies ist die identische Methode, FragmentPagerAdapterdie beim Erstellen neuer Fragmente verwendet wird. Siehe diesen Link http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104

Ismar Slomic
quelle
Übrigens gibt es mehr über das Thema in diesem Q & A: stackoverflow.com/questions/6976027/…
Thomas
1
Was ist der Eingabeparameter viewId? Welche Ansicht?
Nilzor
@Nilzor viewId ist die ID des ViewPager.
Dr.jacky
Vielleicht könnte das Fragment, anstatt das Tag zu erraten, sein Tag seiner Aktivität in mitteilen onAttach()?
Becken
4
Dies ist nicht der richtige Weg. @ Tony Chans Antwort ist der beste und richtige Weg.
Morteza Rastgoo
16

Sie müssen instantiateItemdie Kompatibilität mit internen makeFragmentNameMethoden nicht überschreiben oder sich darauf verlassen, dass Sie Fragment-Tags manuell erstellen.
instantiateItemist eine öffentliche Methode, daher können und sollten Sie sie in der onCreateMethode Ihrer Aktivität aufrufen , die von Aufrufen startUpdateund finishUpdateMethoden umgeben ist, wie in PagerAdapter javadoc beschrieben :

Ein Aufruf der PagerAdapter-Methode startUpdate (ViewGroup) zeigt an, dass sich der Inhalt des ViewPager ändern wird. Ein oder mehrere Aufrufe von instantiateItem (ViewGroup, int) und / oder destroyItem (ViewGroup, int, Object) folgen, und das Ende einer Aktualisierung wird durch einen Aufruf von finishUpdate (ViewGroup) signalisiert.

Auf diese Weise können Sie bei Bedarf Verweise auf Instanzen Ihrer Fragmente auf lokalen Variablen speichern. Siehe Beispiel:

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
        }
    }
}

instantiateItemIch werde zuerst versuchen, Verweise auf vorhandene Fragmentinstanzen von abzurufen FragmentManager. Nur wenn sie noch nicht existieren, werden neue mit erstelltgetItem Methode Ihres Adapters und FragmentManagerfür die zukünftige Verwendung in der "gespeichert" .

Es ist wichtig zu beachten, dass Sie auch dann, wenn Sie keine Verweise auf Ihre Fragmente benötigen, instantiateItemalle von startUpdate/ finishUpdatein Ihrer onCreateMethode umgebenen Registerkarten wie folgt aufrufen sollten :

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

Wenn Sie dies nicht tun, dann riskieren Sie , dass Ihre Fragment Instanzen werden niemals begangen zu FragmentManager: wenn Ihre Aktivität wird Vordergrund instantiateItemautomatisch Ihre Fragmente aufgerufen werden , um zu erhalten, aber startUpdate/ finishUpdate kann nicht (je nach Implementierungsdetails) und was sie im Grunde do is begin / commit a FragmentTransaction.
Dies kann dazu führen, dass Verweise auf die erstellten Fragmentinstanzen sehr schnell verloren gehen (z. B. wenn Sie Ihren Bildschirm drehen) und viel häufiger als erforderlich neu erstellt werden. Je nachdem, wie "schwer" Ihre Fragmente sind, kann dies nicht zu vernachlässigende Auswirkungen auf die Leistung haben.
Darüber hinaus können in solchen Fällen Fälle von Fragmenten, die auf lokalen Variablen gespeichert sind, auftretenveraltet werden: Wenn die Android-Plattform versucht, sie aus zu erhalten,FragmentManager Aus irgendeinem Grund wird es fehlschlagen und somit neue erstellen und verwenden, während Ihre Vars weiterhin auf die alten verweisen.

Morgwai
quelle
1
In einigen Fällen ist dies möglicherweise die beste Lösung. Aber was passiert, wenn FragmentManger das Fragment tötet und neu bewertet?
Woltran
1
@woltran FragmentManagerkann deine nicht einfach zufällig töten ( zerstören ist hier das richtige Wort) Fragment(denke darüber nach, was passieren würde, wenn es sich entscheiden würde, eine zu töten Fragment, die gerade angezeigt wird;)). Im Allgemeinen ist der Lebenszyklus von a Fragmentan seinen gebunden Activity(siehe github.com/xxv/android-lifecycle für Details) -> a Fragmentkann nur zerstört werden, wenn sein Activityzerstört wurde. In einem solchen Fall , wenn hinten ein Benutzer navigiert zu dem gegebenen Activityseine onCreatewieder aufgerufen werden und eine neue Instanz der Fragmenterstellt wird.
Morgwai
Dies ist die echte Antwort
MJ Studio
Sollten Sie wirklich Fragmente erstellen, anstatt sich darauf zu verlassen, dass sie erstellt werden, wenn ein Benutzer beispielsweise ViewPager scrollt?
Yar
@Yar ja, das solltest du wirklich. In dem von mir bereitgestellten Dokumentauszug wird dies deutlich angegeben, und im Abschnitt "Einige zusätzliche Informationen" wird erläutert, warum.
Morgwai
11

So wie ich es gemacht habe, definiere ich eine Hashtabelle mit schwachen Referenzen wie folgt:

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

Dann habe ich die Methode getItem () folgendermaßen geschrieben:

@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;
}

Dann können Sie eine Methode schreiben:

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

Das scheint gut zu funktionieren und ich finde es etwas weniger hackig als das

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

Trick, da es nicht darauf ankommt, wie der FragmentPagerAdapter implementiert ist. Wenn das Fragment vom FragmentPagerAdapter freigegeben wurde oder noch nicht erstellt wurde, gibt getFragment natürlich null zurück.

Wenn jemand etwas falsch mit diesem Ansatz findet, sind Kommentare mehr als willkommen.

personne3000
quelle
int fragmentIdsollte umbenannt werden inint position
lmaooooo
7
Ich habe einen sehr ähnlichen Ansatz gewählt. Dies schlägt jedoch fehl, wenn der Pager aus einem savedState-Bundle erstellt wird. Beispiel: Die Aktivität tritt in den Hintergrund und tritt nach dem Aufruf von onSavedStateInstance () wieder in den Vordergrund. In diesem Fall werden die Methoden getItem () nicht aufgerufen.
Anoop
Was ist der Grund für das Erstellen einer eigenen Karte, da es in FragmentManager bereits eine gibt, die immer auf dem neuesten Stand ist? Siehe meine Antwort für Details.
Morgwai
Die Tatsache, dass ein Fragment zerstört wurde, garantiert nicht, dass es keine starken Verweise darauf gibt (obwohl dies wahrscheinlich, aber NICHT garantiert ist). In diesem Fall würde Ihre Karte immer noch veraltete Fragmente enthalten.
Morgwai
1
Dies funktioniert nicht richtig, nachdem das System die Fragmente neu erstellt hat.
EpicPandaForce
10

Ich habe diese Methode erstellt, die für mich funktioniert, um einen Verweis auf das aktuelle Fragment zu erhalten.

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
quelle
Schön, nur daran zu denken, dass die Methode und das Feld außerhalb der Methode erstellt wurden, um eine bessere Leistung zu erzielen
Marcos Vasconcelos,
2

Die von @ personne3000 vorgeschlagene Lösung ist nett, hat aber ein Problem: Wenn die Aktivität in den Hintergrund tritt und vom System beendet wird (um freien Speicher zu erhalten) und dann wiederhergestellt wird, ist die Lösung fragmentReferencesleer, weilgetItem dies nicht der Fall ist namens.

Die folgende Klasse behandelt eine solche Situation:

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
quelle
1

Das Hauptproblem beim Erhalten eines Handles für die Fragmente ist, dass Sie sich nicht auf getItem () verlassen können. Nach einer Orientierungsänderung sind Verweise auf die Fragmente null und getItem () wird nicht erneut aufgerufen.

Hier ist ein Ansatz, der nicht auf der Implementierung von FragmentPagerAdapter beruht, um das Tag abzurufen. Überschreiben Sie instantiateItem (), um das aus getItem () erstellte oder vom Fragmentmanager gefundene Fragment zurückzugeben.

@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
quelle
0

Weitere Informationen zum Zurückgeben von Fragmenten aus dem FragmentPagerAdapter finden Sie in diesem Beitrag . Verlässt sich darauf, dass Sie den Index Ihres Fragments kennen - dies wird jedoch in getItem () festgelegt (nur bei Instanziierung).

Gemeinschaft
quelle
0

Ich habe es geschafft, dieses Problem zu lösen, indem ich IDs anstelle von Tags verwendet habe. (Ich verwende FragmentStatePagerAdapter definiert, der meine benutzerdefinierten Fragmente verwendet, in denen ich die onAttach-Methode überschrieben habe, in der Sie die ID irgendwo speichern:

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

Und dann greifen Sie einfach innerhalb der Aktivität einfach auf das Fragment zu:

Fragment f = getSupportFragmentManager.findFragmentById(fragId);
user2740905
quelle
0

Ich weiß nicht, ob dies der beste Ansatz ist, aber nichts anderes hat für mich funktioniert. Alle anderen Optionen, einschließlich getActiveFragment, gaben null zurück oder führten zum Absturz der App.

Ich habe festgestellt, dass bei der Bildschirmdrehung das Fragment angehängt wurde, sodass ich es verwendet habe, um das Fragment an die Aktivität zurückzusenden.

Im Fragment:

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

Dann in der Aktivität:

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

Und schließlich in Aktivität onCreate ():

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

Bei diesem Ansatz wird das tatsächlich sichtbare Fragment an die Aktivität angehängt, ohne dass ein neues erstellt wird.

TacoEater
quelle
0

Ich bin mir nicht sicher, ob meine Methode der richtige oder beste Weg war, da ich ein relativer Anfänger mit Java / Android bin, aber sie hat funktioniert (ich bin sicher, dass sie gegen objektorientierte Prinzipien verstößt, aber für meinen Anwendungsfall hat keine andere Lösung funktioniert).

Ich hatte eine Hosting-Aktivität, die einen ViewPager mit einem FragmentStatePagerAdapter verwendete. Um Verweise auf die Fragmente zu erhalten, die von FragmentStatePagerAdapter erstellt wurden, habe ich eine Rückrufschnittstelle innerhalb der Fragmentklasse erstellt:

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

In der Hosting-Aktivität habe ich die Schnittstelle implementiert und ein LinkedHasSet erstellt, um die Fragmente zu verfolgen:

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

Innerhalb der ViewPagerFragment-Klasse habe ich die Fragmente der Liste in onAttach hinzugefügt und sie in onDetach entfernt:

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

Innerhalb der Hosting-Aktivität können Sie jetzt mFragments verwenden, um die Fragmente zu durchlaufen, die derzeit im FragmentStatePagerAdapter vorhanden sind.

sbearben
quelle
0

Diese Klasse macht den Trick, ohne sich auf interne Tags zu verlassen. Warnung: Auf Fragmente sollte mit der Methode getFragment und nicht mit der Methode getItem zugegriffen werden.

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
quelle
-5

Probieren Sie einfach diesen Code aus.

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
quelle
Najib, wie ich in meiner Antwort unten erklärt habe, erstellt getItem () ein neues Fragment, anstatt vorhandene zurückzugeben, wie man erwarten könnte, dass der Vorname get und nicht erstellt wird . Siehe meine Lösung im selben Beitrag.
Ismar Slomic
Fragment fragment = new YourCustomFragmentClass (); Schreiben Sie hier überprüfen Sie dies.
Najib Ahmed Puthawala
Ich verstehe immer noch nicht, wie dies die Tatsache ändert, dass Sie ein neues Fragment erstellen,
anstatt ein
Sie initialisieren und kehren weiterhin nur für Ihr benutzerdefiniertes Fragment zurück, z. B. als Fragment fragment = new YourFragment (); Fragment zurückgeben;
Najib Ahmed Puthawala