webentwicklung-frage-antwort-db.com.de

Die Ansicht des ViewPager-Fragments ging verloren, wenn das übergeordnete Fragment von ViewPager ausgeblendet und angezeigt wurde

Ich habe ein merkwürdiges Verhalten mit meinem ViewPager und meinem eigenen FragmentStatePagerAdapter festgestellt.

Meine Ansichtshierarchie sieht folgendermaßen aus:

-> (1) Fragment root view (RelativeLayout)
 -> (2) ViewPager
  -> (3) ViewPager's current fragment view

Wenn das Fragment, das für die Fragmentansicht "Fragment" (1) verantwortlich ist, ausgeblendet wird (mithilfe von .hide () in einer Fragmenttransaktion) und anschließend (mit .show ()) angezeigt wird, wird die Fragmentansicht angezeigt, die aktuell im ViewPager angezeigt wurde (3 ) wird zu null, obwohl das Fragment noch vorhanden ist. Mein ViewPager wird grundsätzlich leer/transparent.

Die einzige Möglichkeit, dies zu beheben, besteht darin, anzurufen 

int current = myViewPager.getCurrentItem();
myViewPager.setAdapter(myAdapter);
myViewPager.setCurrentItem(current);

nachdem das übergeordnete Fragment angezeigt wird. Dadurch werden die Ansichten irgendwie neu erstellt und auf dem Bildschirm angezeigt. Leider führt dies gelegentlich zu Ausnahmen, bei denen der Pageradapter zweimal unregisterDataSetObserver() von einem alten Beobachter aufgerufen wird.

Gibt es einen besseren Weg, dies zu tun? Ich denke, was ich frage, ist: 

Warum werden meine Fragmentansichten in meinem ViewPager zerstört, wenn das übergeordnete Fragment des ViewPagers ausgeblendet ist?

Update: Dies geschieht auch, wenn die Anwendung "minimiert" und dann "wiederhergestellt" wird (durch Drücken der Home-Aktionstaste und anschließender Rückkehr).

Auf Anfrage hier meine Pageradapterklasse:

public class MyInfoSlidePagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<MyInfo> infos = new ArrayList<MyInfo>();

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

    public MyInfoSlidePagerAdapter (FragmentManager fm, MyInfo[] newInfos) {
        super(fm);
        setInfos(newInfos);
    }

    @Override
    public int getItemPosition(Object object) {
        int position = infos.indexOf(((MyInfoDetailsFragment)object).getMyInfo());
        return position > 0 ? position : POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return infos.get(position).getName();
    }

    @Override
    public Fragment getItem(int i) {
        return infos.size() > 0 ? MyInfoDetailsFragment.getNewInstance(infos.get(i)) : null;
    }

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

    public Location getMyInfoAtPosition(int i) {
        return infos.get(i);
    }

    public void setInfos(MyInfo[] newInfos) {
        infos = new ArrayList<MyInfo>(Arrays.asList(newInfos));
    }

    public int getPositionOfMyInfo(MyInfo info) {
        return infos.indexOf(info);
    }
}

Ich habe einige Variablen umbenannt, aber ansonsten ist es genau das, was ich habe.

61
John Leehey

Sie geben nicht genügend Informationen für Ihr spezifisches Problem an. Daher habe ich ein Beispielprojekt erstellt, mit dem versucht wird, Ihr Problem zu reproduzieren: Die App verfügt über eine Aktivität, die ein Fragment (PagerFragment) innerhalb eines relativen Layouts enthält versteckt und zeigt oben PagerFragment. PagerFragment hat eine ViewPager und jedes Fragment innerhalb des Pageradapters zeigt einfach ein Label an - dieses Fragment heißt DataFragment. Die Label-Liste wird in der übergeordneten Aktivität erstellt und an PagerFragment und dann über ihren Adapter an jede DataFragment übergeben. Das Ändern der Sichtbarkeit von PagerFragment erfolgt ohne Probleme. Jedes Mal, wenn die Sichtbarkeit von PagerFragment wieder angezeigt wird, wird die zuvor angezeigte Beschriftung angezeigt. 

Der Schlüssel des Problems: Verwenden Sie Fragment # getChildFragmentManager () , wenn Sie den Viewpager-Adapter erstellen und nicht getFragmentManager!

