Wrong position value in Android ViewPager - android

I'm trying to implement 3 slides composed of 3 fragments (or 3 layouts) with ViewPager and I want to know which slide I currently show in order to display the appropriate content. In simpler words, I want content 1 on slide 1, content 2 on slide 2 and so on.
Here is my actual code from my Activity (from android official doc) :
public class SliderActivity extends FragmentActivity {
private static final int NUM_PAGES = 3;
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slider);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
// A simple pager adapter that represents 3 ScreenSlidePageFragment objects, in sequence.
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
System.out.println("POSITION = " + position); // Or mPager.getCurrentItem()
return new SlidesFragment();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
No matter how hard I try, getCurrentItem() or position still prints wrong values. Only the second slide sends a number to the console. It's 2 when I swipe right and 0 when swipe to left.
What am I doing wrong ?

You definitely want to use the position value in getItem, do not call getCurrentItem because the pager will create items that are not the current item and not on-screen. The pager has an offscreen page limit of 2 by default. So, when it is first created, getItem will be called with a position of 0 and then again immediately with position of 1. The fragment at position 1 is not the current item, but it is being created offscreen so the user can start sliding over and see it. Then when you complete the swipe, getItem is called with a position of 2 to pre-load the next and final slide.
To accomplish "I want content 1 on slide 1, content 2 on slide 2", your SlidesFragment needs to take an argument (store in the arguments bundle) the position that tells it which content to display. Or, more likely, you have 3 different fragment types you would create based on the position.

Related

Add Fragment before and after current position in viewpager

I have a ViewPager which contains Fragments. I want to add fragment before and after the current position. Here is the adapter:
public class TabManager extends FragmentStatePagerAdapter {
private final List<Fragment> list = new ArrayList<>();
public TabManager(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public int getItemPosition(Object object) {
if (object instanceof Fragment) {
return list.indexOf(object);
}
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return list.get(position);
}
#Override
public int getCount() {
return list.size();
}
public void addFragment(Fragment fragment, int index) {
list.add(index, fragment);
notifyDataSetChanged();
}
}
I can add fragments after my position (it appears in the right side of the ViewPager) without problem. But if I add a new fragment before my position (the left side of the ViewPager) the ViewPager immediately focuses to it.
So, my question is, how to add a new fragment to the left side, without focusing (so let the user swipe) to it?
In other words here is one fragment:
1
^
I add a new one to the right side, but the first one stay focusing:
1 2
^
Now, if I add another one to the left side it immediately focuses (that's what I don't want):
3 1 2
^
This should be the correct:
3 1 2
^
Sorry, can not comment due to a reputation.
If your current position is 0 (1 fragment), and you add fragment to the right side, current item position dos not change - 0 (added fragment does not change it).
But if you add the fragment to the left side (before fragment 1), your adapter's current position still 0, and the fragment on that position is now fragment 3, NOT fragment 0 anymore. So i guess before notifying adapter you should change your adapter's position to the desired item (if you add after - do nothing, if you add before - correct you position).
In you case - set current adapter position to 1 instead of 0.

TabLayout with ViewPager not working as expected

There is 3 TabLayout with ViewPager in my application.
If I swipe for change Tabs then it behaves strangely.
Below is the scenario.
Let's say 3 Tabs A, B, C.
Default Tab A will open when I start App.
Go to B From A - Works OK.
Go to A From B - Works Ok.
Go To B From A - Works Ok.
Go To C From B - Works Ok.
Now Problem Starts here.
Go To B From C - It will Load A and B Both but display Only B.
Why A and B loads if I come from C ??
Below is my code.
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
ViewPagerAdapter adapter;
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
private void setupViewPager(ViewPager viewPager) {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new FragmentOpen(), "OPEN");
adapter.addFragment(new FragmentClose(), "CLOSE");
adapter.addFragment(new FragmentTest(), "TEST");
viewPager.setAdapter(adapter);
}
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);
}
}
I want to load Tabs every time.
For that, I use below code.
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if(adapter != null){
adapter.getItem(position).onResume();
}
Toast.makeText(getApplicationContext(), "Page Selected", Toast.LENGTH_LONG).show();
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
But it will always load both tabs when I came from C.
What I want is: only Tab B must load when came from C, not Both.
Its not possible when using ViewPager. ViewPager loads at least one right/left adjacent fragment of current fragment as per your swipe direction.
If you read the ViewPager documentation, there is a method ViewPager.setOffscreenPageLimit(LIMIT) which supports off screen page limit and its default value is 1.
SetOffscreenPageLimit:
Set 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.
This is offered as an optimization. If you know in advance the number
of pages you will need to support or have lazy-loading mechanisms in
place on your pages, tweaking this setting can have benefits in
perceived smoothness of paging animations and interaction. If you have
a small number of pages (3-4) that you can keep active all at once,
less time will be spent in layout for newly created view subtrees as
the user pages back and forth.
You should keep this limit low, especially if your pages have complex
layouts. This setting defaults to 1.
See documentation.
Here are some related answers:
1. ViewPager.setOffscreenPageLimit(0) doesn't work as expected
2. How can make my ViewPager load only one page at a time ie setOffscreenPageLimit(0);
3. How does setOffscreenPageLimit() improve ViewPager performance by retaining more offscreen Fragments?
its Default behavior of ViewPager if you are in A then it loads B
also and when your reach B it will Load C and A also is Loaded now
when to reach at C its destroy the View of A and now when you come
again to B it will Load A again, in a simple way ViewPager Maintain
the One Next and One previous state of View by Default.
to Overcome this you should use ViewPager.setOffscreenPageLimit(number) here number is the count of page to load Simulationally for Next and Previous View.
I had only 3 tabs and settingViewPager.setOffscreenPageLimit(2) worked for me.
Try ViewPager.setOffscreenPageLimit(totTabs - 1)

