vetalitet
vetalitet

Reputation: 703

Android Viewpager fragments refreshed when swiped

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?

Upvotes: 3

Views: 5158

Answers (4)

PierMott
PierMott

Reputation: 11

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

Upvotes: 1

Yash Sampat
Yash Sampat

Reputation: 30611

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

Upvotes: 7

Viswanath Lekshmanan
Viswanath Lekshmanan

Reputation: 10083

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.

Upvotes: 3

Pr38y
Pr38y

Reputation: 1565

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

Upvotes: 1

Related Questions