it-swarm.com.de

ViewPager und Fragmente - Wie kann der Status eines Fragments richtig gespeichert werden?

Fragmente scheinen für die Aufteilung der UI-Logik in einige Module sehr nützlich zu sein. Aber zusammen mit ViewPager ist sein Lebenszyklus für mich immer noch unklar. Guru-Gedanken sind also dringend erforderlich!

Bearbeiten

Siehe dumme Lösung unten ;-)

Umfang

Haupttätigkeit hat ein ViewPager mit Fragmenten. Diese Fragmente können eine etwas andere Logik für andere (untergeordnete) Aktivitäten implementieren, sodass die Daten der Fragmente über eine Rückrufschnittstelle innerhalb der Aktivität gefüllt werden. Und alles funktioniert gut beim ersten Start, aber! ...

Problem

Wenn die Aktivität neu erstellt wird (z. B. bei Änderung der Ausrichtung), werden auch die Fragmente des ViewPager erstellt. Der Code (siehe unten) besagt, dass ich bei jeder Erstellung der Aktivität versuche, einen neuen ViewPager Fragmentadapter zu erstellen, der mit Fragmenten identisch ist (möglicherweise ist dies das Problem), aber FragmentManager hat alle diese Fragmente bereits irgendwo gespeichert (wo?) und startet den Erholungsmechanismus für die. Der Erholungsmechanismus ruft also das "alte" Fragment "onAttach", "onCreateView" usw. mit meinem Callback-Schnittstellenaufruf auf, um Daten über die implementierte Methode der Aktivität zu initiieren. Diese Methode verweist jedoch auf das neu erstellte Fragment, das mit der Methode onCreate der Activity erstellt wird.

Problem

Vielleicht verwende ich falsche Muster, aber selbst in Android 3 Pro-Büchern ist nicht viel dran. Also, bitte, geben Sie mir einen Doppelschlag und zeigen Sie, wie es richtig gemacht wird. Danke vielmals!

Code

Haupttätigkeit

public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {

private MessagesFragment mMessagesFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    setContentView(R.layout.viewpager_container);
    new DefaultToolbar(this);

    // create fragments to use
    mMessagesFragment = new MessagesFragment();
    mStreamsFragment = new StreamsFragment();

    // set titles and fragments for view pager
    Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
    screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
    screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);

    // instantiate view pager via adapter
    mPager = (ViewPager) findViewById(R.id.viewpager_pager);
    mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);

    // set title indicator
    TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
    indicator.setViewPager(mPager, 1);

}

/* set of fragments callback interface implementations */

@Override
public void onMessageInitialisation() {

    Logger.d("Dash onMessageInitialisation");
    if (mMessagesFragment != null)
        mMessagesFragment.loadLastMessages();
}

@Override
public void onMessageSelected(Message selectedMessage) {

    Intent intent = new Intent(this, StreamActivity.class);
    intent.putExtra(Message.class.getName(), selectedMessage);
    startActivity(intent);
}

BasePagerActivity aka helper

public class BasePagerActivity extends FragmentActivity {

BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}

Adapter

public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {

private Map<String, Fragment> mScreens;

public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {

    super(fm);
    this.mScreens = screenMap;
}

@Override
public Fragment getItem(int position) {

    return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}

@Override
public int getCount() {

    return mScreens.size();
}

@Override
public String getTitle(int position) {

    return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}

// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {

    // TODO Auto-generated method stub
}

}

Fragment

public class MessagesFragment extends ListFragment {

private boolean mIsLastMessages;

private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;

private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;

// define callback interface
public interface OnMessageListActionListener {
    public void onMessageInitialisation();
    public void onMessageSelected(Message selectedMessage);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // setting callback
    mListener = (OnMessageListActionListener) activity;
    mIsLastMessages = activity instanceof DashboardActivity;

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    inflater.inflate(R.layout.fragment_listview, container);
    mProgressView = inflater.inflate(R.layout.listrow_progress, null);
    mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
    return super.onCreateView(inflater, container, savedInstanceState);
}

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

