27

I,m using slidingTabLayout with 4 tabs and a viewpager and 4 fragment class for every tab.

i need to reload one of my fragments (first tab) in my project when user click some button, and for this i need to have a tag for that fragment

my question is : how can i set a tag for one of viewpagers fragment???

My MainActivity tab and viewpager codes:

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener {



Toolbar toolbar;
ViewPager pager;
ViewPagerAdapter adapter;
SlidingTabLayout tabs;
CharSequence Titles[]={"نوع","قیمت","برند","همه"};
int Numboftabs = 4;




@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);




    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "درج آگهی رایگان", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });



    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);




    // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
    adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs);

    // Assigning ViewPager View and setting the adapter
    pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(adapter);

    // Assiging the Sliding Tab Layout View
    tabs = (SlidingTabLayout) findViewById(R.id.tabs);
    tabs.setDistributeEvenly(true); // To make the Tabs Fixed set this true, This makes the tabs Space Evenly in Available width

    // Setting Custom Color for the Scroll bar indicator of the Tab View
    tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
        @Override
        public int getIndicatorColor(int position) {
            return getResources().getColor(R.color.colorWhite);
        }
    });

    // Setting the ViewPager For the SlidingTabsLayout
    tabs.setViewPager(pager);
    pager.setCurrentItem(3);




} and ....

Thank you a lot

K1-Aria
  • 1,093
  • 4
  • 21
  • 34
  • if you want to use fragments already in viewpager, you can use adapter's getItem(position) method. and for tags, i think you should pass the tags in Constructor of Adapter and then set to each fragments. – Sandip Fichadiya Jan 18 '16 at 18:12
  • thank you my friend , but can you explain me more about passing the tags to adapter? thank you – K1-Aria Jan 18 '16 at 19:28

4 Answers4

33

There are couple of solution for this.

1). When the FragmentPagerAdapter adds a Fragment to the FragmentManager, it uses a special tag based on the particular position that the Fragment will be placed. So you can simply get the current visible Fragment in your ViewPager using these lines of code

 Fragment fragment = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
 // based on the current position you can then cast the page to the correct Fragment class and call some method inside that fragment to reload the data:
 if (0 == ViewPager.getCurrentItem() && null != fragment) {
      ((TabFragment1)fragment).reloadFragmentData();     
 } 

2). You should keep track of all the active fragments in the FragmentStatePagerAdapter. For demo sample you can refer to second approach here

3). You can make use of EventBus to post events to your Fragment from anywhere. All you have to do is to register your Fragment for that specific event. For official documentation refer this

Mukesh Rana
  • 4,051
  • 3
  • 27
  • 39
  • but i just used ViewPagerAdapter . what should i do? – K1-Aria Jan 18 '16 at 20:36
  • your ViewPagerAdapter may be extending FragmentPagerAdapter or FragmentStatePagerAdapter. isn't it? – Mukesh Rana Jan 19 '16 at 05:36
  • no i,m not used it never!!!! is there any other wat to set tags to fragment? idont know why its so difficult!!! – K1-Aria Jan 19 '16 at 14:49
  • you can go with the third solution then, its a universal solution and will work for sure without any issues. – Mukesh Rana Jan 19 '16 at 17:50
  • aha! Thanks . now with EventBus i can Tag fragments?? or i can refresh Them?? – K1-Aria Jan 19 '16 at 19:17
  • you can refresh them. You don't need to set any tag. You only have to register your Fragment for reload event that you can post from anywhere – Mukesh Rana Jan 19 '16 at 19:18
  • have you any tutorial or example about this issuse?? – K1-Aria Jan 19 '16 at 22:07
  • Although the official documentation has already the sample for that but you can let me know your specific requirement mean to say where that button is present etc. , I'll let you know how you can proceed further – Mukesh Rana Jan 20 '16 at 05:48
  • that button was an example to explain my issuse , actually i want to refresh the fragment when user swipe down page with swipe refresh layout – K1-Aria Jan 20 '16 at 13:49
  • and where is that swipe refresh layout? in the same Fragment itself? – Mukesh Rana Jan 20 '16 at 18:40
  • Yes its exactly in that fragment,s XML which i want to refresh that – K1-Aria Jan 21 '16 at 00:58
  • Then what's the use of getting Fragment Tag and reloading that, you can simply refresh the fragment data by creating a function to refresh data and call that function in your Fragment itself when SwipeRefresh completes. Morevover, if you still want to reload the whole Fragment , I do have one more solution. Let me know if you want that as well. – Mukesh Rana Jan 21 '16 at 05:16
  • yes my friend , exactly i want to refresh my recyclerview items from db and JSON , and i write my getdata() method in swipe refresh layout , on refresh listener , but the problem is this : my list will not update with new items , this is my main question : http://stackoverflow.com/questions/34843354/refresh-recyclerview-items-in-a-fragment – K1-Aria Jan 21 '16 at 12:01
  • hey sorry for such late reply, so what actual problem you are facing now? – Mukesh Rana Jan 24 '16 at 08:13
  • actually i want refresh the fragment , and really i think this eventbus is best way. so about refreshing , have you any tutorial?? really thank you – K1-Aria Jan 24 '16 at 10:07
  • official github link for EventBus already have the sample example as well.. But I think if you are doing everything in your Fragment i.e using SwipeRefreshLayout, then I don't think there is any need of using EventBus – Mukesh Rana Jan 24 '16 at 10:34
  • my fragment returns null always – abh22ishek Jun 28 '19 at 08:21
  • @abh22ishek make sure your viewpager adapter is extending FragmentPagerAdapter if you are going with the 1st solution. – Mukesh Rana Jun 28 '19 at 09:55
11

Here is the method which is used by FragmentPagerAdapter to create the Fragment's tag name:

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

viewId is the id of the ViewPager.
id is the position of the fragment inside the ViewPager.

Example:

String fragmentTag = makeFragmentName(myViewPager.getId(), 2);
Fragment fragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
vovahost
  • 34,185
  • 17
  • 113
  • 116
  • Worked perfectly, thanks! Note for some users: make sure you are using the correct method to get the Fragment Manager. I used ```getChildFragmentManager```. – DIRTY DAVE Nov 13 '21 at 04:00
3

In case anyone's using FragmentStateAdapter, the tag is a bit different. This is the code from FragmentStateAdapter:

mFragmentManager.beginTransaction()
    .add(fragment, "f" + holder.getItemId())
    .setMaxLifecycle(fragment, STARTED)
    .commitNow();

As you can see it's a combination of static string "f" and getItemId(). After overriding fun getItemId(position: Int): Long in your adapter, finding a specific fragment should be quite simple to do. For example, override fun

getItemId(position: Int): Long {
    return if(position == 0) {
            "my_first_fragment"
    }else "my_second_fragment"
}

The tag for our first fragment would be "fmy_first_fragment" and found with childFragmentManager.findFragmentByTag("fmy_first_fragment") as? MyFirstFragment

Careful whenever you look for fragment, you have to use the same FragmentManager as was used to create it. So I recommend creating your adapter with FragmentManager and LifeCycle as a constructor.

Jakub Kostka
  • 551
  • 2
  • 7
  • 20
  • There is no need to make such if-else. You can create custom method and just call: `return "f" + super.getItemId(position);}`. – deadfish Mar 02 '23 at 19:18
  • Then whenever you want to call your method, you've got to parse the adapter as (ViewPager.adapter as? MyAdapter)?.myCustomMethod(). Not really a fan of that, but that's a matter of a preference. My post is mainly about how it works with the ViewPager2 which is a bit different from others' propositions. Thanks for the addition, it is a valid point. Feel free to propose edit on my post and it can have both ways of approach. – Jakub Kostka Mar 03 '23 at 22:09
0

Though this question has already been answered. I have another approach for achiving this and this might be better one as the way fragments are tagged in the code are subjected to change. Also to point out using adapter.getItem(position) does not return the instance of fragment being displayed, it will depend on our own implementation of getItem method.

So Basically the problem is that we do not have the instance of Fragments that are present in the viewPager. The idea is to override method

public Object instantiateItem(ViewGroup container, int position) {
    // If we already have this item instantiated, there is nothing
    // to do.  This can happen when we are restoring the entire pager
    // from its saved state, where the fragment manager has already
    // taken care of restoring the fragments we previously had instantiated.
    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    Fragment fragment = getItem(position);
    if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
    if (mSavedState.size() > position) {
        Fragment.SavedState fss = mSavedState.get(position);
        if (fss != null) {
            fragment.setInitialSavedState(fss);
        }
    }
    while (mFragments.size() <= position) {
        mFragments.add(null);
    }
    fragment.setMenuVisibility(false);
    fragment.setUserVisibleHint(false);
    mFragments.set(position, fragment);
    mCurTransaction.add(container.getId(), fragment);

    return fragment;
}

of FragmentStatePagerAdapter. If we look at the default implementation of this method, it basically does the job of instantiating new Fragments if they do not exists and returning them. Also it stores them in a list of fragments which is private. So what we can do is Override this method and store the instances on fragments in our own list. This can be implemented in the following way

private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<Fragment> fragmentList;

    private final int NUM_PAGES;

    public ScreenSlidePagerAdapter(FragmentManager fm, int numPages) {
        super(fm);
        NUM_PAGES = numPages;
        fragmentList = new ArrayList<>(NUM_PAGES);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = new CustomFragment();
        //this method can be modified according to the requirement
        return fragment;
    }

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

    public Fragment getFragment(int position){
        return fragmentList.get(position);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position){
        Fragment fragment = (Fragment) super.instantiateItem(container,position);
        if(fragmentList.size()<NUM_PAGES&&!fragmentList.contains(fragment)){
            fragmentList.add(fragment);
        }
        return fragment;
    }
}

Therefore we will have the instances of all the fragments available with us we would no longer need any tags to find the fragment instances being displayed to the user.