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.....
Related
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 have Fragment connected with FragmentPagerAdapter, with code
public class LeaveAdapterApproval extends FragmentPagerAdapter{
private static final String TAG = LeaveAdapterApproval.class.getSimpleName();
private static final int FRAGMENT_COUNT = 4;
public LeaveAdapterApproval(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToAll");
return new LeaveFragmentToAll();
case 1:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToPending");
return new LeaveFragmentToPending();
case 2:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToApproved");
return new LeaveFragmentToApproved();
case 3:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToDenied");
return new LeaveFragmentToDenied();
}
return null;
}
#Override
public int getCount() {
return FRAGMENT_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "All";
case 1:
return "Pending";
case 2:
return "Approved";
case 3:
return "Denied";
}
return null;
}
}
and results in Logcat
09-22 15:45:21.259 27234-27234/dan.taaku D/LeaveAdapterApproval: LOG : CURRENT FRAGMENT LeaveFragmentToPending
09-22 15:45:21.259 27234-27234/dan.taaku D/LeaveAdapterApproval: LOG : CURRENT FRAGMENT LeaveFragmentToAll
09-22 15:45:21.259 27234-27234/dan.taaku D/LeaveAdapterApproval: LOG : CURRENT FRAGMENT LeaveFragmentToApproved
We can see from the Logcat results that Fragment loads three Fragments at once, I set when the Fragment is opened Automatic to LeaveFragmentToPending in LeaveClassApproval
public class LeaveClassApproval extends Fragment {
private static final String TAG = LeaveClassApproval.class.getSimpleName();
private TabLayout tabLayout;
private ViewPager viewPager;
public LeaveClassApproval() {
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.hr_employee_leave_class, container, false);
tabLayout = view.findViewById(R.id.tabs);
viewPager = view.findViewById(R.id.view_pager);
viewPager.setAdapter(new LeaveAdapterApproval(getChildFragmentManager()));
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, true); // I set it here
return view;
}
}
But why LeaveClassApproval open LeaveFragmentToAll and LeaveFragmentToApproved, should not it be opening LeaveFragmentToPending instead. Is this a fault or is it a Fragment function?
I searched through Google but did not find an answer
So how to fix it?
Sounds like your problem is the default functionallity from the ViewPager itself. It has a variable called offscreenPageLimit, where the default value is 1. Means it'll always load one page to the left and one to the right. And you won't be able to set it to zero, because the source code of android behind it looks like this:,
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
If you realy want to get rid of this behaviour one thing you could do is building your own Pager class based on the android source code and simply try to cut out this functionallity.
By default the ViewPager opens the current Fragment and its two neighboring ones (that is, the one before and the one after in the data). If you want to change the default behavior use viewPager.setOffscreenPageLimit(int numberOfFragmentsToBePreloadedOnEachSideOfTheCurrentFragment). Notice, however, that the ViewPager will always load at least its two neighbors.
From the docs:
int getOffscreenPageLimit ()
Returns the number of pages that will be retained to either side of
the current page in the view hierarchy in an idle state. Defaults to
1.
So I have a section_home layout that shows a picture. Then I made another layout called section_home_two that shows a different picture. In my code I have a class called HomeFragment for the section_home layout. The second java class is called PreviewsFragment for the section_home_two layout.
Is there anyway you can make HomeFragment swipe horizontally to PreviewsFragment? Is there anyway to merge them?
I would suggest that you use a ViewPager to swipe between the fragments.
Follow the instructions at http://developer.android.com/training/animation/screen-slide.html to get the ViewPager working, and then change the number of pages to 2:
private static final int NUM_PAGES = 2;
And use this PagerAdapter instead of the one in the sample:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == 0)
return new HomeFragment(); // The ViewPager will show this fragment first
else
return new PreviewsFragment; // and will allow you to swipe to this one and back.
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
This will allow you to swipe horizontally back and forth between the two fragments. if you're using a factory method to create your fragments, use that instead of the return new HomeFragment(); and return new PreviewsFragment(); lines.
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.
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.