    // instantiate loading task
    mLoadMessagesTask = new LoadMessagesTask();

    // instantiate list of messages
    mMessagesList = new ArrayList<Message>();
    mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
    setListAdapter(mAdapter);
}

@Override
public void onResume() {
    mListener.onMessageInitialisation();
    super.onResume();
}

public void onListItemClick(ListView l, View v, int position, long id) {
    Message selectedMessage = (Message) getListAdapter().getItem(position);
    mListener.onMessageSelected(selectedMessage);
    super.onListItemClick(l, v, position, id);
}

/* public methods to load messages from Host acitivity, etc... */
}

Lösung

Die blöde Lösung ist, die Fragmente in onSaveInstanceState (von Host Activity) mit putFragment zu speichern und sie über getFragment in onCreate abzurufen. Aber ich habe immer noch das seltsame Gefühl, dass die Dinge nicht so funktionieren sollten ... Siehe Code unten:

    @Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    getSupportFragmentManager()
            .putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}

protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    ...
    // create fragments to use
    if (savedInstanceState != null) {
        mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
                savedInstanceState, MessagesFragment.class.getName());
                StreamsFragment.class.getName());
    }
    if (mMessagesFragment == null)
        mMessagesFragment = new MessagesFragment();
    ...
}
478

Wenn FragmentPagerAdapter dem FragmentManager ein Fragment hinzufügt, wird ein spezielles Tag verwendet, das auf der bestimmten Position basiert, an der das Fragment platziert wird. FragmentPagerAdapter.getItem(int position) wird nur aufgerufen, wenn für diese Position kein Fragment existiert. Nach dem Drehen bemerkt Android, dass es bereits ein Fragment für diese bestimmte Position erstellt/gespeichert hat, und versucht einfach, mit FragmentManager.findFragmentByTag() eine neue Verbindung herzustellen, anstatt ein neues zu erstellen. All dies ist kostenlos, wenn Sie FragmentPagerAdapter verwenden. Deshalb ist es üblich, dass Ihr Fragment-Initialisierungscode in der getItem(int) -Methode enthalten ist.

Auch wenn wir kein FragmentPagerAdapter verwendet haben, ist es keine gute Idee, in Activity.onCreate(Bundle) jedes Mal ein neues Fragment zu erstellen. Wie Sie bemerkt haben, wird ein Fragment, das dem FragmentManager hinzugefügt wird, nach dem Drehen für Sie neu erstellt und muss nicht erneut hinzugefügt werden. Dies ist eine häufige Fehlerursache beim Arbeiten mit Fragmenten.

Ein üblicher Ansatz bei der Arbeit mit Fragmenten ist folgender:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

Bei Verwendung von FragmentPagerAdapter geben wir die Fragmentverwaltung an den Adapter ab und müssen die obigen Schritte nicht ausführen. Standardmäßig wird nur ein Fragment vor und hinter der aktuellen Position geladen (obwohl es diese nicht zerstört, es sei denn, Sie verwenden FragmentStatePagerAdapter). Dies wird durch ViewPager.setOffscreenPageLimit (int) gesteuert. Aus diesem Grund ist es nicht garantiert, dass Methoden, die direkt auf die Fragmente außerhalb des Adapters angewendet werden, gültig sind, da sie möglicherweise nicht einmal aktiv sind.

Um es kurz zu machen, Ihre Lösung, putFragment zu verwenden, um anschließend eine Referenz zu erhalten, ist nicht so verrückt und auch nicht so anders als die normale Art, Fragmente zu verwenden (siehe oben). Andernfalls ist es schwierig, eine Referenz zu erhalten, da das Fragment vom Adapter hinzugefügt wird und nicht von Ihnen persönlich. Stellen Sie einfach sicher, dass offscreenPageLimit hoch genug ist, um die gewünschten Fragmente jederzeit zu laden, da Sie darauf vertrauen, dass sie vorhanden sind. Dies umgeht die Funktionen zum verzögerten Laden des ViewPagers, scheint jedoch genau das zu sein, was Sie für Ihre Anwendung wünschen.

