FragmentStatePagerAdapter view pager fragments not showing after activity recreate - android

When my Activity gets recreated via orientation change or if "Don't keep activities" is turned on in developer settings, my ViewPager that has
FragmentStatePagerAdapter doesn't recreate fragments.
this is activity onCreate method:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
ButterKnife.bind(this);
pagerAdapter = new CustomPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
viewPager.addOnPageChangeListener(this);
presenter.onCreate();
}
this is FragmentStatePager:
public class CustomPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments = new ArrayList<>();
public MyFoodaPagerAdapter(FragmentManager fm) {
super(fm);
}
public void init(List<Fragment> initFrags) {
fragments.addAll(initFrags);
notifyDataSetChanged();
}
public void update(Fragment fragment, int position) {
fragments.set(position, fragment);
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getItemPosition(Object object) {
if (!fragments.contains(object)) {
return POSITION_NONE; // replace
}
return super.getItemPosition(object);
}
public void clear(){
fragments.clear();
notifyDataSetChanged();
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return "Title "+position; // TODO: 25/10/2017
}
}
Since FragmentStatePagerAdapter saves currently selected fragment and the one's right next to it, if I only have 2 fragments in pager and Activity gets recreated, changing fragment list and calling notifyDataSetChanged(); doesn't do anything.
getItem(int position) doesn't get triggered in PagerAdapter at all after activity gets recreated. If I have 3+ fragments in the pager and 1st one is selected then moving to 3rd page will render that fragment and moving back to 1st fragment will finally render 1st fragment. If I have only 2 fragments in pager then moving between does nothing, they do not get rendered.
Also what I'm seeing after Activity is recreated. FragmentManager has those 2 fragments with reference numbers lets say (Fragment1#1000) and (Fragment2#1001), when I add new fragments into private List<Fragment> fragments, (Fragment1#1050) and (Fragment2#1051) in pager adapter and call notifyDataSetChanged(), fragmentManager does not update its fragments, it will still have those old fragments in.
EDIT: I think I found my problem support FragmentPagerAdapter holds reference to old fragments
Which says in short, even in the comments, is to never hold reference to fragments outside of PagerAdapter.

Please use this code to update the Fragment View when you are moving between Fragments whether its two or more than that.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
//getPlanListData();
}
}

Related

Refresh fragment from tab layout on click

I have a tab layout with 2 fragment and I have a toolbar with different options that belong to the activity which hosts the view pager.
When I click on a toolbar option, it clean the data from DB that are used in the fragment and then it should refresh it to apply changes at screen.
But I can't find a way to refresh the fragment from the activity.
Here is the fragment adapter :
public class FragmentAdapter extends FragmentPagerAdapter
{
private final List<Fragment> lstFragment = new ArrayList<>();
private final List<String> lstTitles = new ArrayList<>();
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return lstFragment.get(i);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return lstTitles.get(position);
}
#Override
public int getCount() {
return lstTitles.size();
}
public void AddFragment (Fragment fragment , String title)
{
lstFragment.add(fragment);
lstTitles.add(title);
}
}
Here is the way I call it in the fragments in main :
FragAdapter = new FragmentAdapter(getSupportFragmentManager());
((FragmentAdapter) FragAdapter).AddFragment(new FragmentProgramme(),"Programmes");
((FragmentAdapter) FragAdapter).AddFragment(new FragmentPlanning(),"Agenda"); //FRAGMENT I WANT TO REFRESH
mviewpager.setAdapter(FragAdapter);
mtablayout = (TabLayout) findViewById(R.id.main_tabs);
mtablayout.setupWithViewPager(mviewpager);
In the main, I have the option on the toolbar and I would like to do something like this :
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int pos = item.getItemId();
switch (pos)
{
DatabaseAgenda.getInstance(this).dropDB(); //CLEAN DB
// I would like to find a function like this : ((FragmentAdapter) FragAdapter).refresh(FragmentPlanning, "Agenda");
break;
}
return super.onOptionsItemSelected(item);
}
The point is to simply destroy the fragment because all the data must be cleaned from screen and then recreate it so the users can add new data to it
I hope this is clear, thank you for your hlelp
I have found a lot answers but never what I was really looking for
You can override the method setUserVisibleHint inside fragments to detect when it is visible. Data can be refreshed inside this method. Selecting the items of pageradapter programatically triggers setUserVisibleHint of present fragment. You would not need destroying fragments each time.

