44

I have the following code :

MainActivity.java

package com.erc.library;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.FragmentTransaction;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

import com.erc.sayeghlibrary.adapter.TabsPagerAdapter;

public class MainActivity extends FragmentActivity implements
        ActionBar.TabListener {

    private ViewPager viewPager;
    private TabsPagerAdapter mAdapter;
    private ActionBar actionBar;
    // Tab titles
    private String[] tabs = { "Stories", "Dictionaries", "eBooks"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);






        int actionBarTitleId = Resources.getSystem().getIdentifier("action_bar_title", "id", "android");
        if (actionBarTitleId > 0) {
            TextView title = (TextView) findViewById(actionBarTitleId);
            if (title != null) {
                title.setTextColor(Color.WHITE);
            }
        }       

        // Initilization
        viewPager = (ViewPager) findViewById(R.id.pager);
        actionBar = getActionBar();
        mAdapter = new TabsPagerAdapter(getSupportFragmentManager());

        viewPager.setAdapter(mAdapter);
        actionBar.setHomeButtonEnabled(false);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);


        // Adding Tabs
        for (String tab_name : tabs) {
            actionBar.addTab(actionBar.newTab().setText(tab_name)
                    .setTabListener(this));
        }

        /**
         * on swiping the viewpager make respective tab selected
         * */
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                // on changing the page
                // make respected tab selected
                actionBar.setSelectedNavigationItem(position);
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
            }

            @Override
            public void onPageScrollStateChanged(int arg0) {
            }
        });







    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        // on tab selected
        // show respected fragment view
        viewPager.setCurrentItem(tab.getPosition());


    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    }






       @Override
       public boolean onCreateOptionsMenu(Menu menu) {
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.main, menu);
         return true;
       } 




       @Override
       public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
         // action with ID action_refresh was selected
         case R.id.Favorites:
           Toast.makeText(this, "Favorites selected", Toast.LENGTH_SHORT)
               .show();
           break;
         // action with ID action_settings was selected

         default:
           break;
         }

         return true;
       } 


}

TabsPagerAdapter.java

package com.erc.library.adapter;

import com.erc.library.Dictionaries;
import com.erc.library.Ebooks;
import com.erc.library.Stories;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class TabsPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int index) {

        switch (index) {
        case 0:

            return new Stories();
        case 1:

            return new Dictionaries();
        case 2:
            // Movies fragment activity
            return new Ebooks();
        }

        return null;
    }

    @Override
    public int getCount() {
        // get item count - equal to number of tabs
        return 3;
    }

}

I'm making a library application in which the navigation is by tabs, the problem is each time I go from the third tab to the first or the first to the third, the tab content is refreshing, I want to prevent the refresh, Any help please ?

Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
Monzer Yaghi
  • 1,300
  • 1
  • 10
  • 21

7 Answers7

142

By default, ViewPager recreates the fragments when you swipe the page. To prevent this, you can try one of three things:

1. In the onCreate() of your fragments, call setRetainInstance(true).

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

3. Use a FragmentPagerAdapter as part of your ViewPager.

If I remember correctly, the second option is more promising. But I urge you to try all three and see which of them work.

Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
  • 3
    Retaining fragment will not work in this case, and retaining is generally a bad practice since it can cause hard to track memory leaks when used without cautious. I will use here a second option, because you are telling to ViewPager - "cache a X fragments for me". And that's it – Gaskoin Feb 13 '15 at 08:04
  • Yes, as you said, the first option should be used judiciously ... although it does work ;) – Yash Sampat Feb 13 '15 at 08:05
  • 1
    @Y.S. I my fragment have 2 tabs , when call fragment at that time 2 tab's fragment call , can i prevent this , like when fragment call first tab 1 is selected so tab 1 show but same time background tab 2 also onCreate also call so can i prevent this - Help please i have problem when i load data from server both tab have different API call so when tab activity call same time both tabs API call – Mansukh Ahir Mar 22 '16 at 14:34
  • 2
    The first method did not worked for me but second option worked perfectly. So thanks guy – Ali.DM Oct 17 '18 at 08:25
  • @YashSampat Hi, these are not working for me as i'm using FragmentStateAdapter and ViewPager2 . Please provide solutions for me as well thank you . – CODAR747 May 27 '23 at 05:01
25

one has to instance the FragmentPagerAdapter first, then .getCount() will return a value -

while .getCount() - 1 should be set as the default off-screen limit:

TabsPagerAdapter adapter = new TabsPagerAdapter(getSupportFragmentManager());

/* the ViewPager requires a minimum of 1 as OffscreenPageLimit */
int limit = (adapter.getCount() > 1 ? adapter.getCount() - 1 : 1);

ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(limit);
Martin Zeitler
  • 1
  • 19
  • 155
  • 216