Ein anderer Ansatz besteht darin, FragmentPageAdapter.instantiateItem(View, int) zu überschreiben und einen Verweis auf das vom Super-Aufruf zurückgegebene Fragment zu speichern, bevor es zurückgegeben wird (es hat die Logik, das Fragment zu finden, falls es bereits vorhanden ist).

Schauen Sie sich für ein vollständigeres Bild die Quellen FragmentPagerAdapter (kurz) und ViewPager (lang) an.

443
antonyt

Ich möchte eine Lösung anbieten, die die wunderbare Antwort von antonyt erweitert, und erwähne, dass FragmentPageAdapter.instantiateItem(View, int) überschrieben wird, um Verweise auf erstellte Fragments damit Sie später daran arbeiten können. Dies sollte auch mit FragmentStatePagerAdapter funktionieren; Einzelheiten finden Sie in den Hinweisen.


Hier ist ein einfaches Beispiel, wie Sie einen Verweis auf Fragments erhalten, der von FragmentPagerAdapter zurückgegeben wird und nicht auf dem internen tags basiert, der für Fragments festgelegt ist. Der Schlüssel ist, instantiateItem() zu überschreiben und Referenzen dort anstelle von in getItem() zu speichern.

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 es vorziehen, mit tags anstelle von Klassenmitgliedervariablen/Verweisen auf Fragments zu arbeiten, können Sie auch tags nehmen, das von FragmentPagerAdapter auf dieselbe Weise: HINWEIS: Dies gilt nicht für FragmentStatePagerAdapter, da tags beim Erstellen seines Fragments nicht festgelegt wird.

@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 von tag gesetzten internen FragmentPagerAdapter beruht und stattdessen geeignete APIs zum Abrufen dieser verwendet. Auf diese Weise sind Sie auch dann sicher, wenn sich tag in zukünftigen Versionen von SupportLibrary ändert.


Vergessen Sie nicht , dass je nach Design Ihres Activity das Fragments, an dem Sie arbeiten möchten, möglicherweise noch vorhanden ist oder nicht Um dies zu berücksichtigen, führen Sie null Überprüfungen durch, bevor Sie Ihre Referenzen verwenden.

Wenn Sie stattdessen mit FragmentStatePagerAdapter arbeiten, möchten Sie keine festen Verweise auf Ihr Fragments behalten, da Sie möglicherweise viele von ihnen und haben harte Referenzen würden sie unnötigerweise im Gedächtnis behalten. Speichern Sie stattdessen die Fragment Referenzen in WeakReference Variablen anstatt in Standardvariablen. 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...
}
35
Tony Chan

Ich habe eine andere relativ einfache Lösung für Ihre Frage gefunden.

Wie Sie aus dem FragmentPagerAdapter-Quellcode ersehen können, werden die von FragmentPagerAdapter verwalteten Fragmente im FragmentManager unter dem Tag gespeichert, das generiert wurde mit:

String tag="Android:switcher:" + viewId + ":" + index;

Die viewId ist die container.getId(), die container ist Ihre ViewPager Instanz. Das index ist die Position des Fragments. Daher können Sie die Objekt-ID unter outState speichern:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("viewpagerid" , mViewPager.getId() );
}

@Override
    protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null)
        viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );  

    MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);        
    mViewPager = (ViewPager) findViewById(R.id.pager);
    if (viewpagerid != -1 ){
        mViewPager.setId(viewpagerid);
    }else{
        viewpagerid=mViewPager.getId();
    }
    mViewPager.setAdapter(titleAdapter);

Wenn Sie mit diesem Fragment kommunizieren möchten, können Sie if von FragmentManager abrufen, z.

getSupportFragmentManager().findFragmentByTag("Android:switcher:" + viewpagerid + ":0")
18
user2110066

Ich möchte eine alternative Lösung für einen vielleicht etwas anderen Fall anbieten, da mich viele meiner Nachforschungen nach Antworten zu diesem Thema geführt haben.

