Ist es möglich, auf das aktuelle Fragment zuzugreifen, das von einem ViewPager angezeigt wird?

70

Ich habe eine App mit a ViewPagerund drei Fragments. Ich versuche herauszufinden, wie der aktuelle Wert Fragmentangezeigt wird, damit ich zu seinen Argumenten gelangen kann.

Ich OnPageChangeListenergreife nach dem aktuellen Seitenindex, aber

ViewPager.getChildAt(int position);

gibt a zurück View. Wie ist die Beziehung zwischen diesem Viewund dem Strom Fragment?

MitchellSalad
quelle

Antworten:

46

Ich habe endlich eine Antwort gefunden , die für mich funktioniert hat. Grundsätzlich können Sie mit dem Tag "android: switcher:" + R.id.viewpager + ": 0" auf das Fragment für eine viewPager-Seite zugreifen.

James
quelle
27
Die Methode in der verknüpften Antwort funktioniert nicht mit FragmentStatePagerAdapter, aber die zweite Antwort auf dieselbe verknüpfte Frage verfügt über eine Methode, die dies tut.
Adam L.
4
Während dies theoretisch die Frage beantworten kann, wäre es vorzuziehen , die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen.
18

Ich habe dieses Problem umgekehrt gelöst. Anstatt nach dem Fragment aus der Aktivität zu suchen, registriere ich das Fragment während seiner onAttach () -Methode bei seiner Eigentümeraktivität und hebe die Registrierung in der onStop () -Methode auf. Die Grundidee:

Fragment:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try{
        mActivity = (IMyActivity)activity;
    }catch(ClassCastException e){
        throw new ClassCastException(activity.toString() +" must be a IMyActivity");
    }

    mActivity.addFragment(this);
}

@Override
public void onStop() {
    mActivity.removeFragment(this);
    super.onStop();
}

IMyActivity:

public interface IFriendActivity {
    public void addFragment(Fragment f);
    public void removeFragment(Fragment f); 
}

Meine Aktivität:

public class MyActivity implements IMyActivity{

    [...]

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

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

}
sdl
quelle
2
Wie finden Sie Ihre Fragmente anhand ihrer Position?
Shawn Lauzon
Dies funktioniert nicht, da OnAttachNICHT aufgerufen wird, wenn Sie Ihre Aktivität reduzieren, während Sie OnStopnoch aufgerufen werden.
Mike Keskinov
5

Bearbeiten - Mach das nicht. Wenn Sie versucht sind, lesen Sie die Kommentare, warum es eine schlechte Idee ist.

Bei der Gelegenheit versuchen Sie immer noch, dieses Problem zu lösen:

Verlängern FragmentPagerAdapter. Erstellen Sie im Konstruktor die benötigten Fragmente und speichern Sie sie in einer Liste (Array / ArrayList) von Fragmenten.

private final int numItems = 3;
Fragment[] frags;

public SwipeAdapter (FragmentManager fm) {
    super(fm);

    //Instantiate the Fragments
    frags = new Fragment[numItems];

    Bundle args = new Bundle();
    args.putString("arg1", "foo");

    frags[0] = new MyFragment();
    frags[1] = new YourFragment();
    frags[2] = new OurFragment();
    frags[2].setArguments(args);
}

Dann getItem(int position)können Sie so etwas tun

public Fragment getItem(int position) {
    return frags[position];
}

Ich bin mir nicht sicher, ob dies die allgemein akzeptierte Methode ist, aber es hat bei mir funktioniert.

Bearbeiten

Dies ist wirklich kein guter Weg. Wenn Sie vorhaben, Orientierungsänderungen zu verarbeiten oder Ihre App in den Hintergrund zu rücken, wird dies wahrscheinlich Ihren Code beschädigen. Bitte lesen Sie die Kommentare unter dieser Antwort für weitere Informationen. Verwenden Sie lieber die Antwort von @James