Vielleicht können Sie dieses einfache Projekt mit dem, was Sie haben, vergleichen und prüfen, wo die Unterschiede liegen. Also hier geht es (von oben nach unten):

PagerActivity (die einzige Aktivität im Projekt):

public class PagerActivity extends FragmentActivity {

    private static final String PAGER_TAG = "PagerActivity.PAGER_TAG";

    @Override
    protected void onCreate(Bundle savedInstance) {
        super.onCreate(savedInstance);
        setContentView(R.layout.pager_activity);
        if (savedInstance == null) {
            PagerFragment frag = PagerFragment.newInstance(buildPagerData());
            FragmentManager fm = getSupportFragmentManager();
            fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit();
        }
        findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                changeFragmentVisibility();
            }
        });
    }

    private List<String> buildPagerData() {
        ArrayList<String> pagerData = new ArrayList<String>();
        pagerData.add("Robert de Niro");
        pagerData.add("John Smith");
        pagerData.add("Valerie Irons");
        pagerData.add("Metallica");
        pagerData.add("Rammstein");
        pagerData.add("Zinedine Zidane");
        pagerData.add("Ronaldo da Lima");
        return pagerData;
    }

    protected void changeFragmentVisibility() {
        Fragment frag = getSupportFragmentManager().findFragmentByTag(PAGER_TAG);
        if (frag == null) {
            Toast.makeText(this, "No PAGER fragment found", Toast.LENGTH_SHORT).show();
            return;
        }
        boolean visible = frag.isVisible();
        Log.d("APSampler", "Pager fragment visibility: " + visible);
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        if (visible) {
            ft.hide(frag);
        } else {
            ft.show(frag);
        }
        ft.commit();
        getSupportFragmentManager().executePendingTransactions();
    }
}

seine Layoutdatei pager_activity.xml

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:padding="4dp" >

    <Button
        Android:id="@+id/btnFragments"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_alignParentBottom="true"
        Android:layout_centerHorizontal="true"
        Android:text="Hide/Show fragments" />

    <RelativeLayout
        Android:id="@+id/layout_fragments"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_above="@+id/btnFragments"
        Android:layout_marginBottom="4dp" >
    </RelativeLayout>

</RelativeLayout>

Beachten Sie, dass ich die Variable __ hinzufügen, wenn die Aktivität zum ersten Mal angezeigt wird - und die Klasse PagerFragment:

public class PagerFragment extends Fragment {

    private static final String DATA_ARGS_KEY = "PagerFragment.DATA_ARGS_KEY";

    private List<String> data;

    private ViewPager pagerData;

    public static PagerFragment newInstance(List<String> data) {
        PagerFragment pagerFragment = new PagerFragment();
        Bundle args = new Bundle();
        ArrayList<String> argsValue = new ArrayList<String>(data);
        args.putStringArrayList(DATA_ARGS_KEY, argsValue);
        pagerFragment.setArguments(args);
        return pagerFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        data = getArguments().getStringArrayList(DATA_ARGS_KEY);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.pager_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        pagerData = (ViewPager) view.findViewById(R.id.pager_data);
        setupPagerData();
    }

    private void setupPagerData() {
        PagerAdapter adapter = new LocalPagerAdapter(getChildFragmentManager(), data);
        pagerData.setAdapter(adapter);
    }

}

sein Layout (nur der ViewPager, der volle Größe hat):

<Android.support.v4.view.ViewPager xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/pager_data"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />

und seinen Adapter:

public class LocalPagerAdapter extends FragmentStatePagerAdapter {
    private List<String> pagerData;

    public LocalPagerAdapter(FragmentManager fm, List<String> pagerData) {
        super(fm);
        this.pagerData = pagerData;
    }

    @Override
    public Fragment getItem(int position) {
        return DataFragment.newInstance(pagerData.get(position));
    }

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

Dieser Adapter erstellt für jede Seite eine DataFragment:

public class DataFragment extends Fragment {
    private static final String DATA_ARG_KEY = "DataFragment.DATA_ARG_KEY";

    private String localData;