Mein Fall - Ich erstelle/füge Seiten dynamisch hinzu und schiebe sie in einen ViewPager, aber beim Drehen (onConfigurationChange) erhalte ich eine neue Seite, weil natürlich OnCreate erneut aufgerufen wird. Ich möchte jedoch weiterhin auf alle Seiten verweisen, die vor der Rotation erstellt wurden.

Problem - Ich habe keine eindeutigen Bezeichner für jedes Fragment, das ich erstelle. Der einzige Weg, auf das ich verweisen kann, besteht darin, Verweise in einem Array zu speichern, die nach der Rotation/Konfigurationsänderung wiederhergestellt werden sollen.

Problemumgehung - Das Schlüsselkonzept bestand darin, dass die Aktivität (die die Fragmente anzeigt) auch das Array von Verweisen auf vorhandene Fragmente verwaltet, da diese Aktivität Bundles in onSaveInstanceState verwenden kann

public class MainActivity extends FragmentActivity

Im Rahmen dieser Aktivität erkläre ich ein privates Mitglied zum Nachverfolgen der geöffneten Seiten

private List<Fragment> retainedPages = new ArrayList<Fragment>();

Dies wird jedes Mal aktualisiert, wenn onSaveInstanceState in onCreate aufgerufen und wiederhergestellt wird

@Override
protected void onSaveInstanceState(Bundle outState) {
    retainedPages = _adapter.exportList();
    outState.putSerializable("retainedPages", (Serializable) retainedPages);
    super.onSaveInstanceState(outState);
}

... sobald es gespeichert ist, kann es abgerufen werden ...

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState != null) {
        retainedPages = (List<Fragment>) savedInstanceState.getSerializable("retainedPages");
    }
    _mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
    _adapter = new ViewPagerAdapter(getApplicationContext(), getSupportFragmentManager());
    if (retainedPages.size() > 0) {
        _adapter.importList(retainedPages);
    }
    _mViewPager.setAdapter(_adapter);
    _mViewPager.setCurrentItem(_adapter.getCount()-1);
}

Dies waren die notwendigen Änderungen an der Hauptaktivität. Daher brauchte ich die Member und Methoden in meinem FragmentPagerAdapter, damit dies funktioniert

public class ViewPagerAdapter extends FragmentPagerAdapter

ein identisches Konstrukt (wie oben in MainActivity gezeigt)

private List<Fragment> _pages = new ArrayList<Fragment>();

und diese Synchronisierung (wie oben in onSaveInstanceState verwendet) wird speziell von den Methoden unterstützt

public List<Fragment> exportList() {
    return _pages;
}

public void importList(List<Fragment> savedPages) {
    _pages = savedPages;
}

Und schließlich in der Fragmentklasse

public class CustomFragment extends Fragment

damit dies alles funktioniert, gab es zunächst zwei Änderungen

public class CustomFragment extends Fragment implements Serializable

fügen Sie dies dann zu onCreate hinzu, damit die Fragmente nicht zerstört werden

setRetainInstance(true);

Ich bin immer noch dabei, Fragmente und den Android -Lebenszyklus in den Kopf zu stecken, daher kann es bei dieser Methode zu Redundanzen/Ineffizienzen kommen. Aber es funktioniert für mich und ich hoffe, dass es in ähnlichen Fällen für andere hilfreich sein kann.

16
Frank Yin

Meine Lösung ist sehr unhöflich, funktioniert aber: Da meine Fragmente dynamisch aus gespeicherten Daten erstellt wurden, entferne ich einfach alle Fragmente aus dem PageAdapter, bevor ich super.onSaveInstanceState() aufrufe, und erstelle sie bei der Erstellung der Aktivität erneut:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("viewpagerpos", mViewPager.getCurrentItem() );
    mSectionsPagerAdapter.removeAllfragments();
    super.onSaveInstanceState(outState);
}

Sie können sie in onDestroy() nicht entfernen, andernfalls erhalten Sie diese Ausnahme:

Java.lang.IllegalStateException: Kann diese Aktion nach onSaveInstanceState nicht mehr ausführen

