webentwicklung-frage-antwort-db.com.de

support FragmentPagerAdapter enthält Verweise auf alte Fragmente

LETZTE INFOS:

ich habe mein Problem auf ein Problem beschränkt, bei dem der fragmentManager Instanzen alter Fragmente beibehält und mein Viewpager nicht mit meinem FragmentManager synchronisiert ist. Lesen Sie diese Ausgabe ... http://code.google.com/p/Android/issues/detail?id=19211#makechanges . Ich habe immer noch keine Ahnung, wie ich das lösen kann. Irgendwelche Vorschläge...

Ich habe lange versucht, dieses Problem zu beheben, und jede Hilfe wäre sehr dankbar. Ich verwende einen FragmentPagerAdapter, der eine Liste von Fragmenten wie folgt akzeptiert:

List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName())); 
...
new PagerAdapter(getSupportFragmentManager(), fragments);

Die Implementierung ist Standard. Ich verwende die ActionBarSherlock- und v4-Berechnungsbibliothek für Fragmente.

Mein Problem ist, dass nach dem Verlassen der App und dem Öffnen mehrerer anderer Anwendungen und dem Zurückkehren die Fragmente ihren Bezug auf die FragmentActivity (dh getActivity() == null) verlieren. Ich kann nicht herausfinden, warum das so ist. Ich habe versucht, setRetainInstance(true); manuell einzustellen, aber das hilft nicht. Ich dachte mir, dass dies passiert, wenn meine FragmentActivity zerstört wird. Dies passiert jedoch immer noch, wenn ich die App öffne, bevor ich die Protokollnachricht erhalte. Gibt es Ideen?

@Override
protected void onDestroy(){
    Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
    super.onDestroy();
}

Der Adapter:

public class PagerAdapter extends FragmentPagerAdapter {
    private List<Fragment> fragments;

    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);

        this.fragments = fragments;

    }

    @Override
    public Fragment getItem(int position) {

        return this.fragments.get(position);

    }

    @Override
    public int getCount() {

        return this.fragments.size();

    }

}

Eines meiner Fragmente wurde entfernt, aber ich sagte, dass alles ausgezogen ist und trotzdem nicht funktioniert ...

public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    handler = new Handler();    
    setHasOptionsMenu(true);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
    context = activity;
    if(context== null){
        Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
    }else{
        Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
    }

}

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

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.my_fragment,container, false);

    return v;
}


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

private void callService(){
    // do not call another service is already running
    if(startLoad || !canSet) return;
    // set flag
    startLoad = true;
    canSet = false;
    // show the bottom spinner
    addFooter();
    Intent intent = new Intent(context, MyService.class);
    intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
    context.startService(intent);
}

private ResultReceiver resultReceiver = new ResultReceiver(null) {
    @Override
    protected void onReceiveResult(int resultCode, final Bundle resultData) {
        boolean isSet = false;
        if(resultData!=null)
        if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
            if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
                removeFooter();
                startLoad = false;
                isSet = true;
            }
        }

        switch(resultCode){
        case MyService.STATUS_FINISHED: 
            stopSpinning();
            break;
        case SyncService.STATUS_RUNNING:
            break;
        case SyncService.STATUS_ERROR:
            break;
        }
    }
};

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    menu.clear();
    inflater.inflate(R.menu.activity, menu);
}

@Override
public void onPause(){
    super.onPause();
}

public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
    boolean loadMore = /* maybe add a padding */
        firstVisible + visibleCount >= totalCount;

    boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;

    if(away){
        // startLoad can now be set again
        canSet = true;
    }

    if(loadMore) 

}

public void onScrollStateChanged(AbsListView arg0, int state) {
    switch(state){
    case OnScrollListener.SCROLL_STATE_FLING: 
        adapter.setLoad(false); 
        lastState = OnScrollListener.SCROLL_STATE_FLING;
        break;
    case OnScrollListener.SCROLL_STATE_IDLE: 
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_IDLE;
        break;
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
        break;
    }
}

