Abrufen der aktuellen Fragmentinstanz im Viewpager


210

Unten ist mein Code, in den 3 Fragment classesjeweils die drei Registerkarten eingebettet sind ViewPager. Ich habe eine Menüoption. Wie in der Abbildung gezeigt onOptionsItemSelected(), muss ich durch Auswahl einer Option das derzeit sichtbare Fragment aktualisieren. Um dies zu aktualisieren, muss ich eine Methode aufrufen, die sich in der Fragmentklasse befindet. Kann jemand bitte vorschlagen, wie man diese Methode aufruft?

public class MainActivity  extends ActionBarActivity {

     ViewPager ViewPager;
     TabsAdapter TabsAdapter;

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

            ViewPager = new ViewPager(this);
            ViewPager.setId(R.id.pager);
            setContentView(ViewPager);

            final ActionBar bar = getSupportActionBar();

            bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

            //Attaching the Tabs to the fragment classes and setting the tab title.
            TabsAdapter = new TabsAdapter(this, ViewPager);
            TabsAdapter.addTab(bar.newTab().setText("FragmentClass1"),
                    FragmentClass1.class, null);
            TabsAdapter.addTab(bar.newTab().setText("FragmentClass2"),
              FragmentClass2.class, null);
            TabsAdapter.addTab(bar.newTab().setText("FragmentClass3"),
              FragmentClass3.class, null);


            if (savedInstanceState != null) {
                bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
            }

        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {

            switch (item.getItemId()) {

            case R.id.addText:

           **// Here I need to call the method which exists in the currently visible Fragment class**

                    return true;

            }

            return super.onOptionsItemSelected(item);
        }


     @Override
     protected void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
            outState.putInt("tab", getSupportActionBar().getSelectedNavigationIndex());

     }

     public static class TabsAdapter extends FragmentPagerAdapter
      implements ActionBar.TabListener, ViewPager.OnPageChangeListener {

      private final Context mContext;
            private final ActionBar mActionBar;
            private final ViewPager mViewPager;
            private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

            static final class TabInfo {
                private final Class<?> clss;
                private final Bundle args;

                TabInfo(Class<?> _class, Bundle _args) {
                    clss = _class;
                    args = _args;
                }
            }

      public TabsAdapter(ActionBarActivity activity, ViewPager pager) {
       super(activity.getSupportFragmentManager());
                mContext = activity;
                mActionBar = activity.getSupportActionBar();
                mViewPager = pager;
                mViewPager.setAdapter(this);
                mViewPager.setOnPageChangeListener(this);
            }

      public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
                TabInfo info = new TabInfo(clss, args);
                tab.setTag(info);
                tab.setTabListener(this);
                mTabs.add(info);
                mActionBar.addTab(tab);
                notifyDataSetChanged();

            }

      @Override
      public void onPageScrollStateChanged(int state) {
       // TODO Auto-generated method stub

      }

      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
       // TODO Auto-generated method stub

      }

      @Override
      public void onPageSelected(int position) {
       // TODO Auto-generated method stub
       mActionBar.setSelectedNavigationItem(position);
      }

      @Override
      public void onTabReselected(Tab tab, FragmentTransaction ft) {
       // TODO Auto-generated method stub

      }

      @Override
      public void onTabSelected(Tab tab, FragmentTransaction ft) {
       Object tag = tab.getTag();
                for (int i=0; i<mTabs.size(); i++) {
                    if (mTabs.get(i) == tag) {
                        mViewPager.setCurrentItem(i);

                    }
                }

                tabPosition = tab.getPosition();
      }

      @Override
      public void onTabUnselected(Tab tab, FragmentTransaction ft) {
       // TODO Auto-generated method stub

      }

      @Override
      public Fragment getItem(int position) {
       TabInfo info = mTabs.get(position);
                return Fragment.instantiate(mContext, info.clss.getName(), info.args);
      }

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

     }

    }

Angenommen, unten ist die Fragmentklasse mit der Methode, die updateList()ich aufrufen möchte:

 public class FragmentClass1{

    ArrayList<String> originalData;


    @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
           Bundle savedInstanceState) {

          View fragmentView = inflater.inflate(R.layout.frag1, container, false);

          originalData = getOriginalDataFromDB();

          return fragmentView;

         }


    public void updateList(String text)
    {
       originalData.add(text);
       //Here I could do other UI part that need to added
    }
}