Hier der Code im Seitenadapter:

public void removeAllfragments()
{
    if ( mFragmentList != null ) {
        for ( Fragment fragment : mFragmentList ) {
            mFm.beginTransaction().remove(fragment).commit();
        }
        mFragmentList.clear();
        notifyDataSetChanged();
    }
}

Ich speichere nur die aktuelle Seite und stelle sie in onCreate() wieder her, nachdem die Fragmente erstellt wurden.

if (savedInstanceState != null)
    mViewPager.setCurrentItem( savedInstanceState.getInt("viewpagerpos", 0 ) );  
8
Sir John

Was ist das BasePagerAdapter? Sie sollten einen der Standard-Pager-Adapter verwenden - entweder FragmentPagerAdapter oder FragmentStatePagerAdapter, je nachdem, ob Fragmente, die von ViewPager nicht mehr benötigt werden, entweder beibehalten werden sollen (ersterer) ) oder lassen Sie ihren Zustand speichern (letzteres) und bei Bedarf neu erstellen.

Beispielcode für die Verwendung von ViewPager finden Sie hier

Es ist richtig, dass die Verwaltung von Fragmenten in einem Ansichtspager über Aktivitätsinstanzen hinweg etwas kompliziert ist, da das FragmentManager im Framework dafür sorgt, dass der Status gespeichert und alle aktiven Fragmente wiederhergestellt werden, die der Pager erstellt hat. Dies bedeutet nur, dass der Adapter beim Initialisieren sicherstellen muss, dass er sich wieder mit den wiederhergestellten Fragmenten verbindet. Sie können sich den Code für FragmentPagerAdapter oder FragmentStatePagerAdapter ansehen, um zu sehen, wie dies gemacht wird.

4
hackbod

Wenn Probleme mit dem FragmentStatePagerAdapter auftreten, die den Status der Fragmente nicht ordnungsgemäß wiederherstellen, werden vom FragmentStatePagerAdapter neue Fragmente erstellt, anstatt sie aus dem Status wiederherzustellen.

Stellen Sie sicher, dass Sie ViewPager.setOffscreenPageLimit() aufrufen, bevor Sie ViewPager.setAdapeter(fragmentStatePagerAdapter) aufrufen

Beim Aufruf von ViewPager.setOffscreenPageLimit()... sucht der ViewPager sofort nach seinem Adapter und versucht, seine Fragmente zu erhalten. Dies kann passieren, bevor der ViewPager die Möglichkeit hat, die Fragmente aus savedInstanceState wiederherzustellen (wodurch neue Fragmente erstellt werden, die aus SavedInstanceState nicht neu initialisiert werden können, weil sie neu sind).

2
dell116

Ich habe diese einfache und elegante Lösung gefunden. Es wird davon ausgegangen, dass die Aktivität für die Erstellung der Fragmente verantwortlich ist und der Adapter sie nur bedient.

Dies ist der Code des Adapters (nichts Ungewöhnliches hier, außer dass mFragments eine Liste von Fragmenten ist, die von der Aktivität verwaltet werden)

class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

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

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

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

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        TabFragment fragment = (TabFragment)mFragments.get(position);
        return fragment.getTitle();
    }
} 

Das ganze Problem dieses Threads besteht darin, einen Verweis auf die "alten" Fragmente zu erhalten. Daher verwende ich diesen Code in der OnCreate-Aktivität.

    if (savedInstanceState!=null) {
        if (getSupportFragmentManager().getFragments()!=null) {
            for (Fragment fragment : getSupportFragmentManager().getFragments()) {
                mFragments.add(fragment);
            }
        }
    }

Natürlich können Sie diesen Code bei Bedarf weiter optimieren, um beispielsweise sicherzustellen, dass die Fragmente Instanzen einer bestimmten Klasse sind.

0
Merlevede

Um die Fragmente nach einer Orientierungsänderung zu erhalten, müssen Sie das .getTag () verwenden.

    getSupportFragmentManager().findFragmentByTag("Android:switcher:" + viewPagerId + ":" + positionOfItemInViewPager)