Refresh fragment when change between tabs

App open on first fragment and there is 2 tabs i want to refresh second fragment
when i move to it but i don't want to refresh first fragment
MainActivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new Main_Button(), "Main");
adapter.addFragment(new TwoFragment(), "Main2");
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
please explain your answer because i'm new in android and i don't know how i can do that
When a Fragment is made visible (i.e., the selected page in your ViewPager), its setUserVisibleHint() method is called. You can override that method in your TwoFragment and use it to trigger a refresh.
public class TwoFragment extends Fragment {
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Refresh your fragment here
}
}
Inside Your fragment class use the below code:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
}
}
Try This
ViewPager mViewPager = (ViewPager)findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(2);
As of 2020 it is advisable to use architecture components(MVVM) such as LiveData and viewModel
This way all Fragments can share the same state
see docs here https://developer.android.com/jetpack/guide
Try this - it works for me
This code reloads current fragment, when it visible to user. Works when swiping and when back button pressed on next fragment.
#Override
public void setUserVisibleHint(boolean isVisible) {
super.setUserVisibleHint(isVisible);
if (isVisible) {
FragmentTransaction ftr = getFragmentManager().beginTransaction();
ftr.detach(this).attach(this).commit();
}
}
If you wouldn't mind refreshing every fragment every time a tab changes, you could simply override the getItemPosition() method of your FragmentPagerAdapter and make it return always the value POSITION_NONE. Example:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
By doing that, you tell android to refresh the fragment each time the view pager tries to get it. Performance wise, it isn't the best practice. So there is a second approach:
First, create an interface to be called when your need refresh:
public interface Updatable {
public void update();
}
Then, make your fragments implement this interface:
public class MyFragment extends Fragment implements Updateable {
...
#Override
public void update() {
// refresh your fragment, if needed
}
}
If your really wish to not update your first fragment, do nothing in its update() method.
Third, override the getItemPosition method and call update(). This will be called every time a fragment gets selected:
#Override
public int getItemPosition(Object object) {
Updatable f = (Updatable) object;
if (f != null) {
f.update();
}
return super.getItemPosition(object);
}
Most of this code came from this answer.
you could use ViewPager.addOnPageChangeListener to listen to when the fragments are swiped and incorporate some type of action there.

Android: Replacing a fragment in a view pager

