public static class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm, HomeActivity activity) {
super(fm);
}
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
case 2:
return new FragmentThree();
default:
return null;
}
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position)
{
case 0 : return "Fragment 1";
case 1 : return "Fragment 2";
case 2 : return "Fragment 3";
//TODO
default : return "";
}
}
}
The above is the adapter for view pager. The problem is :
when clicking Tab 1, onResume() of FragmentOne and FragmentTwo get called.
going Tab1 to Tab 2, onResume() of FragmentThree called.
going Tab2 to Tab 3, onPause() of FragmentOne called.
going Tab3 to Tab 2, onResume() of FragmentOne called.
Why all the Fragments are called in this way?
The answer is in the way how ViewPager works. By default it stores three fragments: the one which is shown currently, one previous and one next (if present). If you want to prevent Pager from loading extra Fragments you may call its method setOffscreenPageLimit(0), but actually I wouldn't recommend doing it. So it is not an issue it is working in a way it is intended to work.
UPD: By the way if you want to make your Pager more memory efficient, you may use FragmentStatePagerAdapter instead of FragmentAdapter, it actually destroys fragments instead of simply detaching them.
UDP2: I'm terribly sorry but offscreenPageLimit can be not fewer than 1. So in your case since your Fragment shows a ProgressDialog it passes its host Activity as context. That's why ProgressDialog shows up when adjacent Fragment is shown. So in onResume() method of your Fragment you should check UservisibleHint of the Fragment in order to check if this is the Fragment, shown to user. If it is true then show dialog if not, then dismiss it. Like this:
#Override
public void onResume() {
super.onResume();
if (getUserVisibleHint()) {
progressDialog = ProgressDialog.show(getActivity(), "blah-blah-blah", "blah-blah-blah");
}
else {
if (progressDialog != null && progressDialog.isShowing()) progressDialog.dismiss();
}
}
Related
As the title suggests, is it possible to replace two fragments while in a third one, for example pressing a button in fragment 3 replaces fragment 1 with fragment 2, while all 3 are inside a ViewPager? For simpler understanding, this is what my PagerAdapter looks like:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
int numberOfTabs;
public SectionsPagerAdapter(FragmentManager fm, int numberOfTabs) {
super(fm);
this.numberOfTabs=numberOfTabs;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch(position){
case 0:
CreateEventFragmentPage1 tab1 = new CreateEventFragmentPage1();
return tab1;
case 1:
CreateEventFragmentPage2 tab2 = new CreateEventFragmentPage2();
return tab2;
case 2:
CreateEventFragmentPage3 tab3 = new CreateEventFragmentPage3();
return tab3;
default:
return null;
}
}
#Override
public int getCount() {
// Show 3 total pages.
return numberOfTabs;
}
}
Any and all advice will be highly appreciated.
As this is an adapter FragmentPagerAdapter so there is must be an notifyChanged method or similar
And i suppose it will works as notifyDataSetChanged with Recyclerview
Another reason for this that surrounded fragments of the current are not fully be destroyed
In addition rewrite method getItem(int position) to perform logic of reordering your fragments
Also try out to use an FragmentStatePagerAdapter
I have a TabLayout with 4 tabs, what I want is to keep the first 3 tabs on ViewPager memory and recreate the fragment in the 4th tab everytime this tab is selected, is this possible?
This is my adapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private Fragment[] mFragments;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
mFragments = new Fragment[4];
mFragments[0] = PlaceholderFragment.newInstance(0);
mFragments[1] = PlaceholderFragment.newInstance(1);
mFragments[2] = PlaceholderFragment.newInstance(2);
mFragments[3] = PlaceholderFragment.newInstance(3);
}
#Override
public Fragment getItem(int position) {
return mFragments[position];
}
#Override
public int getCount() {
// Show 4 total pages.
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return null;
case 1:
return null;
case 2:
return null;
case 3:
return null;
}
return null;
}
}
I know I am a little late to this.
You can use the offscreenPageLimit on the ViewPagerInstance to specify the amount of pages to keep alive on either side. The default value is 1 (Also the min value is 1. So you can't have every fragment recreated. IMHO this is probably done for smooth animations)
Check this documentation link for details offscreenPageLimit https://developer.android.com/reference/android/support/v4/view/ViewPager#setoffscreenpagelimit
As specified in the docs, be careful when tweaking this property. It can make the user experience better or worse based on how and where to use it.
Now for the second part of your question about recreating only a particular fragment. Check this documentation link https://developer.android.com/reference/android/support/v4/app/Fragment#setUserVisibleHint(boolean)
This basically should be called whenever your fragment is made visible to the user or not.
public class YourFragmentThatNeedsToBeRecreated extends Fragment {
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// write your refresh logic here
}
}
Though you might face problems with this approach. It doesn't work perfectly for some reason (Insert a rant about fragments here). You can try attaching and detaching your fragment in the refresh logic. But I hope there's a cleaner solution for it.
I want to use 3 fragments within an Android App, I red:
Creating-and-Using-Fragments
.But I want to use the viewpager for swiping and displaying the fragments like explained in:
ViewPager-with-FragmentPagerAdapter
.But this code or the default code of Android Studio Example, use newInstance to create the instances, each time it is needed and destroy when not needed.
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return FirstFragment.newInstance(0, "Page # 1");
case 1: // Fragment # 0 - This will show FirstFragment different title
return FirstFragment.newInstance(1, "Page # 2");
case 2: // Fragment # 1 - This will show SecondFragment
return SecondFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
but I want to create once for all :
// Within an activity
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
fragmentA = FragmentA.newInstance("foo");
fragmentB = FragmentB.newInstance("bar");
fragmentC = FragmentC.newInstance("baz");
}
}
and only hide/show them, as in the example:
// ...onCreate stays the same
// Replace the switch method
protected void displayFragmentA() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (fragmentA.isAdded()) { // if the fragment is already in container
ft.show(fragmentA);
} else { // fragment needs to be added to frame container
ft.add(R.id.flContainer, fragmentA, "A");
}
// Hide fragment B
if (fragmentB.isAdded()) { ft.hide(fragmentB); }
// Hide fragment C
if (fragmentC.isAdded()) { ft.hide(fragmentC); }
// Commit changes
ft.commit();
}
But how to do that with a FragmentPagerAdapter
public Fragment getItem(int position) no longer has to be like that
Also, how to access a data
public double [][] tableaux;
that is in the MainActivity from one Fragment.
Data will be persistant if I assign a pointer of the Fragment I just created in the MainActivity onCreate to point on the MainActivity.tableaux
You can return you pre-initialized fragments in getItem method.
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: return fragmentA;
case 1: return fragmentB;
case 2: return fragmentC;
default: return null;
}
UPDATE: #Alok is right. You should not have fragment references in Activity. And I suggest instead of increasing offscreen page limit using setOffscreenPageLimit, you should consider saving & restoring fragment states using public void onSaveInstanceState(Bundle) and savedInstanceState argument of public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState).
#Lotfi you don't need to save your fragment inside activity, it is prone to leaks .
Also you can assign id to each of your fragment and later when you need to reuse that fragment you can ask fragment manager to return the fragment by passing ie. by calling fragment manager method findFragmentByID() inside your getItem().
I have a PagerAdapter which creates 3 fragments.
In the MainActivity I set the ViewPager like this:
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setOffscreenPageLimit(2);
pager.setAdapter(new PagerAdapter(getSupportFragmentManager()));
the pager.setOffScreenPageLimit(2) is from here https://stackoverflow.com/a/11852707/1662033, to make sure OnViewCreated is called once for each fragment.
Here is my PagerAdapter class:
public class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Home";
case 1:
return "Live";
case 2:
return "Gallery";
default:
return null;
}
}
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new HomeFragment();
case 1:
return new LiveFragment();
case 2:
return new GalleryFragment();
default:
return null;
}
}
}
In the current code: all of the fragments's onCreateView, onActivityCreated etc are called once, at the beginning and that's it.
The issue I am having is - in one of the fragments (LiveFragment) I have a custom view which connects to a camera and shows the live stream.
What I want is - to inflate the view of LiveFragment only when the user navigates to the fragment, instead of how its now - its inflated at the beginning with the other fragments.
Is there a way to call onCreateView only when fragment is chosen?
FragmentPagerAdapter creates all the Fragments and has all of them in memory at all time. i.e. All your Fragments are created only once and you can navigate around them.
FragmentStatePagerAdapter creates and has only 3 Fragments (the current Fragment and the left and right Fragments to the current one) in memory at any given time, by default. You cannot reduce that number. However, you can increase the number Fragments in memory by using the viewpager.setOffScreenPageLimit().
Since you have only 3 Fragments, all your 3 fragments are created when the Viewpager is initialised. You can track which Fragment is currently visible on the screen using viewpager.addOnPageChangeListener(). Using this you can change the View of your LiveFragment from dummy one to actual View only when the Fragment is currently visible.
How can I avoid a recyclerview to reload when the tabs change?
For example, I have a TabLayout with three Tabs, my ViewPager is:
class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new Tab1Fragment();
case 1:
return new Tab2Fragment();
case 2:
return new Tab3Fragment();
}
return null;
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "tab1";
case 1:
return "tab2";
case 2:
return "tab3";
}
return null;
}
}
In the first one, I have a recyclerview, when I change the tabs and back to the first one, the list is reloaded.
There is a way do avoid this reload? Just keep the data loaded.
Android keeps only one fragment on either side of the current fragment in a ViewPager by default. So when you come back to your first tab from the third one, it will recreate the first tab which is what is happening in your case. Here is the documentation.
You need to keep all the Fragments in memory with
viewPager.setOffscreenPageLimit(<(number of fragments in view pager) - 1>) which is 2 in your case.
Of course, as the docs suggest, this number should be kept low especially if its a complex layout.
yourViewPager.setOffscreenPageLimit(2);