I've a ViewPager (Parent ViewPager) in my activity with 4 fragments. One of the fragments implements a sliding ViewPager (Child ViewPager) with automatic sliding.
When I swipe the parent ViewPager to, say, tab 3 and then I go back to the first fragment which contains the sliding ViewPager, some of the slide disappears and some of it stays. When I slide the ViewPager (the child ViewPager) back and forth couple of times, it gets restored to its original state.
I assume the fragment state of the sliding view pager is getting lost when the parent ViewPager is being slid.
Here's the parent ViewPager's adapter:
public class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Here's the child ViewPager's adapter (slider):
class sliding_PageAdapters extends FragmentPagerAdapter {
int mNumOfTabs;
public sliding_PageAdapters(FragmentManager fm, int numTabs) {
super(fm);
this.mNumOfTabs = numTabs;
}
#Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
viewPager.removeView((View) object);
}
#Override
public Fragment getItem(int position) {
Bundle ar = new Bundle();
FragmentHomeImages fragment = new FragmentHomeImages();
switch (position) {
case 0:
ar.putString("number", "0");
fragment.setArguments(ar);
return fragment;
case 1:
ar.putString("number", "1");
fragment.setArguments(ar);
return fragment;
case 2:
ar.putString("number", "2");
fragment.setArguments(ar);
return fragment;
case 3:
ar.putString("number", "3");
fragment.setArguments(ar);
return fragment;
default:
return null;
}
}
#Override
public int getCount() {
return mNumOfTabs;
}
}
The child ViewPager is being called with getChildFragmentManager() here:
sliding_pagerAdapter = new sliding_PageAdapters(getChildFragmentManager(), 4);
And the parent ViewPager is being called with getSupportFragmentManager() here:
adapter = new ViewPagerAdapter(getSupportFragmentManager());
I can think of one problem. On the parent viewpager call setOffscreenPageLimit(limit).
here limit is the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed. So set the appropriate limit based on your use-case.
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(new MyAdapter(fragmentManager));
viewPager.setOffscreenPageLimit(2);
Related
I have viewpager that has 4 fragments.
I have set the offscreenPageLimit to 4 and did not override getItemPosition of the adapter because I don't want them to be re-created every time when I change tabs.
Now, there will be a scenario when I want a particular fragment to be re-created (it means to call onCreateView).
That is before I select or when I select that fragment again.
How to do this?
Please try this adapter. This recover to re-create fragment again.
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private Fragment[] fragments;
private String[] titleList;
private int index;
public ViewPagerAdapter(FragmentManager fm, String[] titleList){ // titleList is list of title of fragments
super(fm);
fragments = new Fragment[titleList.length];
this.titleList = titleList;
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
if (fragments[position] == null){
YourFragment yourFragment = new YourFragment();
fragment = yourFragment;
fragments[position] = fragment;
return fragment;
}else{
return fragments[position];
}
}
#Override
public int getCount() {
return titleList.length;
}
#Override
public CharSequence getPageTitle(int position) {
return titleList[position].toUpperCase();
}
}
I'm using fragmentpageradapter to inflate fragment , this is my coding.
i'm adding cart item in one fragment and showing it to other fragment and all fragment comes in tab layout.My problem is cart item is not refreshing on other fragment while swiping fragment.
public class ProfileDetailePagerAdapter extends FragmentPagerAdapter
{
int TabCount;
HashMap<Integer, String> mFragmentTags;
FragmentManager mFragmentManager;
private boolean isPagingEnabled = true;
public ProfileDetailePagerAdapter(FragmentManager fm, int TabCount) {
super(fm);
this.TabCount = TabCount;
mFragmentTags = new HashMap<Integer, String>();
mFragmentManager = fm;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
MenuFragment tab1 = new MenuFragment();
return tab1;
case 1:
SelectMember_fragment tab2 = new SelectMember_fragment();
return tab2;
case 2:
ViewCart_fragment tab3 = new ViewCart_fragment();
return tab3;
/* case 3:
PaymentMode_fragment tab4=new PaymentMode_fragment();
return tab4;*/
}
return null;
}
#Override
public int getCount() {
return TabCount;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
}
Call FragmentStatePagerAdapter
You should use addOnTabSelectedListener
This method is used to add a listener that will be invoked when tab
selection changes.
tabLayoutOBJ.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
public void onTabReselected(TabLayout.Tab tab) {
}
public void onTabSelected(TabLayout.Tab tab){
String getTAB = (String)tab.getText();
// Do your WORK
}
public void onTabUnselected(TabLayout.Tab tab){
}
});
The reason behind it is the implementation of viewpager. There is a property called offset in ViewPager to make ready fragments next to it up to offset(int) value.
Default value is 1. For example, you are in 1st position, 0th and 2nd positions are also created.
On the other hand if you are in 0th position 1st is the only created fragment. If you swipe to right (going 1st) then 2nd is going to be created.
I assume you are changing a shared object from 1st fragment and want to notify 2nd fragment with changed data. To solve it you need to use listener. The elegant way to solve it is adding observer pattern to your shared object.
Good luck
Emre
I have TabLayout and ViewPager with 2 fragments, each of them is a recyclerView that load data from firebase.
All works fine. But when I change screen orientation ViewPager recreate each fragment and fragments load data again.
Is it possible and how to save fragment state in ViewPager?
But I do not want to override onSaveInstanceState method for creating data set and then restore it. I want something like find fragment by tag when check existing fragment.
Activity
public class MainActivity extends BaseBroadcastActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mViewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(mViewPager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
private void setupViewPager(ViewPager viewPager) {
Adapter adapter = new Adapter(getSupportFragmentManager());
adapter.addFragment(MainListFragment.newInstance(connected, "Notices"), "Notices");
adapter.addFragment(MainListFragment.newInstance(connected, "Posts"), "Posts");
viewPager.setAdapter(adapter);
}
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<>();
private final List<String> mFragmentTitles = new ArrayList<>();
public Adapter(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment, String title) {
mFragments.add(fragment);
mFragmentTitles.add(title);
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position);
}
}
In MainListFragment I only create recyclerView and load data from firebase.
Thank You!
EDIT
Thank You for responding!
I find my error. What's happen when we create FragmentPageAdapter at first time and when we recreate activity:
FragmentPageAdapter call instantiateItem method where it find fragment by tag and if fragment exists adapter use it, but if fragment does not exist adapter call getItem method and create fragment.
In my situation fragments are not recreated, but data is refreshing because I refresh them in onCreateView method in fragment, but now I am edit onCreateView:
if (savedInstanceState == null) {
retrieveData();
}
It should be done in the way that you return new instance always in getItem(). I would also rather use FragmentStatePagerAdapter especially when you have more pages.
You also need to save every internal state that can be lost in onSaveInstanceState(). If you don't want to reload, you'll need to save the data. If you have some data in POJOs, you can add Serializable interface to them to serialize them easily into bundle.
There is also setRetainInstance() on fragment, but it always caused some weird behavior for me, so I'm not using it.
static class Adapter extends FragmentStatePagerAdapter {
public Adapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment=null;
switch (position) {
case 0: fragment=MainListFragment.newInstance(mConnected, "Notices"); //don't forget to save mConnected in savedInstanceState
case 1: fragment=MainListFragment.newInstance(mConnected, "Posts");
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
String title="";
switch (position) {
case 0: title="Notices";
case 1: title="Posts";
}
return title;
}
I have a navigation drawer in which there is a ViewPager that extends a Fragment. When i click the item of drawer i open the viewpager in which there are three fragments. It works perfectly. but if i click again the same drawer item to open the viewpager another one time, the viewpager is empty.. I can see the tabs but not the fragments in there. This is the Viewpager:
public class ViewPagerManager extends Fragment {
public static ViewPagerManager instance = null;
Toolbar toolbar;
public static PagerSlidingTabStrip tabs;
public MyPagerAdapter adapter;
public ViewPager pager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_tabbed, container, false);
instance = this;
adapter = new MyPagerAdapter(getFragmentManager());
pager = (ViewPager)view.findViewById(R.id.pager);
tabs = (PagerSlidingTabStrip)view.findViewById(R.id.tabs);
pager.setAdapter(adapter);
tabs.setViewPager(pager);
pager.setOffscreenPageLimit(3);
adapter.notifyDataSetChanged();
pager.invalidate();
return view;
}
public class MyPagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES = { "One", "Two", "Three" };
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
// Top Rated fragment activity
return new FragmentOne();
case 1:
// Games fragment activity
return new FragmentTwo();
case 2:
// Games fragment activity
return new FragmentThree();
}
return null;
}
}
}
Is it normal? How can i solve? If could help i'm using PagerSlidingTabStrip library.
Use this code
adapter = new MyPagerAdapter(getChildFragmentManager());
instead of
adapter = new MyPagerAdapter(getFragmentManager());
Try to redraw the last selected page when you return to the fragment. I think the viewpager is not cached and you need to reselect the last item. You can override the onresume method.
I have a FragmentList with some items. If I click an item the appropriate Fragment will open which contains a Viewpager. No problem so far but if I go back to the List and click the item again the method getItem(int position) is not called anymore.
Here a link with a similar problem:
Display fragment viewpager within a fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_fragment_runtime, container, false);
mViewPager = (ViewPager) view.findViewById(R.id.pager);
indicator = (TitlePageIndicator) view.findViewById(R.id.indicator);
MyFragmentPagerAdapter mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity().getSupportFragmentManager());
mViewPager.setAdapter(mMyFragmentPagerAdapter);
indicator.setViewPager(mViewPager);
return view;
}
class MyFragmentPagerAdapter extends FragmentStatePagerAdapter implements TitleProvider {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public String getTitle(int position) {
return muxbusTitles[position];
}
#Override
public Fragment getItem(int location) {
return muxbusFragmentList.get(location);
}
#Override
public int getCount() {
return muxbusTitles.length;
}
}
return view;
}
Edit:
If someone is interested,the solution was to extends from PagerAdapter instead of FragmentStatePagerAdpater because the Viewpager is already in a Fragment..
I did have similar problem and after scratching my head for quite some time i made an my own understanding (assumption) that ViewPager is keeping the reference of its child fragments into memory even the hosted Fragment or Activity is destroyed (didn't tested with Activity). So i wrote this method freeFragmentsFromViewPager(). You need to call it from onDestroy() of your Fragment.
/**
* This method free the child fragments from ViewPager as ViewPager keeps the reference
* of child fragments into memory and next time when you again come to visit view pager it won't show the fragment layout
* because it has the reference to the fragment whilst the fragment's view was already destroyed.
*/
private void freeFragmentsFromViewPager()
{
for(int i = 0; i < TabsFragmentPagerAdapter.fragments.length; i++)
{
Fragment fragment = TabsFragmentPagerAdapter.fragments[i];
if(fragment != null)
{
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
TabsFragmentPagerAdapter.fragments[i] = null;
}
}
}
And this is my Adapter code
public class TabsFragmentPagerAdapter extends FragmentPagerAdapter
{
public static final int NUM_TABS = 3;
public static Fragment[] fragments = new Fragment[NUM_TABS];
public TabsFragmentPagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
Log.e("TabsFragmentPagerAdapter", "getItem");
Fragment fragment = TabsFragmentFactory.newInstace(position);
fragments[position] = fragment;
return fragment;
}
#Override
public int getCount()
{
return NUM_TABS;
}
#Override
public CharSequence getPageTitle(int position)
{
return TabsFragmentFactory.getName(position);
}