Welche Fragmentmethode möchten Sie aufrufen?
Biraj Zalavadia

@BirajZalavadia Meine eigene definierte Methode, die einen Parameter enthält und die Instanzvariablen dieser Fragmentklasse verwendet, die bereits beim Erstellen einer Ansicht initialisiert wurden.
Rick

1
Können Sie den Klassennamen Ihrer Fragmente angeben, die sich im Viewpager befinden?
Biraj Zalavadia


Der einfachste Weg, auf die sichtbaren Fragmente zuzugreifen , wäre über stackoverflow.com/questions/7379165/… . Schauen Sie sich die beiden wichtigsten Antworten an.
Luksprog

Antworten:


205

Durch Auswahl einer Option muss das aktuell sichtbare Fragment aktualisiert werden.

Eine einfache Möglichkeit hierfür ist die Verwendung eines Tricks im Zusammenhang mit der FragmentPagerAdapterImplementierung:

case R.id.addText:
     Fragment page = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
     // based on the current position you can then cast the page to the correct 
     // class and call the method:
     if (ViewPager.getCurrentItem() == 0 && page != null) {
          ((FragmentClass1)page).updateList("new item");     
     } 
return true;

Bitte überdenken Sie Ihre Namenskonvention für Variablen. Verwenden Sie als Variablennamen den Namen der Klasse, der sehr verwirrend ist (also nein ViewPager ViewPager, verwenden Sie ViewPager mPagerzum Beispiel).


23
Ist dieses Fragment-Tag zukunftssicher, wenn sich die API ändert?
Maarten

7
@ Maarten Nein, es ist nur ein häufiger Hack, der seit dem Erscheinen des Adapters verwendet wurde (und dessen Quellcode verfügbar war). Er hat sich noch nicht geändert. Wenn Sie etwas zuverlässigeres möchten, müssen Sie andere Optionen verwenden, insbesondere die instantiateItem()Methode überschreiben und selbst Verweise auf die richtigen Fragmente erhalten.
Luksprog

5
Nach stundenlanger Frustration und Fragen direkt von der Seite mAdapter.getItem(position), super ... Zumindest weiß ich jetzt, dass die anderen 8512-Versuche nicht funktioniert haben. Vielen Dank
Diolor

29
Beachten Sie, dass dies nicht funktioniert, wenn Sie FragmentStatePagerAdapter anstelle von FragmentPagerAdapter verwenden.
Shawn Lauzon

3
@ShawnLauzon Der FragmentStatePagerAdapter hat eine andere Implementierung. In diesem Fall rufen Sie die Methode instantiateItem () auf, um die sichtbaren Fragmente abzurufen.
Luksprog

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

28
Google sollte FragmentPageAdapter.getCurrentPrimaryItem () bereitstellen, das mCurrentPrimaryItem zurückgibt.
Eugene

1
Ich verwende diese Technik auch. Ich definiere auch ein PagerFragment mit Methoden onPageSelected/ onPageDeselectedund lasse es von allen meinen Pager-Fragmenten erweitern. Dann setPrimaryitemkönnen diese Methoden in geeigneter Weise aufrufen , wenn der Benutzer auf eine neue Seite klaut. Dies bedeutet, dass Sie möglicherweise eine faulere Strategie zum Laden von Daten auf Seiten implementieren, die nicht einmal angezeigt werden. Sie sollten jedoch berücksichtigen, dass es während des Seitenübergangs besser aussieht, wenn die nächste Seite bereits mit Daten gefüllt ist, sodass das verzögerte Laden aller Daten onPageSelectedmöglicherweise nicht ideal ist.
JHH

3
Warum die ifAussage? Fragment ruft nur die equalsMethode des Objekts auf, die ein flacher Vergleich ist. Warum also nicht einfach immer anrufen mCurrentFragment = ((Fragment) object);?
Overclover

versucht und es funktioniert gut, wirf einfach getCurrentFragment () auf das spezifische Fragment, das es sollte
PhilipJ

113

Verfolgen Sie zunächst alle "aktiven" Fragmentseiten. In diesem Fall verfolgen Sie die Fragmentseiten im FragmentStatePagerAdapter, der vom ViewPager verwendet wird.