I have two fragments SearchFragment and CreateFragment in a view pager inside a activity called TicketManagementActivity. Now when the user presses the search button in SearchFragment, I want SearchFragment to be replaced with SearchResultFragment. I should then be able to swipe between SeachResultFragment and CreateFragment in the ViewPager. Also when I press back from SearchResultFragment I should go back to SearchFragment.
Right now, when I press the button I get a blank screen instead of the layout of SearchResultFragment. When I press back I get to SearchFragment but now I have to click the button twice for the blank screen to come. Now after the blank screen comes after the double click, whenever I swipe to CreateFragment tab I get a blank screen instead of CreateFragment layout.
I looked at quite a number of questions on SO but none of them seem to be working for me. Most useful seems to be the first two answers in this question, but the first answer doesn't handle the back press, nor am I able to implement it. The second answer seems very implementable but I get errors which I have mentioned below.
My main TicketManagemementActivity:
public class TicketManagementActivity extends FragmentActivity implements
ActionBar.TabListener {
ViewPager viewPager;
TabsPagerAdapter adapter;
ActionBar actionBar;
String[] tabs={"Search", "Create"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ticket_management);
viewPager=(ViewPager)findViewById(R.id.pager);
actionBar=getActionBar();
adapter=new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for(String tab_name : tabs){
actionBar.addTab(actionBar.newTab().setText(tab_name).setTabListener(this));
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
//removed methods for menu creation and filling and placeholder fragment for brevity on SO
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
My activity_ticket_management.xml which is layout set in onCreate of ticket management activity, just contains the viewpager
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
My TabsPagerAdapter class extending FragmentPagerAdapter:
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
// Top Rated fragment activity
return new SearchFragment();
case 1:
// Games fragment activity
return new CreateFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 2;
}
}
Relevant part of my SearchFragment:
public class SearchFragment extends Fragment implements View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_search, container, false);
.
.//some widget initializations
.
return rootView;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ticket_search_btn: searchSigmaTickets();
break;
}
}
public void searchSigmaTickets(){
.
.
.
.//some operations
.
new SearchAsyncTask().execute();
}
}
private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{
protected Void doInBackground(Void... params){
.
.//some more operation
.
}
protected void onPostExecute(Void param){
Fragment newFragment = new SearchResultFragment();
//Here I use getFragmentManager and not getChildFragmentManager
FragmentTransaction transaction = getFragmentManager().beginTransaction();
//HERE I try to replace the fragment. I'm not sure what id to pass, I pass the id of the main veiwpager in ticketmanagement activity
transaction.replace(R.id.pager, newFragment);
transaction.addToBackStack(null);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.commit();
}
}
}
If I use getChildFragmentManager instead of getFragmentManager as mentioned in the second answer I get
06-25 06:55:32.045: E/AndroidRuntime(2797): java.lang.IllegalArgumentException: No view found for id 0x7f06003c (com.amberroad.sigmaticket:id/pager) for fragment SearchResultFragment{b2fed358 #0 id=0x7f06003c}
Sorry for the lengthy question, how should I solve this?
Kartik, get ready for a lengthy answer to your lenghty question. Replacing fragments in a viewpager is quite involved but is very possible and can look super slick. First, you need to let the viewpager itself handle the removing and adding of the fragments. What is happening is when you replace the fragment inside of SearchFragment, your viewpager retains its fragment views. So you end up with a blank page because the SearchFragment gets removed when you try to replace it.
The solution is to create a listener inside of your viewpager that will handle changes made outside of it so first add this code to the bottom of your adapter.
public interface nextFragmentListener {
public void fragment0Changed(String newFragmentIdentification);
}
Then you need to create a private class in your viewpager that becomes a listener for when you want to change your fragment. For example you could add something like this. Notice that it implements the interface that was just created. So whenever you call this method, it will run the code inside of the class below.
private final class fragmentChangeListener implements nextFragmentListener {
#Override
public void fragment0Changed(String fragment) {
//I will explain the purpose of fragment0 in a moment
fragment0 = fragment;
manager.beginTransaction().remove(fragAt0).commit();
switch (fragment){
case "searchFragment":
fragAt0 = SearchFragment.newInstance(listener);
break;
case "searchResultFragment":
fragAt0 = Fragment_Table.newInstance(listener);
break;
}
notifyDataSetChanged();
}
There are two main things to point out here: 1)fragAt0 is a "flexible" fragment. It can take on whatever fragment type you give it. This allows it to become your best friend in changing the fragment at position 0 to the fragment you desire. 2) Notice the listeners that are placed in the 'newInstance(listener)constructor. These are how you will callfragment0Changed(String newFragmentIdentification)`. The following code shows how you create the listener inside of your fragment.
static nextFragmentListener listenerSearch;
public static Fragment_Journals newInstance(nextFragmentListener listener){
listenerSearch = listener;
return new Fragment_Journals();
}
You could then call the change inside of your onPostExecute
private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{
protected Void doInBackground(Void... params){
.
.//some more operation
.
}
protected void onPostExecute(Void param){
listenerSearch.fragment0Changed("searchResultFragment");
}
}
This would trigger the code inside of your viewpager to switch your fragment at position zero fragAt0 to become a new searchResultFragment. There are two more small pieces you would need to add to the viewpager before it became functional.
One would be in the getItem override method of the viewpager.
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
//this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.
if(fragAt0 == null){
switch(fragment0){
case "searchFragment":
fragAt0 = FragmentSearch.newInstance(listener);
break;
case "searchResultsFragment":
fragAt0 = FragmentSearchResults.newInstance(listener);
break;
}
}
return fragAt0;
case 1:
// Games fragment activity
return new CreateFragment();
}
Now without this final piece you would still get a blank page. Kind of lame, but it is an essential part of the viewPager. You must override the getItemPosition method of the viewpager. Ordinarily this method will return POSITION_UNCHANGED which tells the viewpager to keep everything the same and so getItem will never get called to place the new fragment on the page. Here's an example of something you could do
public int getItemPosition(Object object)
{
//object is the current fragment displayed at position 0.
if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
return POSITION_NONE;
//this condition is for when you press back
}else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
return POSITION_NONE;
}
return POSITION_UNCHANGED
}
Like I said, the code gets very involved, but you basically have to create a custom adapter for your situation. The things I mentioned will make it possible to change the fragment. It will likely take a long time to soak everything in so I would be patient, but it will all make sense. It is totally worth taking the time because it can make a really slick looking application.
Here's the nugget for handling the back button. You put this inside your MainActivity
public void onBackPressed() {
if(mViewPager.getCurrentItem() == 0) {
if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
((FragmentSearchResults) pagerAdapter.getItem(0)).backPressed();
}else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
finish();
}
}
}
You will need to create a method called backPressed() inside of FragmentSearchResults that calls fragment0changed. This in tandem with the code I showed before will handle pressing the back button. Good luck with your code to change the viewpager. It takes a lot of work, and as far as I have found, there aren't any quick adaptations. Like I said, you are basically creating a custom viewpager adapter, and letting it handle all of the necessary changes using listeners
Here is the code all together for the TabsPagerAdapter.
public class TabsPagerAdapter extends FragmentPagerAdapter{
Fragment fragAt0;
fragmentChangeListener listener = new fragmentChangeListener();
FragmentManager manager;
static String fragment0 = "SearchFragment";
//when you declare the viewpager in your adapter, pass it the fragment manager.
public viewPager(FragmentManager fm) {
super(fm);
manager = fm;
}
private final class fragmentChangeListener implements nextFragmentListener {
#Override
public void fragment0Changed(String fragment) {
//I will explain the purpose of fragment0 in a moment
fragment0 = fragment;
manager.beginTransaction().remove(fragAt0).commit();
switch (fragment){
case "searchFragment":
fragAt0 = SearchFragment.newInstance(listener);
break;
case "searchResultFragment":
fragAt0 = Fragment_Table.newInstance(listener);
break;
}
notifyDataSetChanged();
}
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
//this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.
if(fragAt0 == null){
switch(fragment0){
case "searchFragment":
fragAt0 = FragmentSearch.newInstance(listener);
break;
case "searchResultsFragment":
fragAt0 = FragmentSearchResults.newInstance(listener);
break;
}
}
return fragAt0;
case 1:
// Games fragment activity
return new CreateFragment();
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
String[] tab = {"Journals", "Charts", "Website"};
switch (position) {
case 0:
return tab[0].toUpperCase(l);
case 1:
return tab[1].toUpperCase(l);
case 2:
return tab[2].toUpperCase(l);
}
return null;
}
public int getItemPosition(Object object)
{
//object is the current fragment displayed at position 0.
if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
return POSITION_NONE;
//this condition is for when you press back
}else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
return POSITION_NONE;
}
return POSITION_UNCHANGED
}
public interface nextFragmentListener {
public void fragment0Changed(String fragment);
}