Mike T.
quelle
1
Ihre Lösung ist in Ordnung, wenn die Vorerstellung jedes Fragments kein Einwand ist. Unterschätzen Sie jedoch nicht, wie teuer Ihre Fragmente sein können.
Rene
2
Guter Punkt. Ich hatte auch schlimme Probleme mit dieser Methode, wenn Aktivitäten neu gestartet wurden und die Fragmente getActivity()nicht die aktuelle Aktivität zurückgeben, sondern eine frühere Instanz, die zerstört wurde
Mike T
4
Dies funktioniert so lange, bis Sie das Gerät drehen. Dann überleben die Fragmente die Rotation, der Cache Ihres Adapters jedoch nicht. Wenn Sie nun nach dem aktuell angezeigten Fragment fragen, erstellen Sie ein neues Fragment, das überhaupt nicht verwendet wird.
Mark Gjøl
2
Einverstanden. Ich denke nicht, dass dies ein großartiger Ansatz ist, jetzt wo ich ein bisschen mehr gelernt habe. Viele Apps sperren die Bildschirmausrichtung (z. B. Spiele), aber selbst dann, wenn die App in den Hintergrund tritt und dann zurückkommt, tritt meines Erachtens das gleiche Problem auf.
Mike T
3
@RolfSmit: Ich stimme zu. Ich lasse es einfach stehen, damit die Leute die Diskussion lesen, und möglicherweise für Leute, die die Bildschirmorientierung sperren
Mike T
4

Ja, es ist möglich, wenn Sie verwenden FragmentStatePagerAdapter.

ViewPager vp;
//...
YourFragment fragment = (YourFragment) adapter.instantiateItem(vp, vp.getCurrentItem());
Volodymyr Kulyk
quelle
3

BITTE VERWENDEN SIE DIESES NICHT

Lassen Sie Ihren Adapter die folgende FragmentStatePagerWithCurrentAdapterKlasse erweitern und nicht implementierengetItem denselben Code ingetItemAtIndex

Setzen Sie das ViewPager OnPageChangeListenerauf die Instanz des Adapters.

Wenn Sie auf das aktuelle Fragment zugreifen müssen, rufen Sie einfach an adapter.getCurrentItem().

package your.package;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.SparseArray;
import android.view.ViewGroup;

public abstract class FragmentStatePagerWithCurrentAdapter 
extends FragmentStatePagerAdapter 
implements OnPageChangeListener {
    int currentPage = 0;

    private SparseArray<Fragment> mPageReferenceMap = new SparseArray<Fragment>();

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

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

    public abstract Fragment getItemAtIndex(int index);

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {

        super.destroyItem(container, position, object);

        mPageReferenceMap.remove(Integer.valueOf(position));
    }

    public Fragment getCurrentItem() {
        return mPageReferenceMap.get(currentPage);
    }

    @Override
    public void onPageScrollStateChanged(int arg0) {

    }

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

    }

    @Override
    public void onPageSelected(int newPageIndex) {
        currentPage = newPageIndex;
    }

}

Ich habe als Referenz den folgenden Blog-Beitrag verwendet: http://tamsler.blogspot.com/2011/11/android-viewpager-and-fragments-part-ii.html

Jorge Garcia
quelle
3
Das ist nicht richtig. Bei einer Gerätedrehung erstellt das System das Fragment neu, indem es den Konstruktor des Fragments aufruft, ohne PagerAdapter.getItem zu durchlaufen. Die einzig praktikable Alternative besteht darin, das Fragmentobjekt in Fragment.onAttach zu speichern, wie es sdl zeigt.
Stephen Cheng
@ StephenCheng Du hast recht. Ich hatte eine feste Porträt-App, als ich diese verwendete, entdeckte dann aber das von Ihnen erwähnte Problem, als die Aktivität aufgrund von Speicherdruck beendet und dann der Status neu geladen wurde. Am Ende habe ich diesen Code in einen viel komplexeren Code umgeschrieben, um alle Szenarien zu bewältigen, die wir brauchten, einschließlich dieses.
Jorge Garcia
1

Es wurde hier erklärt: http://developer.android.com/guide/topics/fundamentals/fragments.html

In OnCreateView müssen Sie eine Ansicht zurückgeben, um eine Benutzeroberfläche für Ihr Fragment zu zeichnen. Ich denke, das ist die Beziehung.

Auch diese Frage könnte ähnlich sein: Konzentrieren Sie sich auf ViewPager

Dante
quelle
Hallo Dante, ja, ich verstehe das, aber ich versuche, das Fragment angesichts der Ansicht zu bekommen.
MitchellSalad
Ok, was ist getFragmentManager().findFragmentById(R.id.fragment_container);dann? Sie werden das aktuelle Fragment in diesem Container erhalten, denke ich.
Dante
1
Ich habe diese Seite gefunden, weil ich das gleiche Problem habe. findFragmentById funktioniert bei mir nicht.
James
1