@Override
public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferenceMap.put(index, myFragment);
    return myFragment;
}

Um zu vermeiden, dass ein Verweis auf "inaktive" Fragmentseiten beibehalten wird, müssen Sie die destroyItem (...) -Methode des FragmentStatePagerAdapter implementieren:

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

und wenn Sie auf die aktuell sichtbare Seite zugreifen müssen, rufen Sie an:

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

Wo die getFragment (int) -Methode des MyAdapters folgendermaßen aussieht:

public MyFragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}

Hoffe es kann helfen!


Können Sie das bitte klar erklären? Wo muss ich diese FragmentStatePagerAdapter-Klasse in meinem Code aufbewahren? Übrigens habe ich 3 Fragmentklassen, keine einzelne MyFragmentClass.
Rick

7
Noch besser, wenn Sie mPageReferenceMap in instantiateItem anstelle von getItem aktualisieren, da dies auch nach einer Änderung der Ausrichtung funktioniert. '@Override public Object instantiateItem (ViewGroup-Container, int-Position) {Fragment fragment = (Fragment) Super.instantiateItem (Container, Position); mPageReferenceMap.put (Position, Fragment); Fragment zurückgeben; } '
pmellaaho

Welcher Typ ist mPageReferenceMap?
DoruAdryan

10
Diese Antwort ist wirklich schlecht! Sie sollten den Cache eines ViewPagers nicht manuell behalten, da er ihn selbst verwaltet. Der ViewPager entscheidet, wann ein Fragment zugewiesen werden soll oder nicht, was angehalten werden soll und wann die Beibehaltung eines Verweises auf jedes Fragment einen unnötigen Overhead verursacht und nicht für den Speicher optimiert ist.
Livio

3
Ich habe diesen Ansatz ausprobiert, aber leider funktioniert er nicht, wenn die Aktivität neu erstellt wird. Unter Android N und höher können Sie dies sehen, wenn Sie die Multi-Window-Funktion verwenden. Wenn Ihre App geöffnet ist, halten Sie die Schaltfläche "Letzte" (die quadratische) lange gedrückt. Die Aktivität und das Fragment werden aus savedInstanceState neu erstellt, der Adapter wird neu erstellt (sodass die mPageReferenceMap jetzt leer ist), aber getItem wird nicht erneut aufgerufen. Die Karte bleibt leer, sodass adapter.getFragment (Index) abstürzt.
Martin Konicek

102

Nur so erhalte ich keine NullPointerException für die Instanzvariablen dieser bestimmten Fragmentklassen. Dies kann für andere hilfreich sein, die an der gleichen Sache festhalten. In onOptionsItemSelected () habe ich den folgenden Weg codiert:

if(viewPager.getCurrentItem() == 0) {
    FragmentClass1 frag1 = (FragmentClass1)viewPager
                            .getAdapter()
                            .instantiateItem(viewPager, viewPager.getCurrentItem());
    frag1.updateList(text); 
} else if(viewPager.getCurrentItem() == 1) {
    FragmentClass2 frag2 = (FragRecentApps)viewPager
                            .getAdapter()
                            .instantiateItem(viewPager, viewPager.getCurrentItem());
    frag2.updateList(text);
}

3
Ich denke, dies ist die richtigste Antwort - richten Sie sich nach dem Design von aus, FragmentStatePagerAdapterohne Code zu überschreiben. Tatsächlich sollte es so einfach sein wie eine Funktion, die einfach zurückkehrt viewPager.getAdapter().instantiateItem(viewPager, viewPager.getCurrentItem()).
John Pang

39

FragmentStatePagerAdapter verfügt über eine öffentliche Methode mit dem Namen instantiateItem, die Ihr Fragment basierend auf angegebenen Parameterwerten zurückgibt. Diese Methode verfügt über zwei Parameter ViewGroup (ViewPager) und position.

public Object instantiateItem(ViewGroup container, int position);

Verwendete diese Methode, um das Fragment der angegebenen Position abzurufen.

Fragment fragment = (Fragment) adaper.instantiateItem(mViewPager, position);

5
Dies gibt jedoch nicht das aktuelle bereits instanziierte Fragment zurück, sondern ein neues, nein? Also Name "instanziieren" ...
Ixx