@Override
public void onDetach(){
    super.onDetach();
    if(this.adapter!=null)
        this.adapter.clearContext();

    Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}

public void update(final int id, String name) {
    if(name!=null){
        getActivity().getSupportActionBar().setTitle(name);
    }

}

}

Die Aktualisierungsmethode wird aufgerufen, wenn ein Benutzer mit einem anderen Fragment interagiert und die getActivity NULL zurückgibt. Hier ist die Methode, die das andere Fragment aufruft ...

((MyFragment) pagerAdapter.getItem(1)).update(id, name);

Ich glaube, wenn die App zerstört wird und dann neu erstellt wird, anstatt die App nur bis zum Standardfragment zu starten, startet die App und der Viewpager navigiert zur letzten bekannten Seite. Dies scheint merkwürdig, sollte die App nicht einfach in das Standardfragment geladen werden? 

102
Maurycy

Sie haben ein Problem, weil Sie Verweise auf Ihre Fragmente außerhalb von PagerAdapter.getItem instanziieren und beibehalten, und versuchen, diese Verweise unabhängig vom ViewPager zu verwenden. Wie Seraph sagt, haben Sie die Garantie, dass ein Fragment zu einem bestimmten Zeitpunkt in einem ViewPager instanziiert/hinzugefügt wurde - dies sollte als Implementierungsdetail betrachtet werden. Ein ViewPager lädt seine Seiten faul; In der Standardeinstellung wird nur die aktuelle Seite sowie die linke und rechte Seite geladen.

Wenn Sie Ihre App in den Hintergrund stellen, werden die zum Fragment-Manager hinzugefügten Fragmente automatisch gespeichert. Auch wenn Ihre App beendet wird, werden diese Informationen beim Neustart der App wiederhergestellt.

Bedenken Sie nun, dass Sie sich einige Seiten angesehen haben, Fragmente A, B und C. Sie wissen, dass diese zum Fragmentmanager hinzugefügt wurden. Da Sie FragmentPagerAdapter und nicht FragmentStatePagerAdapter verwenden, werden diese Fragmente immer noch hinzugefügt (aber möglicherweise getrennt), wenn Sie zu anderen Seiten blättern.

Bedenken Sie, dass Sie dann Ihre Anwendung im Hintergrund ausführen und dann beendet wird. Wenn Sie zurückkommen, wird sich Android daran erinnern, dass Sie Fragmente A, B und C im Fragmentmanager verwendet haben. Dadurch werden sie für Sie neu erstellt und dann hinzugefügt. Diejenigen, die jetzt zum Fragmentmanager hinzugefügt werden, sind NICHT die, die Sie in Ihrer Fragmentliste in Ihrer Aktivität haben.

Der FragmentPagerAdapter versucht nicht, getPosition aufzurufen, wenn bereits ein Fragment für diese bestimmte Seitenposition hinzugefügt wurde. Da das von Android erstellte Fragment niemals entfernt wird, besteht keine Hoffnung, es durch einen Aufruf von getPosition zu ersetzen. Es ist auch ziemlich schwierig, einen Verweis darauf zu bekommen, weil er mit einem Tag hinzugefügt wurde, der Ihnen unbekannt ist. Dies ist beabsichtigt; Sie werden davon abgehalten, sich mit den Fragmenten zu beschäftigen, die der Ansichts-Pager verwaltet. Sie sollten alle Aktionen innerhalb eines Fragments ausführen, mit der Aktivität kommunizieren und ggf. auffordern, zu einer bestimmten Seite zu wechseln.

Nun zurück zu Ihrem Problem mit der fehlenden Aktivität. Wenn Sie pagerAdapter.getItem(1)).update(id, name) aufrufen, nachdem all dies geschehen ist, wird das Fragment in Ihrer Liste zurückgegeben. das dem Fragmentmanager noch nicht hinzugefügt wurde und hat daher keine Aktivitätsreferenz. Ich würde vorschlagen, dass Ihre Aktualisierungsmethode einige (möglicherweise von der Aktivität verwaltete) gemeinsam genutzte Datenstrukturen ändern sollte. Wenn Sie dann zu einer bestimmten Seite wechseln, kann sie sich auf der Grundlage dieser aktualisierten Daten zeichnen.