5

you can handle view recreation by check if the view is null or not

public class FragmentExample extends Fragment {

    private View rootView;

    public FragmentExample() {}

@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {

    if (rootView == null) {

        rootView = inflater.inflate(R.layout.fragment_example_layout, container, false);

        // Initialise your layout here

    } else {
        ((ViewGroup) rootView.getParent()).removeView(rootView);
    }

    return rootView;
    }
}
Aristo Michael
  • 2,166
  • 3
  • 35
  • 43
Ahmad Nasser
  • 295
  • 3
  • 15
3

In the onCreate() of your fragments, call setRetainInstance(true)

Mohsen mokhtari
  • 2,841
  • 1
  • 30
  • 39
  • instead of calling `setRetainInstance(true)` one could use a `FragmentStatePagerAdapter` (it depends what the intended behavior shall be, as the SDK documentaion reads: "the fragment lifecycle will be slightly different when an activity is recreated"). – Martin Zeitler May 29 '17 at 12:56
2

A bit late to this question, but thanks to Y.S., got to know how ViewPager works. I was building an app with 4 tabs and at any point of time, noticed that only two tabs were being refreshed, which I suppose was a default behaviour. After hours of investigation, I understood that Android refreshes multiple tabs to bring in a smooth swiping performance for the user - you might notice that you would have clicked tab2, but android brings in the data for tab3 and keeps it ready.

Though this behaviour is good, it has its pros and cons. Pros - you get a smooth swiping experience, without data being loaded from an external server when you land up in that tab. Cons-your backstack implementation in tabs could go for a toss. When you click a tab, the view pager actually calls smoother tab and you'll end up in a big trouble, if your methods are setting up backarrow (home) at the top left based on what is in the backstack in the clicked tab.

setOffscreenPageLimit is the answer to this. If you want your custom backstack framework to function, and do not want tab3 to be called when tab2 is clicked, you simply need to set the value to the number of tabs. For instance, if you have 4 tabs, set setOffScreePageLimit(4). This would mean that Android would refresh all the 4 fragments initially, which is a bit of a performance overhead (which you should manage properly). But, your backstack and tab switching remain intact. Hope this helps.

1

Since the activity implements ActionBar.TabListener, the activity's onCreate() is getting called again and again. So place the following code in onResume() method:

   // Initilization
    viewPager = (ViewPager) findViewById(R.id.pager);
    actionBar = getActionBar();
    mAdapter = new TabsPagerAdapter(getSupportFragmentManager());

    viewPager.setAdapter(mAdapter);
    actionBar.setHomeButtonEnabled(false);
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);


    // Adding Tabs
    for (String tab_name : tabs) {
        actionBar.addTab(actionBar.newTab().setText(tab_name)
                .setTabListener(this));
    }

    /**
     * on swiping the viewpager make respective tab selected
     * */
    viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageSelected(int position) {
            // on changing the page
            // make respected tab selected
            actionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    });
Nagabhushan Baddi
  • 1,164
  • 10
  • 18
0

In my case above suggestion does not work.

To restrict recreation of fragment, what i did:

In onCreateView you can store inflated view in a global variable and initialize it only if it is null, like in this code:

    var root:View?=null
    var apiDataReceived=false
 override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    // Inflate the layout for this fragment
    if (root==null)
        root=inflater!!.inflate(R.layout.fragment_layout, container, false)
    return root
}

Now if you are parsing some data and fill it into RecyclerView or any other View

  1. Make a global variable like in above code apiDataReceived

  2. Set it to true if you successfully parsed data.

  3. Before apiCalls place a condition like this:

    if (!apiDataReceived) { apiCalls() }

So if apiCalls() would be called only if data is not parsed.

Do your http calls and parsing or any other thing in method which called after onCreateView like onStart

The above code is in kotlin, If you are facing any issue, let me know in comments.

Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46
  • Api data will be flushed after changing page? – Harsh Bhavsar Jan 30 '19 at 07:04
  • @HarshBhavsar then your response in shared preference. – Suraj Vaishnav Jan 30 '19 at 08:26
  • I'm trying to prevent ui update after api call for vertain items (grid items) in gridview. Is it possible to update the rest of the items by holding certain items in gridview. any help will be appreciated. Thanks in adavance. – user7418129 Dec 30 '21 at 04:37
  • @ABHIMANGALMS Not understood the entire question, but if you want partial updates, you can use ListAdapter with DiffUtil instead of RecyclerViewAdapter, and whenever you want to update something in the list, all you need to do is create a new list that contains the updated data need to be shown and pass the new list to ListAdapter. – Suraj Vaishnav Jan 01 '22 at 06:47