1
@Ixx Nein, es wird kein neues Fragment erstellt. Ich denke, der Methodenname ist hier falsch.
Bugs passieren am

15

Ich weiß, dass es zu spät ist, aber ich habe wirklich einfache Möglichkeiten, es zu tun,

// für Fragment bei 0 Besitz

((mFragment) viewPager.getAdapter().instantiateItem(viewPager, 0)).yourMethod();

10

Hier gibt es viele Antworten, die nicht wirklich die grundlegende Tatsache ansprechen, dass es wirklich KEINE MÖGLICHKEIT gibt, dies vorhersehbar zu tun, und auf eine Weise, die nicht dazu führt, dass Sie sich irgendwann in der Zukunft in den Fuß schießen.

FragmentStatePagerAdapter ist die einzige Klasse, die weiß, wie sie zuverlässig auf die Fragmente zugreifen kann, die von der verfolgt werden FragmentManager - Jeder Versuch, die ID oder das Tag des Fragments zu erraten, ist langfristig nicht zuverlässig. Und Versuche, die Instanzen manuell zu verfolgen, funktionieren wahrscheinlich nicht gut, wenn der Status gespeichert / wiederhergestellt wird, weilFragmentStatePagerAdapter die Rückrufe möglicherweise nicht aufgerufen werden, wenn der Status wiederhergestellt wird.

Das einzige, wofür ich arbeiten konnte, ist das Kopieren des Codes für FragmentStatePagerAdapter und das Hinzufügen einer Methode, die das Fragment bei gegebener Position zurückgibt ( mFragments.get(pos)). Beachten Sie, dass diese Methode davon ausgeht, dass das Fragment tatsächlich verfügbar ist (dh es war irgendwann sichtbar).

Wenn Sie besonders abenteuerlustig sind, können Sie mithilfe der Reflexion auf die Elemente der privaten mFragmentsListe zugreifen , aber dann kehren wir zum ersten Punkt zurück (der Name der Liste bleibt garantiert nicht gleich).


1
Um mit dem Problem umzugehen, dass "nicht gut funktioniert, wenn der Status gespeichert / wiederhergestellt wird", überschreibe ich die Methode saveState (). Damit der Pager kein Fragment speichert, wenn die Aktivität angehalten wird. Es klappt.
AntoineP

2
Dies trägt nur zum Patchwork bei, ohne das zugrunde liegende Problem zu beheben. Ich behaupte nicht, dass es nicht funktioniert - nur, dass es keine gute Implementierung ist.
Melllvar

8
getSupportFragmentManager().getFragments().get(viewPager.getCurrentItem());

Wandeln Sie die von der obigen Zeile empfangene Instanz in das Fragment um, mit dem Sie arbeiten möchten. Funktioniert einwandfrei.

viewPager

ist die Pager-Instanz, die die Fragmente verwaltet.


Dies funktioniert nur, wenn alle Seiten als Fragment geladen sind. Wenn es beispielsweise ein Tablayout mit vielen Registerkarten / Seiten gibt, werden nicht alle geladen, sodass dies eine Indexoutofbounce-Ausnahme auslösen kann.
ProgNewbie

@progNewbie Ja, das erfordert einen zusätzlichen Workaround-Code.
Gaurav Pangam

@progNewbie Evgenys Antwort unten würde diese Situation behandeln, denke ich
Gaurav Pangam

7

Durch Auswahl einer Option muss das aktuell sichtbare Fragment aktualisiert werden.

Angenommen, Sie haben einen Verweis auf ViewPageras, um einen Verweis auf das aktuell sichtbare Fragment zu erhalten mPager. Die folgenden Schritte erhalten dann einen Verweis auf currentFragment:

  1. PageAdapter adapter = mPager.getAdapter();
  2. int fragmentIndex = mPager.getCurrentItem();
  3. FragmentStatePagerAdapter fspa = (FragmentStatePagerAdapter)adapter;
  4. Fragment currentFragment = fspa.getItem(fragmentIndex);

Die einzige Besetzung, die in Zeile 3 verwendet wird, ist normalerweise gültig. FragmentStatePagerAdapterist ein nützlicher Adapter für einen ViewPager.


7

