简体   繁体   中英

Android, How to mix ActionBar.Tab + View Pager + ListFragment

I've been trying to test how to mix all these things together and I'm having problems!! I just want an app with three tabs using the ActionBar.Tab. For example, this tabs can be movies genres Action, Adventure and Animation, the user can swipe through the tabs, so it will use the ViewPager and each tab will show a list of movies of that genre. There's no need to have three different fragments classes because all tabs will be the same format a simple list. And I'm having problems because when I select the second tab, the position for onPageSelected is 1,

mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCollectionPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
     public void onPageSelected(int position) {
        mActionBar.setSelectedNavigationItem(position);
     }
});

This causes the call to the method public Fragment getItem(int i) inside the CollectionPagerAdapter class, but then the value of i is 2 NOT 1, so then it calls the createView for the TabFragment class with a value of 2 NOT 1, so tabs are not refreshing successfully.

Any help will be really appreciated!!

Code to create the tabs,

// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mCollectionPagerAdapter.getCount(); i++) {
        mActionBar.addTab(mActionBar.newTab()
            .setText(mGenres.get(i).getName()) 
            .setTabListener(this));

        //Let's request the movies for the first three genres
        new GetMoviesByGenre().execute(mGenres.get(i).getId());         
}

When a tab is selected,

@Override
public void onTabSelected(ActionBar.Tab tab, android.app.FragmentTransaction arg1) {

//Let's update the dataset for the selected genre
TabFragment fragment = 
      (TabFragment) getSupportFragmentManager().findFragmentByTag(
                   "android:switcher:"+R.id.pager+":"+tab.getPosition());
     if(fragment != null)  // could be null if not instantiated yet
     {
        if(fragment.getView() != null) 
        {
           fragment.updateDisplay(tab.getPosition()); // do what updates are required
        }
     }

  mViewPager.setCurrentItem(tab.getPosition());
}

CollectionPageAdapter class

public class CollectionPagerAdapter extends FragmentPagerAdapter {

final int NUM_ITEMS = 3; // number of tabs

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

public Fragment getItem(int pos) {
  return fragments.get(pos);
}

public void addFragment(Fragment f) {
  fragments.add(f);
}

public CollectionPagerAdapter(FragmentManager fm) {
    super(fm);

    //Let's add  the fragments
    for (int i=0;i<NUM_ITEMS;i++)
    {
        Fragment fragment = new TabFragment();
        Bundle args = new Bundle();
        args.putInt(TabFragment.ARG_OBJECT, 0);
        fragment.setArguments(args);
        addFragment (fragment);
    }
}

@Override
public int getCount() {
    return NUM_ITEMS;
}

}

TabFragment class

public class TabFragment extends ListFragment {

public static final String ARG_OBJECT = "object";

private MoviesAdapter m_Adapter;
private ArrayList <Movie> mMovies = new ArrayList<Movie>();

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

  // you only need to instantiate these the first time your fragment is
  // created; then, the method above will do the rest
  if (m_Adapter == null) {
      m_Adapter = new MoviesAdapter(getActivity(), mMovies);
  }
  setListAdapter(m_Adapter);
}

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

    int position = getArguments().getInt(ARG_OBJECT); // to check is the right fragment
    View rootView = inflater.inflate(R.layout.tabs, container, false);
    return rootView;
}

public void updateDisplay (int type)
{
     GlobalVars gv = (GlobalVars)getActivity().getApplicationContext();

    switch (type)
    {
        case 0:
            mMovies = gv.getActionMovies();
            break;
        case 1:
            mMovies = gv.getAdventureMovies();
            break;
        case 2:
            mMovies = gv.getAnimationMovies();
            break;
    }

    m_Adapter.notifyDataSetChanged();
}

}

I don't what I'm doing wrong, I guess that the fragments are messed up, because when I press the second tab, data from the first tab is updated, and so on ...

Thanks!

Instead of using CollectionPageAdapter, I changed to use the TabsAdapter class shown in Android documentation of ViewPager and it works!

public class TabsAdapter extends FragmentPagerAdapter
    implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

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

static final class TabInfo {
    private final Class<?> clss;
    private final Bundle args;

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

public TabsAdapter(FragmentActivity activity, ViewPager pager) {
    super(activity.getSupportFragmentManager());
    mContext = activity;
    mActionBar = activity.getActionBar();
    mViewPager = pager;
    mViewPager.setAdapter(this);
    mViewPager.setOnPageChangeListener(this);
}

public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
    TabInfo info = new TabInfo(clss, args);
    tab.setTag(info);
    tab.setTabListener(this);
    mTabs.add(info);
    mActionBar.addTab(tab);
    notifyDataSetChanged();
}

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

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
}

@Override
public Fragment getItem(int position) {

    TabInfo info = mTabs.get(position);
    Fragment fr = Fragment.instantiate(mContext, info.clss.getName(), info.args);
    //addFragment (fr, position);
    return  fr;

}

public void addFragment(Fragment f, int location) {

    if (fragments.size() == 0)
        fragments.add(f);
    else
        fragments.add(location, f);
  }

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}

@Override
public void onPageSelected(int position) {
    mActionBar.setSelectedNavigationItem(position);
}

@Override
public void onPageScrollStateChanged(int state) {
}

@Override
public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
    Object tag = tab.getTag();
    for (int i=0; i<mTabs.size(); i++) {
        if (mTabs.get(i) == tag) {
            updateDatasetMovies (i);
            mViewPager.setCurrentItem(i);
        }
    }
}



@Override
public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
    // TODO Auto-generated method stub

}

@Override
public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
    // TODO Auto-generated method stub

}


public void updateDatasetMovies (int pos)
{
    //Let's update the dataset for the selected genre
    TabFragment fragment = 
          (TabFragment) ((FragmentActivity)mContext).getSupportFragmentManager().findFragmentByTag(
                       "android:switcher:"+R.id.pager+":"+pos);

    //TabFragment fragment = (TabFragment) getItem(pos);
    if(fragment != null)  // could be null if not instantiated yet
      {
         if(fragment.getView() != null) 
         {
            // no need to call if fragment's onDestroyView() 
            //has since been called.
            fragment.updateDisplay(pos); // do what updates are required
         }
      }

}

}`

I agree with nirvik on this, however there is one cruciual improvement that should be made. This implementation has one flaw - when swiping to a next fragment the onPageSelected method is invoked, which in turn invokes onTabSelected method by calling mActionBar.setSelectedNavigationItem(position);. This causes a flicker in the animation. This:

viewPager.setCurrentItem(i);

should be replaced by this:

if(i != viewPager.getCurrentItem()) {
     viewPager.setCurrentItem(i);
}

resulting in a smooth transition.

You can try including the following code in your FragmentPagerAdapter and see if this addresses the issue.

public int getItemPosition(Object object) {
    return POSITION_NONE;
}

You could also try using a FragmentStatePagerAdapter .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM