it-swarm.com.de

Rufen Sie ein Fragment von einem ViewPager ab

Ich verwende eine ViewPager zusammen mit einer FragmentStatePagerAdapter, um drei verschiedene Fragmente zu hosten:

  • [Fragment1]
  • [Fragment2]
  • [Fragment3]

Wenn ich Fragment1 von der ViewPager in der FragmentActivity bekommen möchte. 

Was ist das Problem und wie kann ich es beheben?

227
Qun Qin

Die Hauptantwort basiert auf einem Namen, der vom Framework generiert wird. Wenn sich das jemals ändert, wird es nicht mehr funktionieren.

Was ist mit dieser Lösung, die instantiateItem() und destroyItem() Ihrer Fragment(State)PagerAdapter überschreibt:

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

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

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

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(...); 
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

Dies scheint für mich zu funktionieren, wenn es um Fragmente geht, die verfügbar sind. Fragmente, die noch nicht instanziiert wurden, geben beim Aufruf von getRegisteredFragment null zurück. Aber ich habe dies hauptsächlich verwendet, um die aktuelle Fragment aus der ViewPager: adapater.getRegisteredFragment(viewPager.getCurrentItem()) zu holen, und dies wird null nicht zurückgegeben.

Ich kenne keine anderen Nachteile dieser Lösung. Wenn es welche gibt, würde ich gerne wissen.

492

Um Fragmente aus einem ViewPager herauszuholen, gibt es eine Menge Antworten hier und in anderen verwandten SO - Threads/Blogs. Jeder, den ich gesehen habe, ist gebrochen, und sie scheinen im Allgemeinen in eine der zwei unten aufgeführten Arten zu fallen. Es gibt einige andere gültige Lösungen, wenn Sie nur das aktuelle Fragment greifen möchten, wie this andere Antwort in diesem Thread.

Wenn Sie FragmentPagerAdapter verwenden, siehe unten. Wenn Sie FragmentStatePagerAdapter verwenden, sollten Sie this betrachten. Das Erfassen von Indizes, die nicht die aktuellen Indizes in einem FragmentStateAdapter sind, ist aufgrund der Art von Wenn diese vollständig abgerissen werden, sind sie außerhalb der Sichtweite von offScreenLimit-Grenzen.

DIE UNGLÜCKLICHEN WEGE

Falsch: Pflegen Sie Ihre eigene interne Liste von Fragmenten, die hinzugefügt wird, wenn FragmentPagerAdapter.getItem() aufgerufen wird.

  • In der Regel eine SparseArray oder Map
  • Keines der vielen Beispiele, die ich gesehen habe, berichtet über Lebenszyklusereignisse. Daher ist diese Lösung fragil. Da getItem nur aufgerufen wird, wenn eine Seite zum ersten Mal in der ViewPager gescrollt wird (oder erhalten wird, wenn Ihre ViewPager.setOffscreenPageLimit(x)> 0 ist), wenn die Hosting-ActivityFragment BEENDET ODER NEU GESTARTET WIRD, WIRD DIE INTERNE SpaseArray GEL&OUML;SCHT, WENN DIE BENUTZERDEFINIERTE FRAGMENTPAGERACTIVITY WIEDERHERGESTELLT WIRD , ABER HINTER DEN KULISSEN WERDEN DIE INTERNEN VIEWPAGERS-FRAGMENTE NEU ERSTELLT, UND getItem WIRD _/_ NICHT für einen der Indizes aufgerufen, sodass die Möglichkeit, ein Fragment aus dem Index zu erhalten, für immer verloren geht. Sie können dies berücksichtigen, indem Sie diese Fragmentverweise über FragmentManager.getFragment() und putFragment ausspeichern und wiederherstellen. Dies beginnt jedoch, IMHO unordentlich zu machen.

Falsch: Konstruieren Sie Ihre eigene Tag-ID, die der in FragmentPagerAdapter unter der Kapuze verwendeten Verwendung entspricht, und verwenden Sie diese Option, um die Seite Fragmente von FragmentManager abzurufen.

  • Dies ist insofern besser, als es mit dem Problem des Verlierens von Fragmenten in der ersten internen Array-Lösung zurechtkommt, aber wie in den Antworten oben und anderswo im Netz zu Recht darauf hingewiesen wird - fühlt es sich als private -Methode an intern zu ViewPager, das sich jederzeit oder für jede Betriebssystemversion ändern kann.

Die Methode, die für diese Lösung neu erstellt wird, ist 

private static String makeFragmentName(int viewId, long id) {
    return "Android:switcher:" + viewId + ":" + id;
}

EIN GLÜCKLICHER WEG: ViewPager.instantiateItem()

Ein ähnlicher Ansatz zu getItem() oben, aber nicht lebenszyklusbrechend besteht darin, in instantiateItem() anstelle von getItem() einzuhaken, da die erstere jedes Mal aufgerufen wird, wenn der Index erstellt/aufgerufen wird. Siehe diese Antwort