118
antonyt

Ich habe eine einfache Lösung gefunden, die für mich funktioniert hat.

Machen Sie Ihren Fragmentadapter so, dass er FragmentStatePagerAdapter anstelle von FragmentPagerAdapter erweitert und die Methode onSave überschreibt, um NULL zurückzugeben

@Override
public Parcelable saveState()
{
    return null;
}

Dies verhindert, dass Android ein Fragment neu erstellt


Einen Tag später fand ich eine andere und bessere Lösung.

Rufen Sie setRetainInstance(true) für alle Ihre Fragmente auf und speichern Sie die Referenzen irgendwo. Ich habe das in statischen Variablen in meiner Aktivität gemacht, weil es als singleTask deklariert ist und Fragmente immer gleich bleiben können.

Auf diese Weise erstellt Android keine Fragmente neu, sondern verwendet dieselben Instanzen.

103
mc.dev

Ich habe dieses Problem gelöst, indem ich direkt über FragmentManager auf meine Fragmente zugegriffen habe, statt wie bisher über den FragmentPagerAdapter. Zuerst muss ich das Tag des Fragments herausfinden, das automatisch vom FragmentPagerAdapter generiert wird ...

private String getFragmentTag(int pos){
    return "Android:switcher:"+R.id.viewpager+":"+pos;
}

Dann bekomme ich einfach einen Hinweis auf dieses Fragment und mache, was ich so brauche ...

Fragment f = this.getSupportFragmentManager().findFragmentByTag(getFragmentTag(1));
((MyFragmentInterface) f).update(id, name);
viewPager.setCurrentItem(1, true);

In meinen Fragmenten stelle ich setRetainInstance(false); so ein, dass ich manuell Werte zum SavedInstanceState-Bundle hinzufügen kann. 

@Override
public void onSaveInstanceState(Bundle outState) {
    if(this.my !=null)
        outState.putInt("myId", this.my.getId());

    super.onSaveInstanceState(outState);
}

und dann in OnCreate greife ich den Schlüssel und stelle den Zustand des Fragments bei Bedarf wieder her. Eine einfache Lösung, die (zumindest für mich) schwer herauszufinden war. 

27
Maurycy

Globale getestete Lösung.

getSupportFragmentManager() behält die Nullreferenz einige Male bei, und der View-Pager erstellt keine neuen, da er auf dasselbe Fragment verweist. Um dieses Problem zu lösen, löst getChildFragmentManager() das Problem auf einfache Weise.

Tu das nicht:  

new PagerAdapter(getSupportFragmentManager(), fragments);

Mach das:

new PagerAdapter(getChildFragmentManager() , fragments);

21
Vinayak

Versuchen Sie nicht, zwischen Fragmenten in ViewPager zu interagieren. Sie können nicht garantieren, dass ein anderes Fragment angehängt ist oder sogar existiert. Sie müssen den Actionbar-Titel nicht von Fragment ändern, sondern Sie können dies aus Ihrer Aktivität heraus tun. Verwenden Sie dazu das Standardschnittstellenmuster:

public interface UpdateCallback
{
    void update(String name);
}

public class MyActivity extends FragmentActivity implements UpdateCallback
{
    @Override
    public void update(String name)
    {
        getSupportActionBar().setTitle(name);
    }

}

public class MyFragment extends Fragment
{
    private UpdateCallback callback;

    @Override
    public void onAttach(SupportActivity activity)
    {
        super.onAttach(activity);
        callback = (UpdateCallback) activity;
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
        callback = null;
    }

    public void updateActionbar(String name)
    {
        if(callback != null)
            callback.update(name);
    }
}
7
Seraph