Rufen Sie am besten einfach CallingFragmentName auf fragment = (CallingFragmentName) viewPager .getAdapter () .instantiateItem (viewPager, viewPager.getCurrentItem ()); Das aufrufende Fragment wird erneut instanziiert, sodass keine Nullzeigerausnahme ausgelöst und keine Methode dieses Fragments aufgerufen wird.


6

Aktuelles Fragment:

Dies funktioniert, wenn Sie ein Projekt mit der Registerkartenvorlage Fragmente erstellt haben.

Fragment f = mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());

Beachten Sie, dass dies mit der Standardimplementierung von Aktivitätsvorlagen mit Registerkarten funktioniert.


30
Dies hängt von Ihrer Implementierung von getItem () ab. Möglicherweise wird eine neue Instanz des Fragments zurückgegeben, was wahrscheinlich nicht Ihren Wünschen entspricht.
Tom

So machst du es. Wenn Sie beim Erstellen eines Projekts mit Fragmenten immer noch die Standardkonfiguration haben. Keiner hatte diese Lösung. Also dachte ich, es würde helfen.
Hasan

3

Ich habe folgendes verwendet:

 int index = vpPager.getCurrentItem();
 MyPagerAdapter adapter = ((MyPagerAdapter)vpPager.getAdapter());
 MyFragment suraVersesFragment = (MyFragment)adapter.getRegisteredFragment(index);

1
Dies basiert auf einer benutzerdefinierten Pager-Implementierung aus der hier gefundenen Antwort: stackoverflow.com/questions/8785221/…
Tom Redman

1
Genau das habe ich versucht, aber ohne zu wissen, welche Methoden außer Kraft gesetzt werden müssen, hätte ich Stunden verschwendet - danke!
Bron Davies

2

FragmentStatePagerAdapterhat eine private Instanzvariable mCurrentPrimaryItemvom Typ Fragment. Man kann sich nur fragen, warum Android-Entwickler es nicht mit einem Getter geliefert haben. Diese Variable wird in der setPrimaryItem()Methode instanziiert . Überschreiben Sie diese Methode also so, dass Sie den Verweis auf diese Variable erhalten. Am Ende habe ich einfach meine eigenen deklariert mCurrentPrimaryItemund den Inhalt von kopiertsetPrimaryItem() meiner Überschreibung .

Bei Ihrer Implementierung von FragmentStatePagerAdapter:

private Fragment mCurrentPrimaryItem = null;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment)object;
    if (fragment != mCurrentPrimaryItem) {
        if (mCurrentPrimaryItem != null) {
            mCurrentPrimaryItem.setMenuVisibility(false);
            mCurrentPrimaryItem.setUserVisibleHint(false);
        }
        if (fragment != null) {
            fragment.setMenuVisibility(true);
            fragment.setUserVisibleHint(true);
        }
        mCurrentPrimaryItem = fragment;
    }
}

public TasksListFragment getCurrentFragment() {
    return (YourFragment) mCurrentPrimaryItem;
}

2

Sie können die definieren , PagerAdapterwie dies dann können Sie alle bekommen Fragmentin ViewPager.

private class PagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();

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

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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

    public void addFragment(Fragment fragment) {
        mFragmentList.add(fragment);
    }
}

Um den Strom zu bekommen Fragment

Fragment currentFragment = mPagerAdapter.getItem(mViewPager.getCurrentItem());

2

Wenn wir den viewPager verwenden, ist instantiateItem (viewpager, index) eine gute Möglichkeit, in Aktivität auf die Fragmentinstanz zuzugreifen. // Index - Index des Fragments, dessen Instanz Sie möchten.

Zum Beispiel greife ich auf die Fragmentinstanz von 1 index- zu.

Fragment fragment = (Fragment) viewPageradapter.instantiateItem(viewPager, 1);

if (fragment != null && fragment instanceof MyFragment) {      
 ((MyFragment) fragment).callYourFunction();

}

1

Ich hatte das gleiche Problem und löste es mit diesem Code.

MyFragment fragment = (MyFragment) thisActivity.getFragmentManager().findFragmentById(R.id.container);

Ersetzen Sie einfach den Namen MyFragment durch den Namen Ihres Fragments und fügen Sie die ID Ihres Fragmentcontainers hinzu.


