reuse fragments in FragmentStatePagerAdapter - android

MY PROBLEM
I am creating an Android app where the user swipes through a load of cards (like tinder)
Each card is VERY UI heavy (has a google map view and an admob ad)
I tried loading the list into a fragment adapter. This means a fragment for every single item. This is very memory intensive as I am creating about 30 google map views and 30 admob views for the 30 items.
My nexus 5 unsurprisingly crashes under this unnecessary heavy load.
My attempted solution
I have created 3 instances of the fragment.Loaded these fragments into a HashMap.
//for currently displayed fragment
key = 0
//for previous page
key = -1
for next page
key = 1
I keep the previous index.
On the getItem method of the adapter I detect if the user has gone to the previous page or the next page.
I get the associated fragment and return it.
My problem is I keep receiving exceptions like:
IllegalStateException: Fragment already added
I have tried detaching and/or removing the fragment but no luck :(
Any suggestions?
Thanks

From the Google Play ViewPager example...
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return new ScreenSlidePageFragment();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
In the getItem(int position) you have to create a new Fragment instance, old fragments should be destroyed when the setOffscreenLimit has been reached. I get the feeling originally you were creating all 30 Fragments at once?

take a look at android's effective navigation sample for proper use of fragmentstatepageradapter.
http://developer.android.com/shareables/training/EffectiveNavigation.zip
I recommend put your admob in the main activity below viewpager. so u dont need to restart it everytime. and google map inside swipe view is not recommended by google as it is hard to handle both swipe and map zoom features. So simply use static map api it provides a screenshot of map according to the coordinates u provide and implement an onclick method to launch map as another activity.

Related

How can I gather data from my ViewPager?

I made an activity which is simply a viewpager with a few tabs. Each tab is like a form that the user needs to fill out. When the user is done, the data from the view pager should be collected and sent back as a result. I almost have this working but for some reason the data on my first tab seems to get reset when the user gets to the third tab. I'm guessing this is due to some view recycling in the pager. Anyways, I'm wondering if there is some easy way to gather all the data from the different tabs or do I have to create some kind of tight coupling between the activity and viewpager object?
you have to use SmartFragmentStatePagerAdapter as described here "https://guides.codepath.com/android/ViewPager-with-FragmentPagerAdapter" and than need to
vpPager.setOffscreenPageLimit(3);
Your issue should be resolved. :)
A viewpager will kill off the fragment if it is 2 pages away from the current page. It then recreates it when the pager is on a page that is 1 step away from it.
You should move your form data into your activity/fragment that is holding the viewpager and use fragment listeners to update your data accordingly
For example, you could use a fragment listener like below to pass the data to/from the containing activity
public interface MyFragmentListener{
void saveMyFormData(MyFormData formData);
MyFormData getFormData();
}
private MyFragmentListener mListener;
//initialise fragment listener in onAttach (or elsewhere)
private void initFormView(){
MyFormData data = mListener.getFormData();
//do stuff with data
}
private void saveData(){
mListener.saveMyFormData(myFormDataObject);
}

Deleting page from FragmentStatePagerAdapter causes issues

Our application has multiple fragments to show data specific to location. We have used FragmentStatePagerAdapter and ViewPager. User should be able to add new page and delete as well. While adding new page works fine, deleting the existing page shows issues. When I verified I had 3 pages. I deleted the 2nd one from left navigation menu button.When I try to slide to the 3rd page, it shows the view of delted page that is 2nd page. ANd it does not show the 3rd page.
I have Overriden getItem() where I return the new page. I have adjusted the ItemCount when I added/deleted the new page to Adapter. And I called startUpdate() before I add/delete, destroy() to delete any fragment object in a position, finishUpdate() after everything done. And finally notifyDataSetChanged().
Looks like nothing is missed but something. I still see the view of deleted fragment and next to it is blank page or something similar depending on which position's page I delete.
I see Removing fragments from FragmentStatePagerAdapter is something similar but could not help my case. Help me with any clue.
I could solve it. Though the answer by Wize here Replace Fragment inside a ViewPager does not solve my issue, I could find a hint from this. I had to override getItemPosition(Object object) which tells system that particulat 'object' is not-changed/no-more-exist.
I have the list of fragments (references) locally as well which I modify according to the list that FragmentStateAdapter maintians and here is my code for getItemPosition():
#Override
public int getItemPosition(Object object) {
if (object instanceof myFragment) {
if (!mFragments.contains(object)) {
return POSITION_NONE;
} else {
return mFragments.indexOf(object);
}
}
return super.getItemPosition(object);
}
Also, I only called notifyDataSetChanged() after I add/delete fragments. I removed startUpdate(), finishUpdate() all that.I do not knwo where exactly where they are used. But it worked without them.

