1

Here's the structure of my App

HomeActivity replaces the contentArea with either TransScreen or FlowScreen. Both are Fragments (support v4).

  • HomeActivity (drawerlayout containing contentArea frameLayout + menu ListView)
    • TransScreen (fragment with viewPager + actionbar tabs)
      • InListFragment (ListFragment)
      • OutListFragment (ListFragment)
    • FlowScreen (fragment)

When the app starts up, TransScreen is the default screen - I see the lists - all good here. Switch from Drawer menu to FlowScreen, that's fine too. Now when I switch back to TransScreen, I see the tabs but blank content in the ViewPager (not the 'empty' message or the listView).

Tried debugging, on the first render of the TransScreen, I see the getItem method of the ViewPager's adapter (derived from FragmentStatePagerAdapter) is called (call stack starts from ViewPager.populate()) But when I switch to another screen and back again, TransScreen::onCreateView() is called but the subsequent getItem calls are not being triggered.

Here's what TransScreen onCreateView looks like:

pseudo code onCreateView()

_tabsPager = new ViewPager(activity); // from getActivity()
_tabsPager.setId(R.id.transactionsTabCarousel);

_tabsPager.setAdapter(_tabsAdapter);
            _tabsPager.setOnPageChangeListener(syncSelectedActionBarTabOnPageSelectListener());

ActionBar actionBar = activity.getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);


TabListener tabListener = getSyncPagerOnTabSelectListener();
actionBar.addTab(createActionTab(actionBar, tab1Text, tabListener));
actionBar.addTab(createActionTab(actionBar, tab2Text, tabListener));

Log.d(TAG, "onCreate end!");

return _tabsPager;

_tabsAdapter is created in onCreate() and does a lookup in an internal Fragment array during getItem(position). However getItem only seems to be called the first time and not after I switch to this Screen via the drawerLayout - pullout menu.

public TransactionTabsAdapter(FragmentManager fm) {
        super(fm);
        try {
            _fragments = new Fragment[2];
            _fragments[0] = new InListFragment();
            _fragments[1] = new OutListFragment();
        } catch (Exception e) {
            Log.e(TAG, "TabsAdapter ctor", e);
        }

    }

No exceptions in LogCat.

Tried passing in getChildFragmentManager to the tabsAdapter inside first level fragment TransScreen. No dice.


Another update:

So to eliminate the possibility that I messed up in TransScreen. I downloaded the code for the following android sample http://developer.android.com/samples/SlidingTabsColors/index.html

I then substituted TransScreen with SlidingTabsColorFragment()

HomeActivity:onCreate

...
        _screens = new Fragment[]{ 
new SlidingTabsColorsFragment(), //new TransactionsScreen()
new CashFlowScreen() };
        setScreen(0);
    }

    private void setScreen(int position) {
        try {
            FragmentManager fm = getSupportFragmentManager();
            fm.beginTransaction()
                .replace(R.id.contentArea, _screens[position])
                .commit();
            _drawerMenu.setItemChecked(position, true);
            _drawerLayout.closeDrawer(_drawerMenu);
        } catch (Exception e) {
            Log.e(TAG, "setScreen via Drawer Menu", e);
        }
    }

Same problem reproducible - and with a stack trace

: threadid=1: thread exiting with uncaught exception (group=0x41be52a0)
: FATAL EXCEPTION: main
: java.lang.IllegalStateException: No activity
:   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1091)
:   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1086)
:   at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1884)
:   at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1514)
:   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947)
:   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
:   at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
:   at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
:   at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
:   at android.os.Handler.handleCallback(Handler.java:615)
:   at android.os.Handler.dispatchMessage(Handler.java:92)
:   at android.os.Looper.loop(Looper.java:137)
:   at android.app.ActivityThread.main(ActivityThread.java:4921)
:   at java.lang.reflect.Method.invokeNative(Native Method)
:   at java.lang.reflect.Method.invoke(Method.java:511)
:   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038)
:   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)
:   at dalvik.system.NativeStart.main(Native Method)
Gishu
  • 134,492
  • 47
  • 225
  • 308

1 Answers1

1

So the answer seems to be : Don't use ViewPager with nested fragments - it doesn't work.

So I looked at an app that had what I wanted.. basically a simple NavDrawer which allows you to jump to different screens, one or more of which could contain a viewpager. So I looked at the Google IO App source.

Lessons learned:.

  1. Use activities for different screens - I was using fragments. When you click on a nav drawer option, spawn the new activity. Suppress default animations + use fade in/out to obtain a less jarring transition.
  2. So now for the screen, we have a ViewPager with single level fragments. (as opposed to a fragment containing a viewpager with tab child fragments)
  3. It seems SlidingTabLayout is preferred over ActionBar tabs (which is what i was using)

The issue isn't solved. With these changes, it's a good enough workaround

Gishu
  • 134,492
  • 47
  • 225
  • 308