Sie haben folgende Möglichkeiten: - Auf der Klassenausdehnung eines Ansichts-Pager-Adapters (wie PagerAdapter, FragmentStatePagerAdapter ...) überschreiben Sie die Methode instantiateItem:

@Override
public Object instantiateItem(ViewGroup container, int position) {
        final Fragment frag = (Fragment) super.instantiateItem(container, position);
        if(frag instanceof ListNoteOfTypeFragment){                
            final ListNoteOfTypeFragment listNoteOfTypeFragment = (ListNoteOfTypeFragment) frag;
            //do whatever you want with your fragment here
            listNoteOfTypeFragment.setNoteChangeListener(mListener);
        }
        return frag;
    }    
Anh Nguyen
quelle
1

Definitive Antwort, die nahtlos funktioniert (aber kleiner Hack):

irgendwo im Layout des Seitenfragments:

<FrameLayout android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" android:id="@+id/fragment_reference">
     <View android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/>
</FrameLayout>

in onCreateView () des Fragments:

...
View root = inflater.inflate(R.layout.fragment_page, container, false);
ViewGroup ref = (ViewGroup)root.findViewById(R.id.fragment_reference);
ref.setTag(this);
ref.getChildAt(0).setTag("fragment:" + pageIndex);
return root;

und Methode zum Zurückgeben von Fragment aus ViewPager, falls vorhanden:

public Fragment getFragment(int pageIndex) {        
        View w = mViewPager.findViewWithTag("fragment:" + pageIndex);
        if (w == null) return null;
        View r = (View) w.getParent();
        return (Fragment) r.getTag();
}
Hox
quelle
0

Jorge Garcias FragmentStatePagerWithCurrentAdapter ist eine sehr gute Lösung, die jedoch geringfügig verbessert werden muss. Falls die Aktivität als Reaktion auf eine Konfigurationsänderung oder ähnliches zerstört und neu erstellt wird, wird getItem nicht für die Fragmente aufgerufen, die vom Fragmentmanager gespeichert und abgerufen wurden. Daher überschreibe ich getItem normalerweise in meiner Unterklasse und füge Folgendes in den FragmentStatePagerWithCurrentAdapter ein

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object item = super.instantiateItem(container, position);
    if ( item instanceof Fragment ) {
        pageReferenceMap.put(position, (Fragment)item);
    }
    return item;
}

Das instanziierte Element wird jedes Mal aufgerufen, wenn auf das Fragment an dieser Position zugegriffen wird.

dmapr
quelle
0

Oder speichern Sie einfach alle Fragmente in einer Karte:

public class MyFragment extends Fragment implements OnPageChangeListener {

    private ViewPager viewPager;
    private FragmentStatePagerAdapter viewAdapter;
    private View rootView;
    private Map<Integer, Fragment> fragments = new HashMap<Integer, Fragment>();

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

        rootView = inflater.inflate(R.layout.introdution, container, false);

        viewPager = (ViewPager) rootView.findViewById(R.id.pager);
        viewAdapter = new ViewAdapter(getFragmentManager());
        viewPager.setAdapter(viewAdapter);
        viewPager.addOnPageChangeListener(this);

        return rootView;

    }

    private class ViewAdapter extends FragmentStatePagerAdapter {

        public ViewAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
        }

        @Override
        public Fragment getItem(int position) {

            Fragment result = null;

            switch (position) {
                case 0: result = Fragment1.newInstance(); break;
                case 1: result = Fragment2.newInstance(); break;
            }

            if (result != null)
                fragments.put(position, result);

            return result;

        }

        @Override
        public int getCount() {

            return 2;
        }

    }

    @Override
    public void onPageScrollStateChanged(int arg0) {
    }

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

    @Override
    public void onPageSelected(int position) {

        Fragment currentFragment = fragments.get(position);

    }

}
almisoft
quelle
-2

Ich denke, es gibt den besseren Weg, dies zu nutzen

Log.i(TAG, "getCurrentItem " + mViewPager.getCurrentItem());

Kann die aktuelle Seite des Anzeigefragments abrufen.

Huy Tower
quelle