Sie können die Fragmente entfernen, wenn Sie den Viewpager zerstören. In meinem Fall habe ich sie bei onDestroyView() meines Fragments entfernt:

@Override
public void onDestroyView() {

    if (getChildFragmentManager().getFragments() != null) {
        for (Fragment fragment : getChildFragmentManager().getFragments()) {
            getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
        }
    }

    super.onDestroyView();
}
4
Raoni Novellino

Nach ein paar Stunden Suche nach einem ähnlichen Problem denke ich, dass es eine andere Lösung gibt. Das hat zumindest für mich funktioniert und ich muss nur ein paar Zeilen ändern.

Dies ist das Problem, das ich hatte. Ich habe eine Aktivität mit einem View-Pager, der einen FragmentStatePagerAdapter mit zwei Fragmenten verwendet. Alles funktioniert einwandfrei, bis ich die Aktivität erzwinge (Entwickleroptionen) oder den Bildschirm drehen. Ich behalte einen Verweis auf die beiden Fragmente, nachdem sie in der Methode getItem erstellt wurden.

An diesem Punkt wird die Aktivität erneut erstellt und alles funktioniert an diesem Punkt, aber ich habe den Verweis auf meine Fragmetns verloren, da getItem nicht mehr aufgerufen wird. 

So habe ich das Problem im FragmentStatePagerAdapter behoben:

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object aux = super.instantiateItem(container, position);

        //Update the references to the Fragments we have on the view pager
        if(position==0){
            fragTabOne = (FragOffersList)aux;
        }
        else{
            fragTabTwo = (FragOffersList) aux;
        }

        return aux;
    }

Sie werden nicht erneut einen Aufruf von getItem erhalten, wenn der Adapter bereits intern einen Verweis darauf hat, und Sie sollten dies nicht ändern. Stattdessen können Sie das Fragment, in dem es verwendet wird, erhalten, indem Sie diese andere Methode instantiateItem () betrachten, die für jedes Ihrer Fragmente aufgerufen wird.

Hoffe das hilft jedem.

4
Lancelot

Meine Lösung: Ich habe fast jede View als static gesetzt. Jetzt interagiert meine App perfekt. Die statischen Methoden von überall aus aufrufen zu können, ist vielleicht kein guter Stil, aber warum sollte man mit Code herumspielen, der nicht funktioniert? Ich habe eine Menge Fragen und deren Antworten hier auf SO gelesen und keine Lösung brachte (für mich) Erfolg. 

Ich weiß, dass dies zu Speicher- und Abfallproblemen führen kann und mein Code für andere Projekte nicht geeignet ist. Ich habe jedoch keine Angst - ich habe die App auf verschiedenen Geräten und Bedingungen getestet Plattform scheint damit umgehen zu können. Die Benutzeroberfläche wird jede Sekunde aktualisiert, und sogar auf einem S2 ICS (4.0.3) -Gerät kann die App Tausende von Geo-Markern verarbeiten.

0
Martin Pfeffer

Da der FragmentManager sich um die Wiederherstellung Ihrer Fragmente kümmert, sobald die onResume () -Methode aufgerufen wird, muss ich das Fragment zur Aktivität aufrufen und sich einer Liste hinzufügen. In meiner Instanz speichere ich all dies in meiner PagerAdapter-Implementierung. Jedes Fragment kennt seine Position, da es bei der Erstellung den Fragmentargumenten hinzugefügt wird. Wann immer ich ein Fragment an einem bestimmten Index bearbeiten muss, muss ich nur die Liste meines Adapters verwenden.

Im Folgenden finden Sie ein Beispiel für einen Adapter für einen benutzerdefinierten ViewPager, der das Fragment vergrößert, wenn es den Fokus erhält, und es verkleinert, wenn es den Fokus verlässt. Neben den Adapter- und Fragmentklassen, die ich hier habe, brauchen Sie lediglich, dass die übergeordnete Aktivität die Adaptervariable referenzieren kann, und Sie werden festgelegt.

Adapter