Für ein bisschen mehr Handling habe ich meine eigene ArrayList für meinen PageAdapter geschrieben, um das Fragment von viewPagerId und der FragmentClass an einer beliebigen Position zu erhalten:

public class MyPageAdapter extends FragmentPagerAdapter implements Serializable {
private final String logTAG = MyPageAdapter.class.getName() + ".";

private ArrayList<MyPageBuilder> fragmentPages;

public MyPageAdapter(FragmentManager fm, ArrayList<MyPageBuilder> fragments) {
    super(fm);
    fragmentPages = fragments;
}

@Override
public Fragment getItem(int position) {
    return this.fragmentPages.get(position).getFragment();
}

@Override
public CharSequence getPageTitle(int position) {
    return this.fragmentPages.get(position).getPageTitle();
}

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


public int getItemPosition(Object object) {
    //benötigt, damit bei notifyDataSetChanged alle Fragemnts refrehsed werden

    Log.d(logTAG, object.getClass().getName());
    return POSITION_NONE;
}

public Fragment getFragment(int position) {
    return getItem(position);
}

public String getTag(int position, int viewPagerId) {
    //getSupportFragmentManager().findFragmentByTag("Android:switcher:" + R.id.shares_detail_activity_viewpager + ":" + myViewPager.getCurrentItem())

    return "Android:switcher:" + viewPagerId + ":" + position;
}

public MyPageBuilder getPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
    return new MyPageBuilder(pageTitle, icon, selectedIcon, frag);
}


public static class MyPageBuilder {

    private Fragment fragment;

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    private String pageTitle;

    public String getPageTitle() {
        return pageTitle;
    }

    public void setPageTitle(String pageTitle) {
        this.pageTitle = pageTitle;
    }

    private int icon;

    public int getIconUnselected() {
        return icon;
    }

    public void setIconUnselected(int iconUnselected) {
        this.icon = iconUnselected;
    }

    private int iconSelected;

    public int getIconSelected() {
        return iconSelected;
    }

    public void setIconSelected(int iconSelected) {
        this.iconSelected = iconSelected;
    }

    public MyPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
        this.pageTitle = pageTitle;
        this.icon = icon;
        this.iconSelected = selectedIcon;
        this.fragment = frag;
    }
}

public static class MyPageArrayList extends ArrayList<MyPageBuilder> {
    private final String logTAG = MyPageArrayList.class.getName() + ".";

    public MyPageBuilder get(Class cls) {
        // Fragment über FragmentClass holen
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return super.get(indexOf(item));
            }
        }
        return null;
    }

    public String getTag(int viewPagerId, Class cls) {
        // Tag des Fragment unabhängig vom State z.B. nach bei Orientation change
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return "Android:switcher:" + viewPagerId + ":" + indexOf(item);
            }
        }
        return null;
    }
}

Also erstelle einfach eine MyPageArrayList mit den Fragmenten:

    myFragPages = new MyPageAdapter.MyPageArrayList();

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_data_frag),
            R.drawable.ic_sd_storage_24dp,
            R.drawable.ic_sd_storage_selected_24dp,
            new WidgetDataFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_color_frag),
            R.drawable.ic_color_24dp,
            R.drawable.ic_color_selected_24dp,
            new WidgetColorFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_textsize_frag),
            R.drawable.ic_settings_widget_24dp,
            R.drawable.ic_settings_selected_24dp,
            new WidgetTextSizeFrag()));

und füge sie dem viewPager hinzu:

    mAdapter = new MyPageAdapter(getSupportFragmentManager(), myFragPages);
    myViewPager.setAdapter(mAdapter);

danach können Sie nach der Orientierung das richtige Fragment mit Hilfe seiner Klasse ändern:

        WidgetDataFrag dataFragment = (WidgetDataFrag) getSupportFragmentManager()
            .findFragmentByTag(myFragPages.getTag(myViewPager.getId(), WidgetDataFrag.class));
0
Scrounger