Not getting the correct position from viewpager

I am using viewpager in order to load images from the server, each fragment should load the image based on positon of the fragment in the adapter. so for example position 0 will load image 0 position 1 load image 1 etc. for the last two days I am struggeling with getting the correct fragment position, in total I have 3 fragments however from print outs I have added to the code I can see only position 0 and 2 and thus the image is duplicated in position 1.
the main question is how can I resolve this? I would like to get the correct position and based on that the correct image. below is the viewpager code and adapter code, it was modified several times based on several solutions however none of them seems to work
public class LoadCarFullSizeImage extends AppCompatActivity{
private ViewPager viewPager;
protected String imagePath;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.car_images);
imagePath=getIntent().getStringExtra("imagePath");
viewPager=(ViewPager)findViewById(R.id.vp_carViewPager);
CirclePageIndicator titleIndicator = (CirclePageIndicator)findViewById(R.id.titles);
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(),imagePath);
viewPager.setAdapter(mPagerAdapter);
titleIndicator.setViewPager(viewPager);
}
#Override
public void onBackPressed() {
if (viewPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
}
}
private static class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
private String imgPath;
public ScreenSlidePagerAdapter(android.support.v4.app.FragmentManager fm, String _imagePath) {
super(fm);
this.imgPath=_imagePath;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return CarImageFragment.newInstance(0, imgPath);
case 1: // Fragment # 0 - This will show FirstFragment different title
return CarImageFragment.newInstance(1, imgPath);
case 2: // Fragment # 1 - This will show SecondFragment
return CarImageFragment.newInstance(2, imgPath);
default:
return null;
}
}
#Override
public int getCount() {
return 3;
}
}
}
Since you are using the indicator.. you have to getthe current page from it... and set current page using it too...
When using indicator stop using the pager or pager listener... any listener should be set to the indicator.
titleIndicator.getCurrentItem()==0...
and
titleIndicator.setCurrent.....

Fragment initializes before being visible to the user in viewpager