public class GrowPagerAdapter extends FragmentPagerAdapter implements OnPageChangeListener, OnScrollChangedListener {

public final String TAG = this.getClass().getSimpleName();

private final int COUNT = 4;

public static final float BASE_SIZE = 0.8f;
public static final float BASE_ALPHA = 0.8f;

private int mCurrentPage = 0;
private boolean mScrollingLeft;

private List<SummaryTabletFragment> mFragments;

public int getCurrentPage() {
    return mCurrentPage;
}

public void addFragment(SummaryTabletFragment fragment) {
    mFragments.add(fragment.getPosition(), fragment);
}

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

    mFragments = new ArrayList<SummaryTabletFragment>();
}

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

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

@Override
public void onPageScrollStateChanged(int state) {}

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

    adjustSize(position, positionOffset);
}

@Override
public void onPageSelected(int position) {
    mCurrentPage = position;
}

/**
 * Used to adjust the size of each view in the viewpager as the user
 * scrolls.  This provides the effect of children scaling down as they
 * are moved out and back to full size as they come into focus.
 * 
 * @param position
 * @param percent
 */
private void adjustSize(int position, float percent) {

    position += (mScrollingLeft ? 1 : 0);
    int secondary = position + (mScrollingLeft ? -1 : 1);
    int tertiary = position + (mScrollingLeft ? 1 : -1);

    float scaleUp = mScrollingLeft ? percent : 1.0f - percent;
    float scaleDown = mScrollingLeft ? 1.0f - percent : percent;

    float percentOut = scaleUp > BASE_ALPHA ? BASE_ALPHA : scaleUp;
    float percentIn = scaleDown > BASE_ALPHA ? BASE_ALPHA : scaleDown;

    if (scaleUp < BASE_SIZE)
        scaleUp = BASE_SIZE;

    if (scaleDown < BASE_SIZE)
        scaleDown = BASE_SIZE;

    // Adjust the fragments that are, or will be, on screen
    SummaryTabletFragment current = (position < mFragments.size()) ? mFragments.get(position) : null;
    SummaryTabletFragment next = (secondary < mFragments.size() && secondary > -1) ? mFragments.get(secondary) : null;
    SummaryTabletFragment afterNext = (tertiary < mFragments.size() && tertiary > -1) ? mFragments.get(tertiary) : null;

    if (current != null && next != null) {

        // Apply the adjustments to each fragment
        current.transitionFragment(percentIn, scaleUp);
        next.transitionFragment(percentOut, scaleDown);

        if (afterNext != null) {
            afterNext.transitionFragment(BASE_ALPHA, BASE_SIZE);
        }
    }
}

@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {

    // Keep track of which direction we are scrolling
    mScrollingLeft = (oldl - l) < 0;
}
}

Fragment

public class SummaryTabletFragment extends BaseTabletFragment {

public final String TAG = this.getClass().getSimpleName();

private final float SCALE_SIZE = 0.8f;

private RelativeLayout mBackground, mCover;
private TextView mTitle;
private VerticalTextView mLeft, mRight;

private String mTitleText;
private Integer mColor;

private boolean mInit = false;
private Float mScale, mPercent;

private GrowPagerAdapter mAdapter;
private int mCurrentPosition = 0;

public String getTitleText() {
    return mTitleText;
}

public void setTitleText(String titleText) {
    this.mTitleText = titleText;
}

public static SummaryTabletFragment newInstance(int position) {

    SummaryTabletFragment fragment = new SummaryTabletFragment();
    fragment.setRetainInstance(true);

    Bundle args = new Bundle();
    args.putInt("position", position);
    fragment.setArguments(args);

    return fragment;
}

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

    mRoot = inflater.inflate(R.layout.tablet_dummy_view, null);

    setupViews();
    configureView();

    return mRoot;
}

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

    if (savedInstanceState != null) {
        mColor = savedInstanceState.getInt("color", Color.BLACK);
    }

    configureView();
}