    public static DataFragment newInstance(String data) {
        DataFragment df = new DataFragment();
        Bundle args = new Bundle();
        args.putString(DATA_ARG_KEY, data);
        df.setArguments(args);
        return df;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        localData = getArguments().getString(DATA_ARG_KEY);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.data_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        view.findViewById(R.id.btn_page_action).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), localData, Toast.LENGTH_SHORT).show();
            }
        });
        ((TextView) view.findViewById(R.id.txt_label)).setText(localData);
    }
}

und DataFragments Layout:

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical"
    Android:padding="4dp" >

    <Button
        Android:id="@+id/btn_page_action"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_alignParentBottom="true"
        Android:layout_centerHorizontal="true"
        Android:text="Interogate" />

    <TextView
        Android:id="@+id/txt_label"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_centerInParent="true"
        Android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

Viel Spaß beim Codieren!

87
gunar

vielleicht hilft es mViewPager.setOffscreenPageLimit (5)

Legen Sie die Anzahl der Seiten fest, die auf beiden Seiten von .__ beibehalten werden sollen. aktuelle Seite in der Ansichtshierarchie im Ruhezustand. Seiten jenseits davon Das Limit wird bei Bedarf vom Adapter neu erstellt.

Dies wird als Optimierung angeboten. Wenn Sie vorab die Nummer kennen. von Seiten, die Sie in .__ unterstützen oder lazy-loading-Mechanismen unterstützen müssen. Wenn Sie diese Einstellung auf Ihren Seiten vornehmen, kann die Einstellung dieser Einstellung Vorteile in .__ haben. wahrgenommene Glätte von Paging-Animationen und Interaktion. Wenn Sie haben eine kleine Anzahl von Seiten (3-4), die Sie gleichzeitig aktiv halten können, Für neu erstellte Ansichts-Unterstrukturen wird weniger Zeit als .__ im Layout aufgewendet. die Benutzerseiten hin und her.

Sie sollten diesen Grenzwert niedrig halten, insbesondere wenn Ihre Seiten komplex sind Layouts. Diese Einstellung ist standardmäßig auf 1 eingestellt.

18
Malder

View Pager ist sehr darauf bedacht, seine Fragmente stets auf dem neuesten Stand zu halten und somit die Leistung zu optimieren, indem Speicherplatz frei wird, wenn ein Fragment nicht verwendet wird. Dies ist eindeutig ein gültiges nützliches Merkmal in einem mobilen System. Aufgrund dieser beharrlichen Freigabe von Ressourcen wird das Fragment jedoch jedes Mal erstellt, wenn es an Konzentration gewinnt. 

mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS);

Hier ist die Dokumentation.

dieser alte Beitrag hat eine interessante Lösung für Ihr Problem

3

Ich hatte das gleiche Problem. Meine App (FragmentActivity) hat einen Pager (ViewPager) mit 3 Framgents. Beim Wischen zwischen den Fragmenten werden sie ständig zerstört und neu erstellt. Eigentlich macht es keine Probleme mit der Funktionalität (erwarten Sie nicht geschlossene Cursors), aber ich habe mich auch über diese Frage gewundert.

Ich weiß nicht, ob es eine Problemumgehung gibt, um das Verhalten des ViewPagers zu ändern. Ich schlage jedoch vor, ein Konfigurationsobjekt (möglicherweise ein statisches Objekt) zu verwenden und vor dem Löschen das myViewPager-Objekt am Konfigurationsobjekt zu speichern.

public class App extends FragmentActivity {

    static MyData data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
         data = (MyData) getLastCustomNonConfigurationInstance();
         if (data == null) {
             data = new MyData();
             data.savedViewPager = myViewPager;
         } else {
             myViewPager = data.savedViewPager;
        }
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        Log.d("onRetainCustomNonConfigurationInstance", "Configuration call");
        return data;
    }
}

public class MyData {
    public ViewPager savedViewPager;
}

Auf diese Weise können Sie den Verweis auf ein Objekt speichern, das nicht zerstört wird. Daher gibt es einen Verweis darauf und Sie können alle wichtigen Objekte erneut laden.

Ich hoffe, Sie finden meinen Vorschlag nützlich!

1
csikos.balint

Für mich wechselte ich zu getChildFragmentManager() anstelle von getFragmentManager() Und funktioniert gut . Ex:

pagerAdapt = new PagerAdapt(getChildFragmentManager());
0
Mahmoud Ayman