3

I am using an ActionBarSherlock example to build on. I have 3 fragments (each extends SherlockListFragment and implement LoaderManager.LoaderCallbacks<>), 1 in each tab. They all use loaders. Tabs 2 and 3 use the same loader class (I don't think this is a problem).

I add the tabs to my FragmentPagerAdapter with class references to the different fragments, along with bundles if needed. When my application starts and tab 0 is displayed, getItem in FragmentPagerAdapter gets tab 0 and tab 1 ready by creating their fragments. Tab 2 isn't created yet. When I SWIPE to tab 1, tab 2 is loaded. When I swipe again, tab 2 is displayed correctly.

The problem occurs when I click from tab 0 to tab 2. getItem does create the fragment, and the loader starts its doInBackground processing, however tab 2's fragment never gets the data in onLoadFinished and so sits with the indefinite progressbar.

Something stranger happened earlier. If I clicked, tab 2's onLoadFinished would receive the data from tab 0's Loader. When I changed each tab's fragment Loader to use a different ID (I don't think they should collide even if they have the same ID) that problem went away. Eg: getLoaderManager().initLoader(0, null, this), getLoaderManager().initLoader(1, null, this), etc. I'm not sure if that just illustrates my ignorance of Loaders or something else.

My main question is, can I force the FragmentPagerAdapter to load all the tabs at once? Even doing that probably wouldn't entirely eliminate this bug in my code.

Secondary question: when I swipe around and click refresh (see code below regarding what that does) eventually the refresh buttons in the action bar disappear. Any thoughts?

MainActivity (very similar to linked ABS example):

public class MainActivity extends SherlockFragmentActivity{

    private static final int NUMBER_OF_STREAMS = 9;

    ViewPager mViewPager;
    TabHost mTabHost;
    TabsAdapter mTabsAdapter;
    private JTVApplication mApplication;

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

        // Set up application
        if (mApplication == null)
            this.mApplication = (JTVApplication)getApplication();

        mTabHost = (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setup();

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);

        // SET UP TABS ADAPTER
        mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);     
        mTabsAdapter.addTab(mTabHost.newTabSpec("categories").setIndicator("Categories"), CategoryListFragment.class, null);
        mTabsAdapter.addTab(mTabHost.newTabSpec("top_streams").setIndicator("Top Streams"), StreamListFragment.class, StreamListFragment.instanceBundle(null, null, null, null, NUMBER_OF_STREAMS, 0));
        mTabsAdapter.addTab(mTabHost.newTabSpec("favorites").setIndicator("Favorites"), FavoriteListFragment.class, null);

        if (savedInstanceState != null) {
            mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("tab", mTabHost.getCurrentTabTag());
    }
    /**
     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/pages.
     */
    public static class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {

        private final Context mContext;
        private final TabHost mTabHost;
        private final ViewPager mViewPager;
        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

        static final class TabInfo {
            @SuppressWarnings("unused")
            private final String tag;
            private final Class<?> clss;
            private final Bundle args;

            TabInfo(String _tag, Class<?> _class, Bundle _args) {
                tag = _tag;
                clss = _class;
                args = _args;
            }
        }

        static class DummyTabFactory implements TabHost.TabContentFactory {
            private final Context mContext;

            public DummyTabFactory(Context context) {
                mContext = context;
            }

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


        List<Fragment> fragments = new ArrayList<Fragment>();

        public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
            super(activity.getSupportFragmentManager());
            mContext = activity;
            mTabHost = tabHost;
            mViewPager = pager;
            mTabHost.setOnTabChangedListener(this);
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);   
        }

        public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
            tabSpec.setContent(new DummyTabFactory(mContext));
            String tag = tabSpec.getTag();

            TabInfo info = new TabInfo(tag, clss, args);
            mTabs.add(info);
            mTabHost.addTab(tabSpec);
            notifyDataSetChanged();
        }

        @Override
        public Fragment getItem(int position) {
            TabInfo info = mTabs.get(position);
            return Fragment.instantiate(mContext, info.clss.getName(), info.args);
        }

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

        @Override
        public void onPageScrollStateChanged(int arg0) {}

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

        @Override
        public void onPageSelected(int position) {
            TabWidget widget = mTabHost.getTabWidget();
            int oldFocusability = widget.getDescendantFocusability();
            widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
            mTabHost.setCurrentTab(position);
            widget.setDescendantFocusability(oldFocusability);
        }

        @Override
        public void onTabChanged(String tabId) {
            int newPosition = mTabHost.getCurrentTab();
            mViewPager.setCurrentItem(newPosition, true);
        }


    }
}