@Override
public void onSaveInstanceState(Bundle outState)  {

    outState.putInt("color", mColor);

    super.onSaveInstanceState(outState);
}

@Override
public int getPosition() {
    return getArguments().getInt("position", -1);
}

@Override
public void setPosition(int position) {
    getArguments().putInt("position", position);
}

public void onResume() {
    super.onResume();

    mAdapter = mActivity.getPagerAdapter();
    mAdapter.addFragment(this);
    mCurrentPosition = mAdapter.getCurrentPage();

    if ((getPosition() == (mCurrentPosition + 1) || getPosition() == (mCurrentPosition - 1)) && !mInit) {
        mInit = true;
        transitionFragment(GrowPagerAdapter.BASE_ALPHA, GrowPagerAdapter.BASE_SIZE);
        return;
    }

    if (getPosition() == mCurrentPosition && !mInit) {
        mInit = true;
        transitionFragment(0.00f, 1.0f);
    }
}

private void setupViews() {

    mCover = (RelativeLayout) mRoot.findViewById(R.id.cover);
    mLeft = (VerticalTextView) mRoot.findViewById(R.id.title_left);
    mRight = (VerticalTextView) mRoot.findViewById(R.id.title_right);
    mBackground = (RelativeLayout) mRoot.findViewById(R.id.root);
    mTitle = (TextView) mRoot.findViewById(R.id.title);
}

private void configureView() {

    Fonts.applyPrimaryBoldFont(mLeft, 15);
    Fonts.applyPrimaryBoldFont(mRight, 15);

    float[] size = UiUtils.getScreenMeasurements(mActivity);
    int width = (int) (size[0] * SCALE_SIZE);
    int height = (int) (size[1] * SCALE_SIZE);

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
    mBackground.setLayoutParams(params);

    if (mScale != null)
        transitionFragment(mPercent, mScale);

    setRandomBackground();

    setTitleText("Fragment " + getPosition());

    mTitle.setText(getTitleText().toUpperCase());
    mLeft.setText(getTitleText().toUpperCase());
    mRight.setText(getTitleText().toUpperCase());

    mLeft.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showNextPage();
        }
    });

    mRight.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showPrevPage();
        }
    });
}

private void setRandomBackground() {

    if (mColor == null) {
        Random r = new Random();
        mColor = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));
    }

    mBackground.setBackgroundColor(mColor);
}

public void transitionFragment(float percent, float scale) {

    this.mScale = scale;
    this.mPercent = percent;

    if (getView() != null && mCover != null) {

        getView().setScaleX(scale);
        getView().setScaleY(scale);

        mCover.setAlpha(percent);
        mCover.setVisibility((percent <= 0.05f) ? View.GONE : View.VISIBLE);
    }
}

@Override
public String getFragmentTitle() {
    return null;
}
}
0
saulpower

Nur damit du es weißt ...

Mit diesen Kursen gibt es noch einen interessanten Bug, der es wert ist, ihn zu teilen. 

Ich verwende einen ViewPager, um durch eine Baumstruktur von Elementen zu navigieren (ein Element auswählen und der Ansichts-Pager animiert das Scrollen nach rechts, und der nächste Zweig wird angezeigt, navigiert zurück und der ViewPager scrollt in die entgegengesetzte Richtung, um zum vorherigen Knoten zurückzukehren) .

Das Problem entsteht, wenn ich Fragmente am Ende des FragmentStatePagerAdapter abschiebe und abbringe. Es ist intelligent genug, um zu bemerken, dass sich die Elemente ändern, und intelligent genug, um ein Fragment zu erstellen und zu ersetzen, wenn sich das Element geändert hat. Aber nicht intelligent genug, um den Fragmentstatus zu verwerfen, oder intelligent genug, um die intern gespeicherten Fragmentzustände abzugleichen, wenn sich die Adaptergröße ändert. Wenn Sie also ein Element platzieren und ein neues an das Ende schieben, erhält das Fragment für das neue Element den gespeicherten Status des Fragments für das alte Element. Dies hat zu absolutem Chaos in meinem Code geführt. Meine Fragmente enthalten Daten, die zum erneuten Abrufen aus dem Internet viel Arbeit erfordern. Es war also keine Option, den Status nicht zu speichern.