How to load fragment data only when its tab is clicked in PagerSlidingTabStrip

I am using PagerSlidingTabStrip in my project and am showing data in fragments. Its a great library which works great. Each Fragment consists of a scholar's lectures which are loaded from a web service. The code to access the web service is in onCreateView method of the Fragment. Currently all the fragment's data is loaded at the same time when they are added to ViewPager. I was wondering if there is any way of loading the Fragment's data only when a user clicks/swipes to a specific tab. Not everything at a time.
Current code is pretty standard, I took help from the provided example [android solution].1
Pager Adapter is as follows:
public class ScholarDetailPagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES = { "Scholar Info", "Lectures"};
Scholar _scholar;
public ScholarDetailPagerAdapter(FragmentManager fm, Scholar scholar) {
super(fm);
_scholar = scholar;
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public Fragment getItem(int arg0) {
if(arg0 == 0)
return ScholarsInfoFragment.netInstance(_scholar.Name, _scholar.Information, _scholar.ThumbnailUrl);
else
return ScholarLecturesFragment.newInstance(_scholar.ScholarId);
}
#Override
public int getCount() {
return TITLES.length;
}
}
It returns TWO fragments, Info and Lectures. Lectures fragment calls a web service to get a scholar's lectures.
Activity code is:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the Above View
setContentView(R.layout.tabbed_view);
Scholar scholar = getScholar(getIntent());
_tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
_pager = (ViewPager) findViewById(R.id.pager);
_adapter = new ScholarDetailPagerAdapter(getSupportFragmentManager(), scholar);
_pager.setAdapter(_adapter);
final int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources()
.getDisplayMetrics());
_pager.setPageMargin(pageMargin);
_tabs.setViewPager(_pager);
}
I tried adding _tabs.setOnPageChangeListener(this); and ended up having onPageSelected(int arg0) in my activity as well. I thought It would be easy now. Then I modified public Fragment getItem(int arg0) in ScholarDetailPagerAdapter above and returned null instead of returning two fragments. But that raised an exception. I thought It would render empty views in pager .. but of course I was wrong.
To make sure Fragment loads data when its respective tab is clicked and is visible, I also have tried overriding its following events and wrote the data loading call in them:
onResume() Not worked. Still loads data when added in activity
Tried overriding onHiddenChanged
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if(!hidden){
loadLectures();
}
}
This did not work too. No data loaded as if this event is not fired at all.
How do I do that? I simply want to load a fragment when user clicks on a tab.
Thanks in advance
You could override setUserVisibleHint event of the fragment to know if its visible to the user and then load your data. something like following:
boolean _areLecturesLoaded = false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && !_areLecturesLoaded ) {
loadLectures();
_areLecturesLoaded = true;
}
}
private boolean isLoaded =false,isVisibleToUser;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser=isVisibleToUser;
if(isVisibleToUser && isAdded() ){
loadData();
isLoaded =true;
}
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
if(isVisibleToUser && (!isLoaded)){
loadData();
isLoaded=true;
}
}

