36

I have following problem: I have one activity in which I have two tabs which are made both as fragments using FragmentPagerAdapter In some moment I would like to change View of one of these fragments, but I am not able to findFragmentById() or findFragmentByTag() I am unable to set id, because I am not declaring Fragment in XML, because of the FragmentPagerAdapter And when I am trying to set tag I am getting following problem:

08-15 21:46:23.805: E/AndroidRuntime(9297): java.lang.IllegalStateException: Can't  change tag of fragment Companies{416d7b78 id=0x7f04002e companies}: was companies now android:switcher:2130968622:1

My code of the pager FragmentPagerAdapter:

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


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

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

    public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mActionBar = activity.getSupportActionBar();
        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 Fragment getItem(int position) {
        TabInfo info = mTabs.get(position);
        FragmentTransaction ft=mContext.getSupportFragmentManager().beginTransaction();
        Fragment fragment=Fragment.instantiate(mContext, info.clss.getName(), info.args);

        if(position==1){
            ft.add(mViewPager.getId(), fragment, "companies");
        }else{
            ft.add(mViewPager.getId(), fragment);
        }
        Log.v("FRAGMENT ID", "fragment tag: "+fragment.getTag());
        ft.commit();
        Log.v("FRAGMENT ID", "fragment tag: "+fragment.getTag());
        return fragment;
    }

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

    @Override
    public void onPageSelected(int position) {
        mActionBar.setSelectedNavigationItem(position);
        new Thread(new Runnable() {

            @Override
            public void run() {
                MainActivity.this.hideKeyboard();

            }
        }).start();

    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

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


    }

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


    }

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

Thank you in advance for your answers.

ziky90
  • 2,627
  • 4
  • 33
  • 47

5 Answers5

51

The Fragments supplied by the FragmentPagerAdapter are auto-tagged when they're instantiated. You can retrieve the tag with this method:

private static String makeFragmentName(int viewPagerId, int index) {
     return "android:switcher:" + viewPagerId + ":" + index;
}

Reference: reusing fragments in a fragmentpageradapter

Community
  • 1
  • 1
DeeV
  • 35,865
  • 9
  • 108
  • 95
  • 17
    Note for people like me: viewId is the id of the ViewPager – htafoya Nov 08 '13 at 18:22
  • I thought the method is put into the class extends `FragmentPagerAdapter`, but the method should be just put in the `Fragment` class. – Robert Jul 20 '15 at 09:07
  • @Robert: Fragments aren't always used in viewpagers but `FragmentPagerAdapter` are always used with Viewpagers. It wouldn't make sense to have it in a `Fragment`. – DeeV Jul 20 '15 at 14:59
  • @DeeV Sorry my fault it's not fragment, it's Activity. In my case there's an `Activity` with `ViewPager` inside. And the `ViewPager` contains an `FragmentPagerAdapter` of 2 fragments. So to reuse fragments I put the method in the `Activity` to `findFragmentByTag()` in the `ViewPager` layout. – Robert Jul 21 '15 at 03:11
  • Why do this? I mean, if you're generating the fragment name from it's position, just get it directly by position. .getSupportFragmentManager().getFragments().get(position) or .getChildFragmentManager().getFragments().get(position) – worked Nov 23 '15 at 18:30
  • This is the right way : http://stackoverflow.com/questions/6976027/reusing-fragments-in-a-fragmentpageradapter/29287415#29287415 – Morteza Rastgoo Dec 27 '15 at 05:37
22

With this function you can find the fragment by pager adapter position.

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

Sample code for v4 support api.

Daniel De León
  • 13,196
  • 5
  • 87
  • 72
6

I found a slightly less gross way to get a handle on a Fragment created by a FragmentPagerAdapter. Instead of imitating the way tags are created, override instantiateItem(), get the Fragment returned by the superclass, and save the tag in TabInfo. This works for me on API >= 15, may work with lower. This still makes some assumptions about private code.

static final class TabInfo {
    private final Class<?> clss;
    private final Bundle args;
    private String tag; // ** add this

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


@Override
public Object instantiateItem(ViewGroup container, int position) {
    final Fragment fragment = (Fragment) super.instantiateItem(container, position);
    final TabInfo info = mTabs.get(position);
    info.tag = fragment.getTag(); // set it here
    return fragment;
}

Or in API < 16, I think instantiateItem() takes a View() instead of ViewGroup(), like this:

public Object instantiateItem(View container, int position);

Then allow a way to get the Fragment, keeping the hack contained.

public Fragment getFragment(int index) {
    return mContext.getFragmentManager().findFragmentByTag(mTabs.get(index).tag);
}

For that to work, the declaration for mContext needs to change to Activity, it's passed in as an Activity anyway:

private final Activity mContext;
biegleux
  • 13,179
  • 11
  • 45
  • 52
nash
  • 61
  • 2
  • 1
    trying to apply this solution, but **super.instantiateItem(container, position)** throws **NPE** for me both FragmentPagerAdapter and FragmentStatePagerAdapter – Sash0k Jan 23 '13 at 05:26
1

A simpler approach to this is to get the Tags directly from the Fragment Manager; like this:

fm.getFragments().get(0).getTag()

You can replace the position, depending on the fragment you need the tag for. Hope this helps others!.

-1

It is very late to answer this question but i have searched a lot no one tell me the exact answer, Might be some one found it useful.

I have set my fragments through FragmentPagerAdapter and i did communication between fragments

https://developer.android.com/training/basics/fragments/communicating.html#Deliver

following this link and for FindFragmentById i simply used viewpager.getId() on the mainactivity .