EIN GLÜCKLICHER WEG: Konstruiere deine eigene FragmentViewPager

Erstellen Sie Ihre eigene FragmentViewPager-Klasse aus der source der neuesten Unterstützung lib und ändern Sie die intern verwendete Methode, um die Fragment-Tags zu generieren. Sie können es durch das folgende ersetzen. Dies hat den Vorteil, dass Sie wissen, dass die Tag-Erstellung niemals geändert wird und Sie sich nicht auf eine private API/Methode verlassen, was immer gefährlich ist.

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "Android:switcher:" + containerViewId + ":" + id;
}

Wenn Sie ein Fragment für einen Index verwenden möchten, rufen Sie einfach etwas wie diese Methode auf (die Sie in die benutzerdefinierte Variable FragmentPagerAdapter oder eine Unterklasse einfügen können). Wenn Sie sich dessen bewusst sind, ist das Ergebnis möglicherweise null, wenn getItem dies hat Für diese Seite wurde noch nicht aufgerufen, dh sie wurde noch nicht erstellt.

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link Android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

Dies ist eine einfache Lösung und löst die Probleme der beiden anderen Lösungen, die überall im Web zu finden sind

83
Dori

Fügen Sie Ihrem FragmentPagerAdapter die folgenden Methoden hinzu:

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

private static String makeFragmentName(int viewId, int index) {
    return "Android:switcher:" + viewId + ":" + index;
}

getActiveFragment (0) muss funktionieren. 

Hier ist die Lösung, die in ViewPager https://Gist.github.com/jacek-marchwicki/d6320ba9a910c514424d implementiert ist. Wenn etwas fehlschlägt, wird ein gutes Absturzprotokoll angezeigt. 

70
kzotin

Eine weitere einfache Lösung:

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }
38
Mindphaser

Ich weiß, das hat ein paar Antworten, aber vielleicht hilft das jemandem. Ich habe eine relativ einfache Lösung verwendet, als ich ein Fragment von meinem ViewPager erhalten musste. In Ihrem Activity oder Fragment, das ViewPager enthält, können Sie mit diesem Code alle Fragment durchlaufen, die es enthält.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • Wenn Sie die Position Ihres Fragment im ViewPager kennen, können Sie einfach getItem(knownPosition) aufrufen.

  • Wenn Sie die Position Ihres Fragment in ViewPager nicht kennen, können Sie Ihre Kinder Fragments eine Schnittstelle mit einer Methode wie getUniqueId() implementieren lassen und diese verwenden differenzieren sie. Oder Sie können alle Fragments durchlaufen und den Klassentyp überprüfen, z. B. if(viewPagerFragment instanceof FragmentClassYouWant)

!!! EDIT !!!

Ich habe festgestellt, dass getItem nur dann von einem FragmentPagerAdapter aufgerufen wird, wenn jedes Fragment zum ersten Mal erstellt werden muss , danach scheint es, dass die Fragments mit dem FragmentManager recycelt werden. Auf diese Weise erstellen viele Implementierungen von FragmentPagerAdapter neue Fragments in getItem. Unter Verwendung der obigen Methode bedeutet dies, dass wir jedes Mal, wenn Fragment aufgerufen wird, neue getItem erstellen, wenn wir alle Elemente in FragmentPagerAdapter durchlaufen. Aus diesem Grund habe ich einen besseren Ansatz gefunden, indem ich FragmentManager verwende, um stattdessen Fragment zu erhalten (unter Verwendung der akzeptierten Antwort). Dies ist eine umfassendere Lösung, die für mich gut funktioniert hat.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

Und Sie werden diese Methode brauchen.

private static String makeFragmentName(int viewId, int position) {
    return "Android:switcher:" + viewId + ":" + position;
}
21
Steven Byle

In meinem Fall hat keine der oben genannten Lösungen funktioniert.

Da ich jedoch den Child Fragment Manager in einem Fragment verwende, wurde Folgendes verwendet:

Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());

Dies funktioniert nur, wenn Ihre Fragmente im Manager dem Viewpager-Element entsprechen.

10
Veedka

Um aktuelles sichtbares Fragment von ViewPager zu erhalten. Ich verwende diese einfache Aussage und es funktioniert gut.

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }
6
Xar E Ahmer

Dies basiert auf der obigen Antwort von Steven. Dies gibt die tatsächliche Instanz des Fragments zurück, das bereits an die übergeordnete Aktivität angehängt ist. 

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }
3
OrangeTree

Hey, ich habe diese Frage hier beantwortet. Grundsätzlich müssen Sie sie überschreiben 

public Object instantiateItem (ViewGroup-Container, int-Position)

methode von FragmentStatePagerAdapter.

2
Utkarsh Saxena

Sie müssen don't später getItem() oder eine andere Methode aufrufen, um die Referenz einer Fragment zu erhalten, die in ViewPager gehostet ist. Wenn Sie einige Daten in Fragment aktualisieren möchten, gehen Sie folgendermaßen vor: Aktualisieren Sie ViewPager dynamisch.

Der Schlüssel ist, neue Daten in Adaper zu setzen und notifyDataSetChanged() aufzurufen, die wiederum getItemPosition() aufrufen wird, eine Referenz Ihrer Fragment übergeben wird und Ihnen die Möglichkeit gibt, diese zu aktualisieren. Bei allen anderen Methoden müssen Sie auf sich selbst oder einen anderen Hack verweisen, was keine gute Lösung ist.

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}
2
M-WaJeEh

Ich behandelte es, indem ich zunächst eine Liste aller Fragmente (List<Fragment> fragments;) erstellte, die ich verwenden wollte, und sie dann dem Pager hinzufügte, um die Handhabung des aktuell angezeigten Fragments zu erleichtern.

So:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

so kann man das nennen:

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

dann könnte ich es in eine if-Anweisung stecken, die nur dann laufen würde, wenn es auf dem richtigen Fragment wäre

Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

aber das ist nur mein hack and slash-ansatz, aber es hat für mich funktioniert. Ich verwende es, wenn mein rückseitiger button gedrückt wird, um relevante Änderungen an meinem aktuell angezeigten Fragment vorzunehmen.

2
Russell Cargill

Ich konnte keinen einfachen, sauberen Weg finden, dies zu tun. Das ViewPager-Widget ist jedoch nur eine andere ViewGroup, die Ihre Fragmente hostet. Der ViewPager hat diese Fragmente als unmittelbare Kinder. Sie könnten also einfach über sie iterieren (mithilfe von .getChildCount () und .getChildAt ()) und nachsehen, ob die von Ihnen gesuchte Fragmentinstanz aktuell in den ViewPager geladen ist und einen Verweis darauf erhalten. Z.B. Sie können ein statisches eindeutiges ID-Feld verwenden, um die Fragmente voneinander zu unterscheiden.

Beachten Sie, dass der ViewPager das gesuchte Fragment möglicherweise nicht geladen hat, da es sich um einen virtualisierenden Container wie ListView handelt.

Muss FragmentPagerAdapter in Ihre ViewPager-Adapterklasse einbinden.
Wenn Sie FragmentStatePagerAdapter verwenden, können Sie Ihre Fragment nicht anhand ihrer ID finden.

public static String makeFragmentName(int viewPagerId, int index) {
  return "Android:switcher:" + viewPagerId + ":" + index;
}

So verwenden Sie diese Methode: -

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
2
i_m_mahii

FragmentPagerAdapter ist die Factory der Fragmente. Um ein Fragment anhand seiner Position zu finden, verwenden Sie Folgendes:

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "Android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

Beispielcode für v4-Support-API.

2
Daniel De León

Die beste Lösung ist die Erweiterung, die wir unter CodePath genannt SmartFragmentStatePagerAdapter erstellt haben. Diesem Leitfaden folgend, wird das Abrufen von Fragmenten und des aktuell ausgewählten Fragments aus einem ViewPager erheblich vereinfacht. Außerdem wird der Speicher der in den Adapter eingebetteten Fragmente besser verwaltet.

1
Nathan

Ich habe dies einfach mit einem etwas anderen Ansatz umgesetzt.

Meine benutzerdefinierte FragmentAdapter.getItem-Methode hat nicht neues MyFragment () zurückgegeben, sondern die Instanz von MyFragment, die im FragmentAdapter-Konstruktor erstellt wurde. 

In meiner Aktivität habe ich dann das Fragment vom Adapter erhalten, prüfen, ob es instanziiert wird, Fragment benötigt, dann die erforderlichen Methoden anwenden und verwenden.

0
lxknvlk

in TabLayout gibt es mehrere Registerkarten für Fragment. Sie können das Fragment über Tag anhand des Index des Fragments finden.

Für ex. Der Index für Fragment1 ist 0, also übergeben Sie in der findFragmentByTag()-Methode das Tag für Viewpager.

String tag = "Android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);

0
SUMIT MONAPARA

Der einfachste und prägnanteste Weg. Wenn alle Ihre Fragmente in ViewPager von verschiedenen Klassen sind, können Sie sie abrufen und wie folgt unterscheiden:

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}
0
riwnodennyk

In Fragment

public int getArgument(){
   return mPage;
{
public void update(){

}

In FragmentActivity

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}
0
Mark Zhuravlyov

Erstellen Sie eine Integer-Ressourcen-ID in /values/integers.xml

<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

Dann in der PagerAdapter-Funktion getItem:

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

Dann schreiben Sie in Aktivität diese Funktion, um die Fragmentreferenz zu erhalten:

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

Rufen Sie die Fragmentreferenz ab, indem Sie die obige Funktion aufrufen und sie in Ihr benutzerdefiniertes Fragment umwandeln:

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }
0
user2574090

Einfache Möglichkeit, Fragmente im Fragmentmanager zu durchlaufen. Suchen Sie den Viewpager mit dem Argument für die Abschnittsposition in public static PlaceholderFragment newInstance (int sectionNumber) .

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}
0
Evgeny