I have a problem with restoring proper ViewPager page after orientation change.
CONCEPT:
I've got ViewPager which is set for 8 pages initially.
It has OnPageCHangeListener and when user reaches 5th page, I fetch data from server and add another 8 pages to ViewPager.
So every 7 pages I add another 8 pages, it makes it endless. This is listener code:
ViewPager mPager = (ViewPager) findViewById(R.id.frame);
mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(arg0 % 7) {
addPagesToViewPagerAdapter(isMainPage);
}
}
This is my ViewPager adapter code:
public class FullImageAdapter extends FragmentStatePagerAdapter {
private static ArrayList<Main> response;
public FullImageAdapter(android.support.v4.app.FragmentManager fm,
ArrayList<Main> resp) {
super(fm);
response = resp;
}
#Override
public int getCount() {
return response.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
#Override
public Fragment getItem(int position) {
return FullImageFragment.newInstance(response.get(position));
}
public void addPages(ArrayList<Main> resp) {
response.addAll(resp);
}
}
I also has onConfigurationChaged in Activity which holds ViewPager fragment.
public void onConfigurationChanged(android.content.res.Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
PROBLEM:
Problem is with showing proper ViewAdapter page when orientation change.
For example:
At the beggining there are 8 pages in the ViewPager.
User scrolles to the 3rd page, then change orientation and 3rd page is shown. Everything works fine.
Problem is when uses scrolls after 8 pages f.ex to the 9th (at that time there are 16 pages, because at page 7th we added 8 more) when he change orientation at 9th, the 8th page is shown, which is bad.
To sum up:
Changing orientation:
At position 3 - shown is 3 - good
At position 7 - shown is 7 - good
At position 8 - shown is 8 - good
At position 9 - shown is 8 -BAD
At position 10 - shown is 8 - BAD
etc.
It seems like whatever is handling orientation changes doesn't know that I added more pages and it's showing the last page that was in the adapter at the beggining.
This only affects changing orientation, when user scrolls "normally" everything is fine.
I also use diffrent layouts for land and horizontal orientation.
How can I fix this?
Related
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.
I have one fragment where are three (default) images and when user click on them, they will change to another. But when i swipe to another fragment and back to fragment with images there are not default ones as on the start. When I swipe two times so I will pass to another fragment (distance from original with images is 2 fragments) images are resetted to default. I was trying to implement setOffscreenPageLimit() from ViewPager and set it to 1, but minimum "length" when views in fragments are resetted is 2. How can I change that images to default manually after swipe action? Thank you.
Edit: I think that issue why onResume() is not working here: Fragment onResume not called
but i dont know what that means :/ I have three classes FragmentController.class, PagerAdapter.class and class of specific fragment for example FirstFragment.class. I don't know how connect these classes together.
Check that you create the fragments in the getItem() method of the adapter and do not hold any reference to that fragments (only WeakReferences if necessary), otherwise the fragments could not be destroyed.
EDIT:
The first fragment is unloaded only when you are in the third one because setOffscreenPageLimit is at least 1 so a viewpager allways loads the fragments that are at both sides of the selected one.
What you could do is to update your adapter with this code to provide a getFragment(position) method:
private HashMap<Integer, WeakReference<Fragment>> fragmentReferences = new HashMap<>();
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
case 0:
fragment = FirstFragment.newInstance();
break;
// etc
}
fragmentReferences.put(position, new WeakReference<>(fragment));
return fragment;
}
public Fragment getFragment(int position) {
WeakReference<Fragment> ref = fragmentReferences.get(position);
return ref == null ? null : ref.get();
}
After then you can get the selected fragment and call the method you want from the first fragment when a page is selected:
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int currentPage) {
if (currentPage == 0) {
Fragment firstFragment = adapter.getFragment(0);
if (firstFragment != null) {
// This method resets the images of the first fragment
((FirstFragment) firstFragment).reset();
}
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Do nothing
}
#Override
public void onPageScrollStateChanged(int state) {
// Do nothing
}
});
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.....
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.
I am using viewpager for displaying 7 pages . I am also using viewpagerindicator lib to get as no of circles as the pages are in viewpager.
When i scroll pages in viewpager I want to display user that you are in page 1 , pager 2 ... . respectively. when he scroll the pages in viewpager.
i am using getcurrentitem() of pagedapter to get in which page i am .. But it go not give me correct result.
Is i am doing some thing wrong?
How to count exact page number in viewpager ?
I tried two ways.
First
scScreens = new Fragment[mSessionManager.getPosScreens()];
#Override
public Fragment getItem(int position) {
return scScreens[position];
}
In each Fragment i Wrote the page Number. So that if the respective fragment show change the page Number. (Very bad Way) I have to create 7 Classes (Maximum pages) .
Second
pagestextview.setText(getCurrentItem()+1);
It work while scrolling first , but gives unexpected result when scrolling back to first
i can recommend the FragmentStateAdapter, as it handles the creating/destroying of the fragments pretty well.
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
final FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
#Override
public int getCount() {
return mContactList.size();
}
#Override
public Fragment getItem(int pos) {
Contact mContact = mContactList.get(pos);
int mCurrentPosition = mContact.getPosition();
return ContactDetailsFragment.newInstance(mCurrentPosition);
}
});
//loads the proper fragment on its meant-to-be position into the ViewPager
mViewPager.setCurrentItem(position);