1

Dies ist zukunftssicherer als die akzeptierte Antwort:

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    /* ------------------------------------------------------------------------------------------ */
    // region Private attributes :

    private Context _context;
    private FragmentManager _fragmentManager;
    private Map<Integer, String> _fragmentsTags = new HashMap<>();

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Constructor :

    public MyFragmentPagerAdapter(Context context, FragmentManager fragmentManager) {

        super(fragmentManager);

        _context = context;
        _fragmentManager = fragmentManager;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region FragmentPagerAdapter methods :

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

    @Override
    public Fragment getItem(int position) {

        if(_fragmentsTags.containsKey(position)) {

            return _fragmentManager.findFragmentByTag(_fragmentsTags.get(position));
        }
        else {

            switch (position) {

                case 0 : { return Fragment.instantiate(_context, Tab1Fragment.class.getName()); }
                case 1 : { return Fragment.instantiate(_context, Tab2Fragment.class.getName()); }
            }
        }

        return null;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {

        // Instantiate the fragment and get its tag :
        Fragment result = (Fragment) super.instantiateItem(container, position);
        _fragmentsTags.put(position, result.getTag());

        return result;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */
}

Sie sollten stattdessen WeakHashMap verwenden.
Prakash Nadar

1

Das fragliche Szenario wird besser bedient, wenn jedes Fragment seine eigenen Menüelemente hinzufügt und onOptionsItemSelected () direkt verarbeitet, wie in der offiziellen Dokumentation beschrieben . Es ist besser, undokumentierte Tricks zu vermeiden.


Diese Antwort könnte weitere Details dazu verwenden. Wie löst das Hinzufügen eines Menüelements das ViewPagerProblem?
Suragch

Das @ Suragch ViewPager-Problem tritt auf, wenn Menüereignisse in einer Aktivität behandelt werden, die Fragmente hostet, und ist nicht vorhanden, wenn die Ereignisse innerhalb des Fragments selbst behandelt werden. Der Link zeigt, wie ein Fragment seine eigenen Menüelemente hinzufügen kann.
Y2i

1

Nachdem ich alle Kommentare und Antworten gelesen habe, erkläre ich eine optimale Lösung für dieses Problem. Die beste Option ist @ riks Lösung, daher basiert meine Verbesserung auf seiner.

Anstatt jede FragmentClass wie fragen zu müssen

if(FragmentClass1){
   ...
if(FragmentClass2){
   ...
}

Erstellen Sie Ihre eigene Benutzeroberfläche, und lassen Sie Ihre Kinderfragmente sie implementieren

public interface MyChildFragment {
    void updateView(int position);
}

Dann können Sie so etwas tun, um Ihre inneren Fragmente zu initiieren und zu aktualisieren.

Fragment childFragment = (Fragment) mViewPagerDetailsAdapter.instantiateItem(mViewPager,mViewPager.getCurrentItem());

if (childFragment != null) {
   ((MyChildFragment) childFragment).updateView();
}

PS Seien Sie vorsichtig, wo Sie diesen Code ablegen. Wenn Sie insatiateItem aufrufen, bevor das System ihn tatsächlich erstellt, ist das savedInstanceStateIhres untergeordneten Fragments dafür null

public void onCreate(@Nullable Bundle savedInstanceState){
      super(savedInstanceState)
}

Stürzt Ihre App ab.

Viel Glück


1

Basierend auf dem, was er @chahat jain geantwortet hat:

"Wenn wir den viewPager verwenden, ist instantiateItem (viewpager, index) eine gute Möglichkeit, auf die Fragmentinstanz in Aktivität zuzugreifen. // Indexindex des Fragments, dessen Instanz Sie möchten."

Wenn du das in Kotlin machen willst

val fragment =  mv_viewpager.adapter!!.instantiateItem(mv_viewpager, 0) as Fragment
                if ( fragment is YourFragmentFragment)
                 {
                    //DO somthign 
                 }

0 auf die Fragmentinstanz von 0

// ================================================ ========================= // // ###################### ######### Anwendungsbeispiel ################################## // // == ================================================== ===================== //

Hier ist ein vollständiges Beispiel, um eine verlustreichste Vision zu erhalten

Hier ist mein veiewPager in der XML-Datei

   ...
    <android.support.v4.view.ViewPager
                android:id="@+id/mv_viewpager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="5dp"/>
    ...

Und die Heimaktivität, bei der ich die Registerkarte einfüge

...
import kotlinx.android.synthetic.main.movie_tab.*

class HomeActivity : AppCompatActivity() {

    lateinit var  adapter:HomeTabPagerAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
       ...
    }



    override fun onCreateOptionsMenu(menu: Menu) :Boolean{
            ...

        mSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
          ...

            override fun onQueryTextChange(newText: String): Boolean {

                if (mv_viewpager.currentItem  ==0)
                {
                    val fragment =  mv_viewpager.adapter!!.instantiateItem(mv_viewpager, 0) as Fragment
                    if ( fragment is ListMoviesFragment)
                        fragment.onQueryTextChange(newText)
                }
                else
                {
                    val fragment =  mv_viewpager.adapter!!.instantiateItem(mv_viewpager, 1) as Fragment
                    if ( fragment is ListShowFragment)
                        fragment.onQueryTextChange(newText)
                }
                return true
            }
        })
        return super.onCreateOptionsMenu(menu)
    }
        ...

}

