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.
Related
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);
}
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.
I'm new to fragments. I have an activity that extneds FragmentActivity and uses the ViewPager to swipe between fragments.
Within one of the fragments I want to launch a new fragment that is "outside" of whats in the view pager. Is it possible to add a fragment like this or do I need to start a new activity with the fragment.
Now I have it launch a new activity but it is very slow.
Activity A
Fragment A
Fragment B
Fragment C
Activity B
Fragment D
Fragment A
So fragment A can launch acitvity B to get to fragment D but ideally it would be cool if I could just inject fragment D in Activity A
Hope that makes sense.
Yes, you can, but I strongly suggest you avoid it, unless you have a good reason to do so.
The ViewPager is (I assume) hosted in your Activity. The ViewPager obtains its views from its adapter. (A FragmentStateAdapter or similar). So you could tell the activity to change its layout (and or hide the Viewpager and show another FrameLayout) or you can simply launch another Activity that contains a single fragment. This is also fine.
Messing with the ViewPager/Adapter is usually complicated and you may waste time trying to make it work.
On the other hand, you could add the Fragment to the ViewPager's Adapter and use the getType of the adapter to return a different type of Fragment.
So you could do (pseudo code):
mPagerAdapter.addFragmentDToTheDataAtPositionZero(); //longFancyName ;)
mPagerAdapter.notifyDataSetChanged();
mViewPager.setCurrentItem(0, false);
that'd add Fragment D to the "top" of the viewpager and will switch to it.
Your Adapter then has to use an Interface to determine what type of fragment must be instantiated…
The getItem of the adapter would look like… (again, pseudocode)
#Override
public Fragment getItem(final int i) {
final FragmentTypeInterface f = yourData.get(i);
if (f.isD()) {
return FragmentD.newInstance();
} else {
return OtherFragment.newInstance();
}
}
And so forth… :)
i got viewpager with 4 tabs .. in each tab there is a fragment.
my first tab is a fragment with a form (for example Users)
after i click save, the data is inserted in the database.
my second tab is another fragment with form but it uses data from the first tab (i am getting it from the database) to populate a spinner.
now after i successfully inserted data from my first tab, in my second tab the spinner is empty. since my db query is implemented in onCreateView() in the second fragment, its called only once when the application is started, so changing between tab 1 i tab2 doesn't starts onCreateView() or even onResume().
The interesting thing for me is that if i go to tab4 and then return to tab2, my data is in my spinner properly, so somehow swiping two tabs away from my current tab refreshing my fragment.
my question is how can i achieve proper refresh to my fragment when onCreateView() is called only once ?
Edit
i tried to put that method psykhi suggested like that:
this.mViewPager.setOffscreenPageLimit(0);
this.mViewPager.setAdapter(this.mPagerAdapter);
this.mViewPager.setOnPageChangeListener(this);
but it's not working for me. Am i missing something ?
The best way that I have discovered to simulate a "setOffscreenPageLimit(0)" behavior is by using an OnPageChangeListener in conjunction with overriding the getItemPosition method in your adapter. Something like this:
In your custom pager adapter:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Then in your Activity containing the ViewPager:
final MyPagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
... anything you may need to do to handle pager state ...
adapter.notifyDataSetChanged(); //this line will force all pages to be loaded fresh when changing between fragments
}
}
This should have the same effect as setting the OffscreenPageLimit to 0. However, this functionality is sort of against what the ViewPager is designed to provide. If you are trying to implement a ViewPager in this way, it may be worth reevaluating your layout to be sure that a ViewPager is really what you want to use.
UPDATE: There is a better way, Please have a look here: Update ViewPager dynamically?
Removing content of this answer because it was a really bad way to access Fragments inside ViewPager, please see the above link for better approach.
It's because you can specify the number of fragment your viewpager will "keep in RAM" (setOffScreenPageLimit () :I think the default is 1). So your second fragment is not reloaded. And when you go to a tab 3 or 4, then your 2 firsts fragments are deleted, then recreated when you come back.
To refresh your fragment, there is many way to do it: you could implement a listener interface of your own in it to tell it when to refresh, or simply call a method that you would implement to update your contents.
first override in the fragment method
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
actionView();//call the method to be refreshed
}
else{
//no
}
}
In my experience, ViewPagers keep:
Your current tab
& 1 tab either side in memory
So when you switch between 1 and 2, nothing is happening under the hood - it's like they are simply being hidden / shown.
But when you navigate to tab 4, tabs 1 & 2 are destroyed (onDestroy() called), so when you navigate back to either of them, they are being re-created fresh (onCreate() called).
As psykhi suggests, you could setOffScreenPageLimit() to 0, so that each fragment is created every time you navigate to it.
If you were interested in keeping the other pages in memory for performance purposes, as they were designed with this is mind, you could use a messaging/event publishing system to send a message from tab 1 to tab 2 telling it to update when you submit the form on tab 1.
Ok may be a late reply,might help others in future, so recently I faced same issue while fetching data from db into tabs it used to display only once I click on 3rd tab.. I tried above mentioned solution but nothing really worked.. fianlly i came across Otto
This library allows you to communicate within your app..
Just use this library to yourAdapter.notifyDataSetChanged() wherever you fetching your data from db..
This video helped me alot https://www.youtube.com/watch?v=lVqBmGK3VuA
I'm trying to create a ViewPager with six fragments but only 2nd fragment to 5th fragment contain data that I want to show and the first fragment and the last fragment I want to be used to reload the data and set the position to the 2nd fragment again. The overall flow is like this :
1st (reload and go back to 2nd) <- 2nd fragment <-> 5th fragment -> 6th fragment (same with 1st)
what I've tried is I create a callback from the 1st fragment and 6th fragment like this
public static class callbackFragmentLoading implements callbackFragmentLoad {
#Override
public void onLoading() {
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(2,false);
}
}
and I passed the callback to the fragment constructor so I can called the onLoading function in the onActivityCreated. But I everytime I do it the application will be force closed and the logcat shows
recursive entry to executependingtransactions
is there any way to do this? or my method for doing it is wrong?
Thank You
is there any way to do this? or my method for doing it is wrong?
Messing with callbacks between Fragments of a ViewPager isn't probably such a good idea. Instead I would do it like this:
Don't load any data(like with a Loader) in the Fragments from the ViewPager, instead let the FragmentActivity do it(and the Fragments will get it through methods from the Activity).
Your two loading fragments(position 0 and 5) will call in their onResume method a reload action on the parent Activity(like a Loader restart)
At this moment the Activity will load/reload the data and when that finishes it will set the ViewPager to the correct items(either 1 or 4)
in the onResume method of the data fragments you'll refresh the fragment's data(here you may need to use some sort of signaling system because you'll need to duplicate the refresh code in the onCreateView(some fragments may have their view destroyed if they are far apart from the current visible position)).
As I don't know many things about the inner data fragment I've written a basic skeleton sample(without the data loading in the activity).