Swipable and replacable tabs, Fragments and ActionBar

As topic states I'm trying to implement swipable tabs using Fragments and ActionBar. I've iplemented it successfully using ActionBarSherlock and code from examples with the TabsAdapter class (subclass to FragmentPageAdapter). Some of the fragments should be able to show a new tab in the same tab spor, and this is where the problem arises. I really can't figure out how to replace a fragment in the pager with a new one. I've googled and tried out a lot of stuff.
I can replacie the fragment in the internal collection holding the fragments classes inside the FragmentPageAdapter, thus making the new Fragment available to the getItem method but that doesn't do it as I can't figure out how to update the pager to show the new Fragment. Even after switching to another Tab and going back I still get the old Fragment which I suppose is being cached somewhere by the pager. So is there a way to notify an update to the pager?
Maybe I should just forget using FragmentPageAdapter ? In that case what is the right way to get all this functionallity happen?
P.S. If you want to see my code let me know, but it's basicly the same as in the examples and the problem is more general I think
When you use a ViewPager it keeps the visible page/fragment in memory in addition to pages/fragments that you can navigate to. In addition to this, if the ViewPager is using a FragmentPagerApdapter it keeps the views in memory for the life of the activity.
I answered a similar post to yours above removing a fragment which can be found here. Your case is an extension of this, removing and then adding something else.
https://stackoverflow.com/a/10399127/629555
Basically, you need to use a FragmentStatePagerAdapter, override the getItemPosition method to return PagerAdapter.POSITION_NONE and make sure you call .notifyDataSetChanged(); on your adapter when you want it changed.
The answer to the link above covers this in much more detail and provides a code example.
What about keeping a reference to the fragments in the adapter my self with the FragmentStatePagerAdapter ? Then I have them both saved in the memory but also have full control of deletion. Is there anything bad I'm missing with this approach and will I run into problems later on? Also I've changed so the fragments are passed on as objects and not classes, thus leaving the initiation outside the pager. Any problems with this?
Attaching my code of the adapter:
public static class TabsAdapter extends FragmentStatePagerAdapter implements
ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<Fragment> mTabs = new ArrayList<Fragment>();
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = activity.getSupportActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(ActionBar.Tab tab, Fragment fragment) {
tab.setTag(fragment);
tab.setTabListener(this);
mTabs.add(fragment);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
public void replaceTab(ActionBar.Tab tab, Fragment fragment, int position) {
tab.setTag(fragment);
tab.setTabListener(this);
mTabs.set(position, fragment);
notifyDataSetChanged();
}
#Override
public int getCount() {
return mTabs.size();
}
#Override
public Fragment getItem(int position) {
return mTabs.get(position);
}
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Object tag = tab.getTag();
for (int i = 0; i < mTabs.size(); i++) {
if (mTabs.get(i) == tag) {
mViewPager.setCurrentItem(i);
}
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
}

Categories

Resources