Ich habe keine saubere Lösung. Ich habe so etwas verwendet:

  public void onSaveInstanceState(Bundle outState) {
    IFragmentListener listener = (IFragmentListener)getActivity();
    if (listener!= null)
    {
        if (!listener.isStillInTheAdapter(this.getAdapterItem()))
        {
            return; // return empty state.
        }

    }
    super.onSaveInstanceState(outState);

    // normal saving of state for flips and 
    // paging out of the activity follows
    ....
  }

Eine unvollkommene Lösung, da die neue Fragmentinstanz immer noch ein saveState-Bundle erhält, aber zumindest keine veralteten Daten enthält.

0
Robin Davies

Ich habe das Problem durch Speichern der Fragmente in SparceArray gelöst:

public abstract class SaveFragmentsPagerAdapter extends FragmentPagerAdapter {

    SparseArray<Fragment> fragments = new SparseArray<>();

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

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

    @Nullable
    public Fragment getFragmentByPosition(int position){
        return fragments.get(position);
    }

}
0
xaxtix

Ich hatte das gleiche Problem, aber mein ViewPager befand sich in einem TopFragment, das einen Adapter mit setAdapter(new FragmentPagerAdapter(getChildFragmentManager())) erstellt und festgelegt hat.

Ich habe dieses Problem behoben, indem ich onAttachFragment(Fragment childFragment) im TopFragment wie folgt überschreibe:

@Override
public void onAttachFragment(Fragment childFragment) {
    if (childFragment instanceof OnboardingDiamondsFragment) {
        mChildFragment = (ChildFragment) childFragment;
    }

    super.onAttachFragment(childFragment);
}

Wie bereits bekannt (siehe Antworten oben), erstellt der childFragmentManager bei der Neuerstellung auch die Fragmente, die sich im viewPager befinden.
Das Wichtigste ist, dass er danach onAttachFragment aufruft - und jetzt haben wir einen Hinweis auf das neu erstellte Fragment! 

Hoffe, das wird jedem helfen, der dieses alte Q wie mich bekommt :)

0
Shirane85

Da die Leute nicht dazu neigen, Kommentare zu lesen, ist hier eine Antwort, die das, was ich geschrieben habe, größtenteils dupliziert hier :

die Hauptursache des Problems ist die Tatsache, dass das System Android nicht getItem aufruft, um tatsächlich angezeigte Fragmente abzurufen, sondern instantiateItem. Diese Methode versucht zunächst, eine Fragmentinstanz für eine bestimmte Registerkarte in FragmentManager zu suchen und erneut zu verwenden. Nur wenn diese Suche fehlschlägt (was nur beim erstmaligen Erstellen von FragmentManager geschieht), wird getItem aufgerufen. Es liegt auf der Hand, Fragmente (die schwer sein können) nicht jedes Mal neu zu erstellen, wenn ein Benutzer sein Gerät dreht.
Um dieses Problem zu lösen, sollten Sie in Ihrer Aktivität keine Fragmente mit Fragment.instantiate erstellen, sondern mit pagerAdapter.instantiateItem, und alle diese Aufrufe sollten von startUpdate/finishUpdate Methodenaufrufen umgeben sein, die starten/commit Fragmenttransaktion jeweils. getItem sollte der Ort sein, an dem Fragmente wirklich mit ihren jeweiligen Konstruktoren erstellt werden.

List<Fragment> fragments = new Vector<Fragment>();

@Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        fragments.add(adapter.instantiateItem(viewPager, 0));
        fragments.add(adapter.instantiateItem(viewPager, 1));
        // and so on if you have more tabs...
        adapter.finishUpdate(viewPager);
}

class MyPagerAdapter extends FragmentPagerAdapter {

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

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

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
}
0
morgwai