1

In meiner vorherigen Implementierung habe ich eine Liste untergeordneter Fragmente gespeichert, um später darauf zugreifen zu können. Dies stellte sich jedoch als falsche Implementierung heraus, die zu großen Speicherverlusten führte.

Am Ende benutze ich die instantiateItem(...)Methode, um das aktuelle Fragment zu erhalten:

val currentFragment = adapter?.instantiateItem(viewPager, viewPager.currentItem)

Oder um ein anderes Fragment in Position zu bringen:

val position = 0
val myFirstFragment: MyFragment? = (adapter?.instantiateItem(viewPager, position) as? MyFragment)

Aus der Dokumentation :

Erstellen Sie die Seite für die angegebene Position. Der Adapter ist für das Hinzufügen der Ansicht zu dem hier angegebenen Container verantwortlich, muss jedoch nur sicherstellen, dass dies zum Zeitpunkt der Rückkehr von finishUpdate (ViewGroup) erfolgt.


0

In meiner Tätigkeit habe ich:

int currentPage = 0;//start at the first tab
private SparseArray<Fragment> fragments;//list off fragments
viewPager.setOnPageChangeListener(new OnPageChangeListener() {

@Override
public void onPageSelected(int pos) {
        currentPage = pos;//update current page
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
@Override
public void onPageScrollStateChanged(int arg0) {}
});



@Override
public void onAttachFragment(Fragment fragment) {
    super.onAttachFragment(fragment);
    if(fragment instanceof Fragment1)
        fragments.put(0, fragment);
    if(fragment instanceof Fragment2)
        fragments.put(2, fragment);
    if(fragment instanceof Fragment3)
        fragments.put(3, fragment);
    if(fragment instanceof Fragment4)
        fragments.put(4, fragment);
}

Then I have the following method for getting the current fragment

public Fragment getCurrentFragment() {
    return fragments.get(currentPage);
}

0
final ViewPager.OnPageChangeListener onPageChangeListener =
    new ViewPager.OnPageChangeListener() {
      @Override public void onPageSelected(int position) {
        switch (position) {
          case 0:
            FragmentClass1 frag1 =
                (FragmentClass1) viewPager.getAdapter()
                    .instantiateItem(viewPager, viewPager.getCurrentItem());
            frag1.updateList("Custom text");
            break;
          case 1:
            break;
          case 2:
            break;
        }
      }

      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

      }

      @Override public void onPageScrollStateChanged(int state) {
      }
    };


 viewPager.addOnPageChangeListener(onPageChangeListener);

viewPager.setAdapter(adapter);


viewPager.post(new Runnable() {
  @Override public void run() {
    onPageChangeListener.onPageSelected(viewPager.getCurrentItem());
  }
});

Manchmal kann das Fragment ein Nullobjekt auslösen, sodass Sie eine neue ausführbare Datei ausführen können, um das Problem beim ersten Laden zu beheben.

Danke Rick


Bitte fügen Sie einen Text hinzu, der erklärt, warum dieser Code das Problem des OP lösen würde. Erklären Sie angesichts der Anzahl der anderen Antworten auch, warum diese Antwort besser ist als die bereits gegebenen. Helfen Sie anderen zu verstehen.
APC

