3

I am new in Android and I don't really understand why a Fragment's content which was added dynamically (for example some image which was added after a button click) is disappearing after scrolling some count of Fragments and then come back.

There is really simple code Activity and Fragment:

public class MyActivity extends FragmentActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
        final CustomAdapter adapter = new CustomAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
    }

    class CustomFragment extends Fragment {

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

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            getView().findViewById(R.id.clickMeButton).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getView().findViewById(R.id.image).setVisibility(View.VISIBLE);
                }
            });
        }

    }

    class CustomAdapter extends FragmentStatePagerAdapter {

        private List<CustomFragment> fragments = Arrays.asList(
            new CustomFragment(),
            new CustomFragment(),
            new CustomFragment()
        );

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

        @Override
        public Fragment getItem(int i) {
            return fragments.get(i);
        }

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

}

and appropriate xmls:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent">

    <android.support.v4.view.ViewPager
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:id="@+id/viewPager" />

</LinearLayout>

fragment.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

    <Button
            android:id="@+id/clickMeButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="click me"/>

    <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher"
            android:visibility="gone"/>

</LinearLayout>

Logic is simple. On each Fragment I can click on a Button and as result an image appears.

And it works. But ...

When I show image on the first fragment, then scroll to the third one and come back to first one again image is gone.

What should I do to prevent this ? Should I somehow save state of visibility?

Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
vetalitet
  • 703
  • 2
  • 10
  • 25

4 Answers4

7

This is happening because FragmentStatePagerAdapter releases the memory associated with a Fragment when it is scrolled out of view. From the docs:

When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.

You can try one of the following four options:

1. Since you have a small number of pages, you can use FragmentPagerAdapter instead of FragmentStatePagerAdapter. FragmentPagerAdapter holds onto the created Fragments and does not destroy them on swipe.

2. In your Fragment, store a boolean in a SharedPreference and set (or clear) its value when you make the image visible or invisible. Then in the onResume() method of your Fragment, make the image visible or invisible based on the value of the SharedPreference.

3. In the onCreate() of your Fragment, call setRetainInstance(true);.

4. If the number of fragments is fixed & relatively small, then in your onCreate() add the following code:

mViewPager = (ViewPager)findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(limit);         /* limit is a fixed integer*/
Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
  • thanks for your reply. I've tried 1 and 3 items (except 2nd) but result is the same. Is it some other way to store fragments without removing from memory? I need to implement something similar to tabs in web browser. So I will use WebView for that. Of course I can store in SharedPreferences page the user selected. But it all the time will be loading of that page. That is not really correct. So I need exactly keep fragment in memory. – vetalitet Jun 14 '14 at 08:00
3

Whats happening is

Since you are using adapter there may be a number of fragments active at a time like 3 or 4 for example.

Adapter will render the views dynamically to reduce the memory usage. In your case

Fragment 1 -> Fragment 2 - > Fragment 3

Let the active fragment be 3 now, So Fragment 1 may be removed from memory when you scroll back , it will redraw the fragment by

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

So the image is still invisible and in its initial stage.

Answer

Set a flag to identify the status of the image and use it in onCreateView() to make it visible or invisible.

UPDATE

mViewPager = (ViewPager)findViewById(R.id.viewPager);
mViewPager.setOffscreenPageLimit(yourLimit);

yourLimit is the number of fragments you want to keep alive which is not in screen currently.

NOTE: This is memory intensive.

Viswanath Lekshmanan
  • 9,945
  • 1
  • 40
  • 64
  • thanks for reply, but maybe there is some way to exactly keep fragment on memory without recreating? I need to use WebView component to implement something similar to tabs in web-browser. But fragment recreation will all the time load the page – vetalitet Jun 14 '14 at 07:59
1

save the visibility of image in onSaveInstance method of your fragment class CustomFragment. Override onSaveInstanceState in the fragment activity class onSaveInstanceState call super.onSaveInstanceState(bundle) in that overridden method

     @Override
    public void onSaveInstanceState(Bundle outState) {

       super.onSaveInstanceState(outState);
       outState.putString("visibility", visibility);
      }

Then you can retain the state in the onActivityCreated method of your fragment class CustomFragment

int visibility = View.INVISIBLE;
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if(savedInstanceState!=null)
            visibility = savedInstanceState.getInt("visibility");
        getView().findViewById(R.id.image).setVisibility(visibility);
        getView().findViewById(R.id.clickMeButton)
        .setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getView().findViewById(R.id.image).setVisibility(View.VISIBLE);
            }
        });
    }
Pr38y
  • 1,565
  • 13
  • 21
  • thank you for reply, but may be there is some way just to keep fragment in memory without fragment recreating – vetalitet Jun 14 '14 at 08:12
  • If you have fix and relatively small number of Fragment `viewPager.setOffscreenPageLimit(numberOfFragment)` – Pr38y Jun 14 '14 at 08:15
1

mViewPager.setOffscreenPageLimit(limit);

It worked for me thanks ! I had a viewpager with 3 fragments and a recyclerview in the first one. When i clicked on the last one my recyclerview disappeared..

Not checked yet about performance

PierMott
  • 11
  • 3