Tab0 Fragment (very similar to other Fragments so I will omit the others):

public class CategoryListFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<List<Category>>{

    public List<Category> mCategories;
    public List<Stream> mStreams;
    private int mLimit;
    private CategoryAdapter mCategoryAdapter;
    private String mSearchFilter;
    private RestClient mCategoryClient;

    public List<Category> getCategories() {
        return mCategories;
    }
    public void setCategories(List<Category> categories) {
        this.mCategories = categories;
    }

    public CategoryListFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstance){
        super.onCreate(savedInstance);
        this.setHasOptionsMenu(true);

        if (mCategoryClient == null)
            this.mCategoryClient = JTVApi.getCategories();
        // Prepare the loader
        getLoaderManager().initLoader(0, null, this);
    }

    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
        // CREATE REFRESH BUTTON
        MenuItem refreshMenu = menu.add("Refresh");
        refreshMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        refreshMenu.setIcon(R.drawable.ic_menu_refresh);
        refreshMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
            public boolean onMenuItemClick(MenuItem item) {
                CategoryListFragment.this.setListShown(false);
                CategoryListFragment.this.getLoaderManager().restartLoader(0, null, CategoryListFragment.this);   
                return true;
            }
        });
    }


    @Override
    public void onLoadFinished(Loader<List<Category>> arg0, List<Category> data) {
        if (mCategories == null){
            mCategoryAdapter = new CategoryAdapter(getActivity(), R.id.listItem, data);
            this.setListAdapter(mCategoryAdapter);
        } else {
            mCategoryAdapter.setData(data);
        }
        this.setListShown(true);
    }
    @Override
    public void onLoaderReset(Loader<List<Category>> arg0) {
        // TODO check for null
        mCategoryAdapter.setData(null);
    }
    @Override
    public Loader<List<Category>> onCreateLoader(int arg0, Bundle arg1) {
        return new CategoryLoader(getActivity(), mLimit, mCategoryClient);
    }
}

Tab0 Loader (very similar to other Loaders so I will omit the others):

public class CategoryLoader extends AsyncTaskLoader<List<Category>> {

    JTVApplication mApplication;
    RestClient mRestClient;
    List<Category> mCategories;

    public CategoryLoader(Context context, int limit, RestClient restClient) {
        super(context);
        this.mRestClient = restClient;
        mApplication = (JTVApplication)context.getApplicationContext();
    }

    @Override
    public List<Category> loadInBackground() {
        List<Category> categories = null;
        Object json;
        try {
            json = mApplication.getJSON(mRestClient, mRestClient.bypassCache());
            if (mRestClient.getApiMethod() == APIMethod.CATEGORY_LIST && json instanceof JSONObject){
                JSONObject jsonObject = (JSONObject)json;
                categories = Category.parseCategories(jsonObject);

                // Get total count of viewers for "All Categories"
                int total_viewers = 0;
                for (Category category : categories)
                    total_viewers += category.getViewers_count();
                categories.add(new Category(Category.ALL_CATEGORIES, "All Streams", total_viewers, 0, 0, null, "All Streams"));

                // Update global categories list with this data
                mApplication.setCategories(categories);

                return categories;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Called when there is new data to deliver to the client.  The
     * super class will take care of delivering it; the implementation
     * here just adds a little more logic.
     */
    @Override
    public void deliverResult(List<Category> data) {
        mCategories = data;
        if (isStarted()) {
            super.deliverResult(data);
        }
    }

    /**
     * Handles a request to start the Loader.
     */
    @Override
    protected void onStartLoading() {
        if (mCategories != null){
            // If we currently have a result available, deliver it
            // immediately.
            deliverResult(mCategories);
        }

        // TODO CHECK FOR NEW DATA OR TIMER
        if (mCategories == null){
            forceLoad();
        }
    }

    /**
     * Handles a request to stop the Loader.
     */
    @Override protected void onStopLoading() {
        // Attempt to cancel the current load task if possible.
        cancelLoad();
    }

    /**
     * Handles a request to completely reset the Loader.
     */
    @Override
    protected void onReset() {
        super.onReset();
    }

}

The adapters are very basic and I am confident they aren't the problem. If anyone asks I can provide more code but I think this is adequate. Thanks in advance for any help.

UPDATE:

When I simply replace FragmentPagerAdapter with FragmentStatePagerAdapter it works. I saw it referenced in FragmentPagerAdapter getItem is not called. What is wrong with my code and using FragmentPagerAdapter?

Community
  • 1
  • 1
datu-puti
  • 1,306
  • 14
  • 33

0 Answers0