0

Überschreiben Sie setPrimaryItem aus Ihrem FragmentPagerAdapter: Das Objekt ist das sichtbare Fragment:

 @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (mCurrentFragment != object) {
                mCurrentFragment = (LeggiCapitoloFragment) object;
            }
            super.setPrimaryItem(container, position, object);
        }

0

Holen Sie sich einfach den aktuellen Artikel vom Pager und fragen Sie Ihren Adapter nach dem Fragment dieser Position.

 int currentItem = viewPager.getCurrentItem();
    Fragment item = mPagerAdapter.getItem(currentItem);
    if (null != item && item.isVisible()) {
       //do whatever want to do with fragment after doing type checking
        return;
    }

3
Was ist, wenn getItemeine neue Instanz erstellt
Prakash Nadar

0

Um das aktuelle Fragment abzurufen , rufen Sie die Position in ViewPager bei public void onPageSelected (endgültige int-Position) ab und dann

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

SECTNUM - Positionsargument, das im öffentlichen statischen PlaceholderFragment newInstance (int sectionNumber) zugewiesen wurde; des Fragments

getChildFragmentManager () oder getFragmentManager () - hängt davon ab, wie SectionsPagerAdapter erstellt wurde


0

Sie können einen BroadcastReceiver im Fragment implementieren und eine Absicht von überall senden. Der Empfänger des Fragments kann auf die bestimmte Aktion warten und die Methode der Instanz aufrufen.

Eine Einschränkung besteht darin, sicherzustellen, dass die View-Komponente bereits instanziiert ist und (und für einige Vorgänge, z. B. das Scrollen einer Liste, muss die ListView bereits gerendert sein).


0

Dies ist der einfachste Hack:

fun getCurrentFragment(): Fragment? {
    return if (count == 0) null
    else instantiateItem(view_pager, view_pager.currentItem) as? Fragment
}

(Kotlin-Code)

Rufen instantiateItem(viewPager, viewPager.getCurrentItem()Sie einfach an und sprechen Sie es an Fragment. Ihr Artikel wurde bereits instanziiert. Um sicherzugehen, können Sie einen Scheck für hinzufügen getCount.

Funktioniert mit beiden FragmentPagerAdapterund FragmentStatePagerAdapter!


0

Wenn sich Ihr Pager in einem Fragment befindet, verwenden Sie Folgendes:

private fun getPagerCurrentFragment(): Fragment? {
    return childFragmentManager.findFragmentByTag("android:switcher:${R.id.myViewPagerId}:${myViewPager.currentItem}")
}

Wo R.id.myViewPagerIdbefindet sich die ID Ihres ViewPagers im XML-Layout?


Dies funktioniert nur mit normalem FragmentPageAdapter und nicht mit FragmentStatePageAdapter.
ProgNewbie

0

Sie können ein Array von Fragmenten als Registerfragmente deklarieren

class DashboardPagerAdapter(fm: FragmentManager?) : FragmentStatePagerAdapter(fm!!) {
    // CURRENT FRAGMENT
    val registeredFragments = SparseArray<Fragment>()

    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        val fragment = super.instantiateItem(container, position) as Fragment
        registeredFragments.put(position, fragment)
        return fragment
    }

    override fun getItem(position: Int): Fragment {
        return when (position) {
            0 -> HomeFragment.newInstance()
            1 -> ConverterDashboardFragment.newInstance()
            2 -> CartFragment.newInstance()
            3 -> CustomerSupportFragment.newInstance()
            4 -> ProfileFragment.newInstance()
            else -> ProfileFragment.newInstance()
        }
    }

    override fun getCount(): Int {
        return 5
    }

}

Dann können Sie es als verwenden

adapter?.let {
    val cartFragment = it.registeredFragments[2] as CartFragment?
    cartFragment?.myCartApi(true)
}

Wenn Sie sich für diesen Weg entscheiden, sollten Sie auch destroyItem () implementieren und die zerstörte Instanz aus registerFragments entfernen. Andernfalls tritt ein Speicherverlust auf.
Dephinera

Das ist ein guter Rat. Sie können die Antwort gerne bearbeiten.
Hamza Khan

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.