I am reusing the same fragment to display an image with an animation. The problem which I facing is the fragment view is loaded with the animation before it is visible to the user while swiping. Below is my PagerAdapter
public class PollsAdapter extends FragmentStatePagerAdapter {
/** Constructor of the class */
int clickPosition = 0;
ArrayList<Polls> pollsList = new ArrayList<Polls>();
public PollsAdapter(FragmentManager fm,ArrayList<Polls> pollsList) {
super(fm);
this.pollsList=pollsList;
}
/** This method will be invoked when a page is requested to create */
#Override
public Fragment getItem(int arg0) {
PollsFragment myFragment = new PollsFragment();
Bundle data = new Bundle();
data.putSerializable("poll", pollsList.get(arg0));
data.putInt("position", arg0);
myFragment.setArguments(data);
return myFragment;
}
/** Returns the number of pages */
#Override
public int getCount() {
return pollsList.size();
}
public int getItemPosition(Object item) {
return POSITION_NONE;
}
}
This is how I set my viewPager
pollsAdapter = new PollsAdapter(fm, pollsList);
viewPager = (ViewPager) _rootView.findViewById(R.id.pager);
viewPager.setAdapter(pollsAdapter);
I tried by using `viewPager.setOffscreenPageLimit(1); but this didn't work. I'm sure I miss something. Please help me to solve this problem.
This is --unfortunately-- not possible. To display the flip animation, ViewPager has to pre-load at least the fragments to the left/right of the currently displayed fragment.
Because of this, the minimum possible value for ViewPager.setOffscreenPageLimit() is 1 as at least the direct neighbours need to be available.
In your case, the problem is rather that your fragment seems to be starting an animation as soon at is created.
In that case, you will need to change the starting logic for your animation. Your fragment can get notified by the ViewPager as soon as it is scrolled to (see ViewPager.OnPageChangeListener) for the mechanism). Note that the OnPageListener is not called when your activity is resumed so you also need to handle this case..
How to determine when Fragment becomes visible in ViewPager has more information on that topic.

How to retrieve user data from fragments embedded in viewpager?

I have a ViewPager, defined in an Activity, and many Fragments sequentially shown in the ViewPager. In these fragments there are dynamically constructed checkboxes and radiobuttons, which the user is supposed to manipulate. On the very moment that the user swipes to the next page I need the user data to be retrieved and stored in the Application object. I can't figure out what the standard way of doing this is. Since there are many Fragments I opted for using the FragmentStatePagerAdapter. Any help would be welcome, thanks in advance!
Update-1:
I do have this:
pageAdapter = new MyPageAdapter(getSupportFragmentManager());
pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(pageAdapter);
// detects viewpager page change
pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
Log.i("TAG", "onPageSelected");
int index = pager.getCurrentItem();
MyPageAdapter adapter = ((MyPageAdapter) pager.getAdapter());
QuestionFragment fragment = (QuestionFragment) adapter.getItem(position);
if (fragment.rdbtn != null) {
for (int i = 0; i < fragment.rdbtn.length; i++) {
if (fragment.rdbtn[i].isChecked())
Log.i("TAG", "checked");
else
Log.i("TAG", "not checked");
}
}
// fragment.refresh();
}
});
When checking the debugger, after starting up, the ViewPager instantiates Fragments 0 and 1 (standard behavior). When the user manipulates fragment-0 and swipes, the handler is indeed called but with position=1, not 0. And the public elements I want to read are null!
UPDATE-2
I notice in the debugger that the data I need is stored in adapter.mCurrentPrimaryItem.
How to retrieve CurrentPrimaryItem in the code?!
You can implement a pageChangeListener in your activity and set this to the viewPager.
Then you can have a class abstract BaseFragment extends Fragment and declare an abstract method, say, getData() that every fragment in the ViewPager extends and overrides the method.
And in onPageSelected() of the activity you can access those data.

Categories

Resources