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.
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 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().
am having three fragments say A,B and C, one with viewpager that it contains multiple fragments(say fragment B has) in it.
while switching to fragment b will render the fragment viewpager contents after moving to other fragment and back to fragment b, here it will reload the content again.
i just want to stop destroying fragments once it get rendered??
Thanks in Advance.
sample code of adapter:
public class QuestionsSortPagerAdapter extends FragmentPagerAdapter {
int mNumOfTabs;
public QuestionsSortPagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
QuestionsSortByVotes byVotes = new QuestionsSortByVotes();
return byVotes;
case 1:
QuestionsSortByActivity byActivity = new QuestionsSortByActivity();
return byActivity;
case 2:
QuestionsSortByHot byHot = new QuestionsSortByHot();
return byHot;
case 3:
QuestionsSortByDate byDate = new QuestionsSortByDate();
return byDate;
case 4:
QuestionsSortByMonth byMonth = new QuestionsSortByMonth();
return byMonth;
default:
return null;
}
FragmentPagerAdapter will keep all fragments, it just destroy the view of fragment which is invisible to user, for example, if you scroll page from 1 to 2, then the view of page 0 will be destroyed, but the instance of page 0 will still be retained by adapter.
You can change this default behaviour by calling the method ViewPager.setOffscreenPageLimit(int limit), if you set limit to 2, then the view of page 0 will also be retained.
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.
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);
I'm testing my app with 'destroy all activities as soon as user leaves it' to simulate the OS killing my app.
In my main activity, in onCreate, I instantiate all the fragments, add them to a list and then:
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), fragments);
mViewPager = (ViewPager) super.findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
Here is the adapter code:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public SectionsPagerAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
super(fragmentManager);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0: return getString(R.string.section_one);
case 1: return getString(R.string.section_two);
case 2: return getString(R.string.section_three);
case 3: return getString(R.string.section_four);
}
return null;
}
}
Now, the problem (crash) lies here, in an action bar item:
case R.id.action_refresh:
PostsFragment fragment = (PostsFragment) mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());
fragment.reloadDataFromServer();
Say the activity was opened, then destroyed on the back button. When I return to this activity and press the refresh action item, the app crashes. Why? When the activity is created again, it creates a new pager adapter, with all new 4 fragments. But onCreate of these fragments is never called, none of the members are initialized, so the reloadDataFromServer fails loading into a listview that hasn't been initialized yet.
HOWEVER, the onCreate of the previous referenced fragments IS called, but we no longer have a reference to them because we recreated the FragmentPagerAdapter with new fragments. This is driving me INSANE! Why is onCreate not called for the new fragments?
In case someone has problems with this, I'll explain my solution. Basically what happens is fragments that were created before are restored on recreation of the activity after a destruction. This may be a duh moment for some of you.
My problem is essentially having incorrect references to fragments.
First, I changed the setOffscreenPageLimit to the full (#tabs - 1). This means all fragments are created at the start and held in memory. I'll explain why this helps later.
This means in onSaveInstanceState, we can use putFragment on all 4 fragments with the outState Bundle.
In create, if savedInstanceState is null, we can create all 4 fragments from scratch. Otherwise, you can use getFragment on all 4 fragments and place them into the new pager adapter to hold as reference.
You may ask why I set setOffscreenPageLimit to max. I had problems with figuring out which fragments to save with putFragment. isAdded() did not seem to always work properly, neither did isResumed(), so it was easier to just save all of them because we know all 4 were created and were in the fragment manager at all times.