1

I'm trying to implement a four tab ViewPager as TabHost does not support swiping. The issue here is that I have some Async calls in each of the fragments' OnCreateView and the calls from the next Fragment are made in addition to the one I slide to while using the ViewPager.

With TabHost however, this isn't the case. The calls from only the selected tab are made. Is it an issue with the ViewPager that I should be addressing or am I using the wrong callback method for Fragment?

Here's a partial code from the project:

public class MainActivity extends FragmentActivity implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {

    private TabHost mTabHost;
    private ViewPager mViewPager;
    private HashMap<String, TabInfo> mapTabInfo = new HashMap<String, MainActivity.TabInfo>();
    private PagerAdapter mPagerAdapter;

    private class TabInfo {
        private String tag;
        private Class<?> clss;
        private Bundle args;
        private Fragment fragment;
        TabInfo(String tag, Class<?> clazz, Bundle args) {
            this.tag = tag;
            this.clss = clazz;
            this.args = args;
        }

    }

    class TabFactory implements TabContentFactory {

        private final Context mContext;

        /**
         * @param context
         */
        public TabFactory(Context context) {
            mContext = context;
        }

        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }

    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Inflate the layout
        setContentView(R.layout.activity_main);
        // Initialise the TabHost
        this.initialiseTabHost(savedInstanceState);
        if (savedInstanceState != null) {
            mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); 
        }
        // Intialise ViewPager
        this.intialiseViewPager();
    }

    protected void onSaveInstanceState(Bundle outState) {
        outState.putString("tab", mTabHost.getCurrentTabTag()); //save the tab selected
        super.onSaveInstanceState(outState);
    }

    private void intialiseViewPager() {

        List<Fragment> fragments = new Vector<Fragment>();
        fragments.add(new ActivityFeedFragment()); 
        fragments.add(new InboxFragment());
        fragments.add(new TaskFragment());
        fragments.add(new CalendarEventFragment());
        this.mPagerAdapter  = new PagerAdapter(super.getSupportFragmentManager(), fragments);

        this.mViewPager = (ViewPager)super.findViewById(R.id.viewpager);
        this.mViewPager.setAdapter(this.mPagerAdapter);
        this.mViewPager.setOnPageChangeListener(this);
    }

    private void initialiseTabHost(Bundle args) {
        mTabHost = (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setup();
        TabInfo tabInfo = null;
        MainActivity.AddTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab1").setIndicator("Tab 1"), ( tabInfo = new TabInfo("Tab1", ActivityFeedFragment.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);
        MainActivity.AddTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab2").setIndicator("Tab 2"), ( tabInfo = new TabInfo("Tab2", InboxFragment.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);
        MainActivity.AddTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab3").setIndicator("Tab 3"), ( tabInfo = new TabInfo("Tab3", TaskFragment.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);
        MainActivity.AddTab(this, this.mTabHost, this.mTabHost.newTabSpec("Tab4").setIndicator("Tab 4"), ( tabInfo = new TabInfo("Tab4", CalendarEventFragment.class, args)));
        this.mapTabInfo.put(tabInfo.tag, tabInfo);

        mTabHost.setOnTabChangedListener(this);
    }

    private static void AddTab(MainActivity activity, TabHost tabHost, TabHost.TabSpec tabSpec, TabInfo tabInfo) {
        // Attach a Tab view factory to the spec
        tabSpec.setContent(activity.new TabFactory(activity));
        tabHost.addTab(tabSpec);
    }

    public void onTabChanged(String tag) {
        int pos = this.mTabHost.getCurrentTab();
        this.mViewPager.setCurrentItem(pos, true);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset,
            int positionOffsetPixels) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onPageSelected(int position) {
        // TODO Auto-generated method stub
        this.mTabHost.setCurrentTab(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        // TODO Auto-generated method stub

    }
}

Also, here is the adapter that I am using

public class PagerAdapter extends FragmentStatePagerAdapter {

    private List<Fragment> fragments;     

    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        this.fragments = fragments;
    }

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

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

If there is any suggestion regarding enabling swipe gestures in TabHost I am willing to try that as well.

SVG
  • 123
  • 9

2 Answers2

1

By default ViewPager create and retain one page in addition to the current page, if you don't want that you can setOffscreenPageLimit of the ViewPager to 0

viewPager.setOffscreenPageLimit(0);

--

An alternative solution is to setOnPageChangeListener to the viewpager and fire the Async call when the fragment selected

private static class CustomOnPageChangeListener extends SimpleOnPageChangeListener
{
    @Override
    public void onPageSelected(int position)
    {
        switch(position){
        case 1:
             FirstFragment fragment1 = (FirstFragment) = FirstFragment.getInstance(); 
             fragment1.startAsyncProcess();
        break;
        case 2:
             SecondFragment fragment2 = (SecondFragment) = SecondFragment.getInstance(); 
             fragment2.startAsyncProcess();
        break;
        }

        super.onPageSelected(position);
    }
}

...

viewPager.setOnPageChangeListener(new CustomOnPageChangeListener());
Omar Albelbaisy
  • 827
  • 8
  • 15
  • I've tried that before, unfortunately it did not work. – SVG Jul 18 '14 at 07:09
  • That's weird! I have updated the answer and alternative solution added, check it, I hope it works with you – Omar Albelbaisy Jul 18 '14 at 07:38
  • 1
    @SVG for a more complete example check out my answer on this question: http://stackoverflow.com/questions/21914808/how-to-handle-asynctasks-in-actionbaractivity-fragments-when-viewpager-is-used/24386516#24386516 – LordRaydenMK Jul 18 '14 at 07:49
  • @LordRaydenMK thanks, so I'll have to move all my async stuff to onResume if I use that solution? – SVG Jul 18 '14 at 09:52
  • @OmarAlbelbaisy Will check out the new solution, but can you tell me if there is any particular callback method that I should ideally be using in such cases? – SVG Jul 18 '14 at 09:58
  • It's not activity's onResume method, you need to create your own method in the fragment and name it whatever you want (startAsyncProcess in my answer) and put the async call in that method then call it from the OnPageChangeListener – Omar Albelbaisy Jul 18 '14 at 10:00
  • 1
    In @LordRaydenMK answer he create an interface and implement it on each fragment and override the two abstract methods onPauseFragment and onResumeFragment and every things else almost the same as my alternative solution, so you can your async call in onResumeFragment if you will use his code – Omar Albelbaisy Jul 18 '14 at 10:06
0

ViewPager loads the previous and next fragment in addition to the current fragment to provide smooth user experience. Therefore ur asynctasks from other fragments are getting called.

Wat i suggest u do is keep public static boolean variables in each fragment and in ur OnPageSelected method u can set these variables.

Then in ur onResume method of each method check if the value of this variable is true and then only execute ur asynctask.

If u do not prefer static variables then access ur fragments using fragment manager and set the values of booleans

vishnus
  • 728
  • 2
  • 7
  • 20
  • Will try with static variables for the time being. – SVG Jul 18 '14 at 07:10
  • No, not sure how to go about this anymore as I have tried a lot of things today, the onResume method also runs the same way. Will try it out again tonight – SVG Jul 18 '14 at 13:08
  • Suppose in ur onPageSelected method u get position as 0 then set boolean for 1st fragment to true and for the rest of the fragments set it to false. Similarly when position is 1 set to true for 1st fragment and rest should be false. – vishnus Jul 18 '14 at 13:14
  • Will let you know how that goes, don't have access to the code at home – SVG Jul 20 '14 at 06:59