ViewPager and Custom Adapter

I have set up a ViewPager with a custom Adapter. That Adapter caches Fragments it shows. Code looks like this:
#Override
public Fragment getItem(int position) {
Fragment f = getCachedFragment(position);
if (f == null) {
//create and add fragment to cachedFragments
}
System.out.println("getItem: Fragment named:" + mTabs.get(position).clss.getSimpleName() + " with Hash " + f.hashCode());
return f;
}
Everything works fine. Except that the initial Fragments shown by the Fragment don't appear to be created using the getItem() method.
I have verified that 2 different kinds of fragments are being initiated, one with the getItem() method, and another one in a miraculous way I'm not able to explain. The Problem is that properly created one isn't the fragment which is being shown, so trying to access it and manipulate a view on it will fail, since onCreateView() has never been called.
As you can see every fragment created or retrieved is logged in chat with the corresponding hashCode. In addition to that I have added another system.out which tells me about new instances of a fragment.
Now, when I am on index 0 of the ViewPager, the fragment for index 1 is loaded as well, but not using getItem(). My Question is, how is that Fragment for index 1 initiated? I checked ViewPagerAdapter documentation but it doesnt provide any kind of solution since it just says it uses getItem() to get the corresponding Item.
Any pointer in the right direction would be greatly appreciated.

Change order of ViewPager and update views

I have a ViewPager with 10+ views. I wish to change the order of the pages occasionally i.e.
Page 1 | Page 2 | page 3
to
Page 3 | Page 1 | Page 2
For the tabs I am using ViewPagerIndicator, I am able to update the order of these by changing the order of my adapter and calling:
mViewPagerIndicator.notifyDataSetChanged();
However, what I cannot update is the content of the ViewPager, for example if I update the ViewPager adapter (to the new order I wish the pages to be in) and call:
mViewPager.getAdapter().notifyDataSetChanged()
This has no effect. The tab indicator bar is updated fine but the content of the page still shows the old order.
Is there a way to change the order of pages in a ViewPager adapter and get the ViewPager to reload all it's views?
My ViewPager adapter extends FragmentStatePagerAdapter and my overridden methods are as follows:
#Override
public Fragment getItem(int position) {
return MyCustomFragment.newInstance();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
//super.destroyItem(container, position, object); // Commented out to ensure views are not destroyed
}
Thanks
It's tricky to do with FragmentStatePagerAdapter because you'll have to implement the logic yourself, but it's a lot easier with FragmentPagerAdapter which already provides the functionality for this purpose.
Calling .notifyDataSetChanged() invokes the getItemPosition(Object object)-method of your adapter. This is where you should provide ViewPager with the information of where to put the Fragment passed in the parameter object, by returning the page-number you want the Fragment to be positioned on.
For an example on how to do this, check out my answer here.
If performance isn't super important, it's really easy to force the FragmentStatePagerAdapter or FragmentPagerAdapter to redraw (invalidate) all of its views/fragments: just override getItemPosition and call notifyDataSetChanged() as you normally would.
#Override
public int getItemPosition(Object object) {
return POSITION_NONE; //In source code, POSITION_NONE = -2 FYI
}
For more info, check out my post: How to use orderByKey() in descending order instead of ascending #Askfirebase

ViewPager + FragmentStatePagerAdapter : need to know when fragment is displayed (selected)

I am using a ViewPager plugged to a FragmentStatePagerAdapter. Each fragment contains a listview. In this listview I wish to display ads and make calls to a analytics server. I only want to make those calls when the user navigates to a fragment (so I cannot use the onCreateView or onActivityCreated events in the Fragment). Is there an event I can hook on to this end ?
Update
I realized that the way I am fetching my current fragment is flawe
#Override
public void onPageSelected(int page) {
this.currentPage = page;
tabsAdapter.getItem(page);
}
getItem(int position) in the pager is actually responsible for instanciating the fragments, so it would create a new unattached fragment (hence getActivity() returning null). I think I will us an approach where I keep a map of the fragments (<Position, Fragment>), i will remove the fragment from the map when destoryItem is called in the pagerAdapter.
You are right, best option is the second solution here:
http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html
so your thoughts are correct.
How about this:
http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html
The onPageSelected(int) method sounds like it does what you want.

Categories

Resources