I know this question has already been asked, more than once, but I think I'm looking for a different kind of "skip".
In a ViewPager linked with a TabLayout:
ViewPager vp = (ViewPager) findViewById(...);
TabLayout tl = (TabLayout) findViewById(...);
vp.setAdapter(new NoSwipePagerAdapter());
tl.setupWithViewPager(vp);
Since the viewpager is not scrollable, the expected behavior would be like: if tap on the "A" tab, viewpager scrolls to that tab.
My problem is that if I'm on "A" and I tap "D", the viewpager animate through B and C. What I'm trying to do is to literally "skip" B and C and show D.
Mainly for a matter of performance, since B and C are heavy fragments, but also for a matter of design.
I've tried every combination of setCurrentItem(int index, boolean smoothScroll), and it didn't work.
Is it possible to skip ViewPager pages? Thanks in advance.
if you want to skip two fragments and directly go to fourth fragment then write the following line
ViewPager.setCurrentItem(3);
I think, you should create PagerAdapter like bellow
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new Fragment_PatientList();
break;
case 1:
fragment = new FragmentB();
break;
case 2:
fragment = new FragmentC();
break;
default:
return null;
}
return fragment;
}
#Override
public int getCount() {
return mNumOfTabs;
}
}
After creating pagerAdapter ,set viewPager.setAdapter(adapter)
At the end, you can connect tablayout with your viewpager like this
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
Related
actually i have 4 fragments in my app and i switch the fragments by swiping left or right. I used view pager for swiping the fragments are swiping perfectly but there is a problem if i swipe and fragment B shows but the backend functionality of fragment C runs. If i go to C then backend functionality of D runs. At fragment A first backend functionality of fragment A runs then it automatically shifted to fragment B but front end view is of fragment A
This is adapter
public class TabsPagerAdapter extends FragmentPagerAdapter {
private int mNumTabs;
public TabsPagerAdapter(FragmentManager fm, int numTabs) {
super(fm);
this.mNumTabs= numTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
PersonalSettings tab0 = new PersonalSettings();
return tab0;
case 1:
Health tab1 = new Health();
return tab1;
case 2:
Statistics tab2 = new Statistics();
return tab2;
case 3:
Motivation tab3 = new Motivation();
return tab3;
default:
return null;
}
}
#Override
public int getCount() {
return mNumTabs;
}
}
This is main activity
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager(),4);
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
If u want further code i will give u
It because the viewpager will preload the item next to the current item to improve UX (in your case, the item is the fragment)
You can set offscreenPageLimit to ViewPager to limit it:
viewPager.setOffscreenPageLimit(1);
But if you do this, you will found the fragment will reload everytime you enter, IMO this is not a good practice.
Override destroyItem method in FragmentpagerAdapter
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
// remove your fragment obj from here
}
So i have the next problem with FragmentPagerAdapter. I have a TabLayout with 3 tabs representing 3 fragments that i can switch. So when i switch to third fragment, for some reason the first one disappears (or its view). Does anyone know how to fix this problem? Thanks in advance.
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private static final int FRAGMENT_COUNT = 3;
private final List<Fragment> listOfFragments = new ArrayList<>();
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return listOfFragments.get(position);
}
#Override
public int getCount() {
return FRAGMENT_COUNT;
}
public void addFragment(Fragment fragment) {
listOfFragments.add(fragment);
}
}
This is the code for FragmentPagerAdapter.
And i think i fixed this :) Only thing i did is to override the FragmentPagerAdapter destroyItem() method, with empty body (no super).
I don't know what is your problem. but if in your app only 3 fragments are there than you can try setOffscreenPageLimit method of ViewPager
ViewPager.setOffscreenPageLimit(3);
One of the problems here is a slightly confusing API.
In FragmentPagerAdapter, getItem(int position) actually means "create item". In other words, you shouldn't try to manually cache the Fragments inside a list in your Adapter.
#Override
public Fragment getItem(int position) {
return listOfFragments.get(position); //No! don't do this
}
Even though it may see counter-intuitive, you should actually create a new instance of the Fragment you want inside getItem, sticking very closely to the official Google example:
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return FirstFragment.getInstance();
case 1:
return SecondFragment.getInstance();
}
}
Otherwise you will run into problems where the FragmentManager's cache and your own List<Fragment> cache are out-of-sync. In short, caching of Fragments is handled by the FragmentManager and you don't need to roll-your-own caching.
I don't know really what happen but this is my code, hope this help you
public class AdapterFragmentViewPager extends FragmentPagerAdapter {
private List<Fragment> fragmentList = new ArrayList<>();
public AdapterFragmentViewPager(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment) {
fragmentList.add(fragment);
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public int getCount() {
return fragmentList.size();
}
}
and this is my Tablayout:
private AdapterFragmentViewPager adapterFragmentViewPager = new AdapterFragmentViewPager(getSupportFragmentManager());
TabLayout.Tab home = tabLayout.newTab();
tabLayout.addTab(home);
TabLayout.Tab newest = tabLayout.newTab();
tabLayout.addTab(newest);
TabLayout.Tab author = tabLayout.newTab();
tabLayout.addTab(author);
TabLayout.Tab category = tabLayout.newTab();
tabLayout.addTab(category);
TabLayout.Tab saved = tabLayout.newTab();
tabLayout.addTab(saved);
adapterFragmentViewPager.addFragment(FragmentHome.newInstance());
adapterFragmentViewPager.addFragment(FragmentQuote.newInstance());
adapterFragmentViewPager.addFragment(FragmentAuthor.newInstance());
adapterFragmentViewPager.addFragment(FragmentCategory.newInstance());
adapterFragmentViewPager.addFragment(FragmentFavorites.newInstance());
viewPager.setAdapter(adapterFragmentViewPager);
tabLayout.setupWithViewPager(viewPager);
If I use a TabLayout with FragmentPagerAdapter, the overridden function getItem(int position) just return one fragment. In case of lager screen I would like to have 2 fragments to be returned.
How can I do it? should wrap these 2 fragments into one fragment and use that instead or is there any better solution?
FragmentPagerAdapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: //Ingredients
return IngredientsFragment.newInstance(mRecipe);
case 1: // Details
{
// TODO Here instead of DetailFragment I want to return
// two fragments called DetailFragment and StepFragment.
return DetailFragment.newInstance(mRecipe);
}
default:
throw new RuntimeException(this.toString() + " Wrong fragment!");
}
}
And then in my Activity onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
// Initializing, etc.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = findViewById(R.id.tabs);
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
}
I do not see you have much of a wiggle room here.
These two options are exlusive:
You are returning one Fragment with two nested fragments (Wrapped fragment)
You are returning two fragments each anchored to his own tab.
If you would like two fragments to appear on singular page of ViewPager, you have no choice but to wrap them.
Otherwise swiping would go between these two fragments, which is the same as if they are completely different, that is non-correlated.
OK I managed to solve the problem using a Fragment consist of two other fragments as children.
Just do not forget in this case the FragmentManager object should be populated with getChildFragmentManager() to work properly.
For more info look at my BakingApp project DetailStepWideScreenFragment.java
GitHub Repos
Fragment Wrapper:
public class DetailStepWideScreenFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_detail_step_wide_screen, container, false);
// I added the fragments here. StepFragment can be replaced using
// replaceStepFragment function.
DetailFragment detailFragment = DetailFragment.newInstance(mRecipe);
StepFragment stepFragment = StepFragment.newInstance(mRecipe, mStepId);
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.fl_detail_fragment_wide_screen, detailFragment);
transaction.add(R.id.fl_step_fragment_wide_screen, stepFragment);
transaction.commit();
return view;
}
public void replaceStepFragment(String stepId) {
mStepId = stepId;
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
StepFragment stepFragment = StepFragment.newInstance(mRecipe, mStepId);
transaction.replace(R.id.fl_step_fragment_wide_screen, stepFragment);
transaction.commit();
}
}
PagerAdapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private DetailStepWideScreenFragment currentFragment;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
currentFragment = null;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: //Ingredients
return IngredientsFragment.newInstance(mRecipe);
case 1: // Details
{
// Show base on screen size.
if (mIsLargeScreen) {
DetailStepWideScreenFragment detailStepWideScreenFragment = DetailStepWideScreenFragment.newInstance(mRecipe, "0");
currentFragment = detailStepWideScreenFragment;
return detailStepWideScreenFragment;
} else {
return DetailFragment.newInstance(mRecipe);
}
}
default:
throw new RuntimeException(this.toString() + " Wrong fragment!");
}
}
#Override
public int getCount() { return 2; }
public DetailStepWideScreenFragment getCurrentFragment() {
return currentFragment;
}
}
I have a simple FragmentView in my app and 3 tabs. All 3 tabs have RecyclerView, the problem is first two tabs are not that complex, while third tab uses some complex operations and slows down the app making it lag while it's doing those operations. My question is: How can I prevent FragmentView to call onCreateView until it's focused?
Right now the third tab starts doing those operations when second tab is focused. I want it to start doing those methods when third tab is focused.
This is my code:
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText("Tab1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab3"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
viewPager = (ViewPager) findViewById(R.id.pager);
adapter = new PagerAdapter
(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
and this is my adapter:
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
RingtonesActivityFragment Ringtones = new RingtonesActivityFragment();
return Ringtones;
case 1:
FavoritesActivityFragment Favorites = new FavoritesActivityFragment();
return Favorites;
case 2:
MySongsActivityFragment Songs = new MySongsActivityFragment();
return Songs;
default:
return null;
}
}
#Override
public int getCount() {
return mNumOfTabs;
}
}
and Tabs:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main, container, false);
...
...
...
return rootView;
}
ViewPager will load 1 page to either side of the current page by default. Even if you set setOffscreenPageLimit to less than 1, it'll reset itself to 1. There is no getting around this unless you modify the ViewPager class to your liking.
To help with lazily executing code you can use ViewPager.OnPageChangeListener and its method onPageSelected(int position) to find out which tab is currently in focus. You can then postpone the work required once the Fragment is in focus.
I'd also add that if you're doing work which causes lag in your UI, you might want to consider moving the work to a background thread. If the work is layout related and you can't do it on a background thread use Hierarchy Viewer or Systrace to help you optimize.
i want to set different pages for ViewPager from class. for example:
i have 4 activity A + B + C and D.
A include my ViewPager.
B +C + D are different pages that must be shown in ViewPager.(each of them have different contex)
my question is that, how can i recognize these pages into ViewPager?
thanks
You can add but better way is that you will create a activity and your four activity should be child fragment of the same .
In short your all activity should be fragments jst load the fregment when you swipe viewpage indicater
sample code for adapter
//User adapter according condition
final FragmentPagerAdapter adapter = new FragmentChildPageAdapter(getChildFragmentManager());
//final FragmentPagerAdapter adapter = new FragmentChildPageAdapter(getActivity().getSupportFragmentManager()));
final ViewPager pager = (ViewPager) v.findViewById(R.id.pager);
pager.setAdapter(adapter);
final TabPageIndicator indicator = (TabPageIndicator) v.findViewById(R.id.indicator);
indicator.setViewPager(pager);
//Adapter
class FragmentChildPageAdapter extends FragmentPagerAdapter implements
IconPagerAdapter {
public FragmentChildPageAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
//first fregment
break;
case 1:
//Second fregment
break;
case 2:
//third fragment
break;
case 3:
//Forth fragment
break;
}
return fragment;
}
#SuppressLint("DefaultLocale")
/*#Override
public CharSequence getPageTitle(int position) {
return CONTENT[position % CONTENT.length].toUpperCase();
}*/
#Override
public int getCount() {
//return CONTENT.length;
return CONTENT.size();
}
#Override
public int getIconResId(int position) {
return (Integer) Icon.get(position % CONTENT.size());
}
}