ViewPager snippet is lost when the parent ViewPager snippet is hidden and then shown

I saw strange behavior with my ViewPager along with my own FragmentStatePagerAdapter.

The My View hierarchy is as follows:

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

When the fragment responsible for the root view of fragment (1) is hidden (using the hid () operation in the fragment transaction) and then displayed (with .show ()), the fragment view that is currently displayed in ViewPager (3) becomes zero although the fragment still exists. Basically, my ViewPager becomes completely empty / transparent.

The only thing I found to fix it was to call

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

after displaying the parent fragment. It somehow makes the looks recreate and appear on the screen. Unfortunately, this sometimes raises exceptions related to the pager adapter calling unregisterDataSetObserver() twice on the old observer.

Is there a better way to do this? I guess I ask:

Why will my fragment views inside my ViewPager be destroyed if the parent fragment of the ViewPager is hidden?

Update: this also happens when the application is โ€œminimizedโ€ and then โ€œrestoredโ€ (by pressing the home action key and then returning).

Upon request, here is my pager adapter class:

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

I renamed some variables, but other than that, this is exactly what I have.

+57
android android-fragments android-viewpager
Aug 22 '13 at 16:37
source share
4 answers

You do not provide enough information for your specific problem, so I created a sample project that tries to reproduce your problem: the application has an activity that contains a fragment ( PagerFragment ) in the relative layout and below this layout I have a button that hides and shows PagerFragment above . PagerFragment has a ViewPager , and each fragment in the pager adapter simply displays a label - this fragment is called a DataFragment . A list of labels is created in the parent activity and passed to PagerFragment, and then through the adapter for each DataFragment . Changing the visibility of a PagerFragment is done without problems, and each time it becomes visible again, it shows the previous label shown.

Problem Key: Use Fragment # getChildFragmentManager () when creating the viewpager adapter, not getFragmentManager!

Perhaps you can compare this simple project with what you have and see where the differences are. So here goes (top to bottom):

PagerActivity (the only action in the project):

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

its layout file 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> 

Note that I add PagerFragment when the operation is active, and the PagerFragment class:

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

its layout (only ViewPager that accepts full size):

 <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" /> 

and its 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(); } } 

This adapter creates a DataFragment for each page:

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

and DataFragment :

 <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> 

Enjoy the coding!

+78
Sep 17 '13 at 9:05
source share
โ€” -

maybe this will help mViewPager.setOffscreenPageLimit (5)

Specify the number of pages to be stored on either side of the current page in the pending view hierarchy. Pages outside this limit will be recreated from the adapter, if necessary.

This is suggested as optimization. If you know in advance the number of pages that you will need to maintain or have lazy loading mechanisms in place on your pages, setting this setting can have the perceived smoothness of paging and interaction animations. If you have a small number of pages (3-4) that you can save at the same time, less time will be spent on the layout for the newly created viewing subtypes, as the user's pages are back and forth.

You should keep this limit low, especially if your pages have complex layouts. By default, this parameter is 1.

+15
Sep 18 '13 at 16:49 on
source share

View Pager is quite adamant, while keeping its fragments fresh and thereby optimizing performance, freeing up memory when the fragment is not in use. Obviously, this is a really useful feature in a mobile system. But because of this constant release of resources, a fragment is created every time it receives focus.

 mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS); 

Here is the documentation.

this Old post has an interesting solution for your problem .. Please refer

+3
Sep 18 '13 at 21:33
source share

I had the same problem. In my application (FragmentActivity) there is a pager (ViewPager) with 3 frames. When passing between fragments, they are destroyed and recreated all the time. Actually this does not create problems in functionality (expect closed cursors), but I was also interested to know about this issue.

I donโ€™t know if there is a way around the ViewPager behavior, but I suggest having a configuration object (possibly static) and save your myViewPager object in the configuration object until destruction.

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

Thus, you can save a link to an object that will not be destroyed, therefore, there is a link to it, and you can reload all your important objects.

I hope you find my suggestion useful!

+1
Sep 12 '13 at 8:08
source share



All Articles