xamarin android viewpager fragments not getting refreshed - android

I'm using this sample to create a simple app, it's made by Joshep Raco (JoeRock11)
https://github.com/JoeRock11/Xamarin_DesignLibrary
I have a problem, I was working with this sample to create a tabbed app, and I found an strange behavior, when the app is starting only the fragment1 and fragment2 loads , but not the fragment3 (when I say load I mean their oncreate and oncreateview method gets triggered ).
To load the fragment3 I have to explicity click it, I thought all the fragments would get load but is not the case, don't know why, I would like to know why this happens.
Also, and this is my main problem and I dont know how to fix it, every time I click a fragment tab, I want this fragment to reaload, now seems like it just shows the instantiated fragment on memory, because it doens't triger any method of the fragment, I need to load it again, because I need to refresh its data, now it only reloads randomly. can you or anyone help me solve this please?
Thanks a lot in advance.
pd: sorry for my english, it's not my mother language.

I believe this is controlled by the Off Screen Page Limit on the ViewPager control. This value defaults to 1, which basically means, the number of the pages the ViewPager should load at a time is 2, the page that is currently displayed, and the one off screen.
This value can be easily adjusted using the OffscreenPageLimit property on the ViewPager.
http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int)
If you want all Fragments to reload data once they are slid on to the screen, you can override UserVisibleHint on each Fragmentand then check to see if the Fragment is visible and execute some code to reload data for that fragment:
public override bool UserVisibleHint {
get {
return base.UserVisibleHint;
}
set {
base.UserVisibleHint = value;
if (value) {
//Fragment is visible, reload data
}
}
}
http://developer.android.com/reference/android/support/v4/app/Fragment.html#setUserVisibleHint(boolean)
Another approach to updating the ViewPager fragments is outlined here: Update ViewPager dynamically?

Related

how ViewPager2 handle fragments

In my app I have a ViewPager2, the fragment inside this ViewPager have some code on onViewCreated. I have 2 question:
If I update the dataset and then return to old one the onViewCreated is not called, because of fragment cache, how can I intercept the re-show? I am ok with cache, bui I want restore the initial focus of the fragment when re-showed.
How can I intercept the hiding from the fragment? My fragment show a dialog, then an external event can change the data set, I want in this case dismiss the dialog.
Are 2 days that I am trying to fix those 2 issues.
EDIT: I solved the 2 observing the changing of data set.
Data sharing by ViewModel looks like a good solution for both cases. Fragments will rely on data change in shared ViewModel (depends on the structure of your screen it can be data sharing with host activity or parent fragment).
If it's not what you are looking for, provide more details.

Load Fragment only when Tab clicked

I want my fragment to load only when the tab is clicked. That is I am calling a webservice on each fragment, so I want that webservice to be called only when user clicks the specific tab; loads the fragment.
My Fragments are attached to the view pager.
I have override the following method in my fragments: setUserVisibleHint
override fun setUserVisibleHint(isFragmentVisible: Boolean) {
super.setUserVisibleHint(true)
if (this.isVisible) {
// we check that the fragment is becoming visible
if (isFragmentVisible && !isLoadOnce) {
callAPI(param)
isLoadOnce = true
}
}
}
the variable is set as: private var isLoadOnce = false in the fragment class.
I have 3 fragments in number the problem is when my activity popsup, the first fragment is visible and if I click the last tab that is the third tab to load the third fragment, nothing happens that is the web service won't call at all.
But when I click the second fragment and then the third fragment, and yes the webservice then only calls
So I want to call the web service whenver the user clicks each fragment (number 2 fragment or number 3 fragment)!
Can somebody please figure out what I am doing wrong?
I think there are at least 2 solutions to your problem:
Call "callApi(param)" on onResume() of the fragment;
Override onPageSelected(int) and call "callApi(param) in it.
Let me know if this worked for you!
viewpager generally loads the side views to provide smooth swipe experience.
you can restrict the viewpager to load the view using
viewpager.setOffScreenLimit(0)

orientation change, FragmentTransaction, Activity has been destroyed, WeakReference

Alright. My first question here. And I already found some solution, but honestly do not really get the stuff that happens in the background. So perhaps there’s someone who could clear up this stuff a little bit. After days of debugging I’m just glad that it works... and hope I did not make some serious error. So let’s have a look.
I got some Main-Activity. Just a FragmentActivity extending JFeinstein’s SlidingFragmentActivity. Further I decided to go the fragment-way and just put any content (list-fragment, article-fragment, …) as a fragment into a container (to right of the sliding-menu); my main-container. So far, so good.
One essential fragment is my article-fragment. A ViewPager (with a FragmentStatePagerAdapter) - containing some pages with text and perhaps another list-fragment. Still no problem so far, until I decide to rotate the device. And to be more precise, rotating the device works too as long as I do not decide to update my article-fragment.
I understood (correct me if I am wrong) that Android handles the fragments state on its own when rotating the device. And it seems to be everything fine just until I want to reload/update its content.
Ok let’s dig deeper into that.
On first start I got some empty main-container. Then I am loading my article-fragment for the first time. Just getting the SupportFragmentAdapter, creating my ArticleFragment and replace the main-container with the newly created fragment - tagged. No rocket-science - just a simple transaction:
ViewPagerFragment pagerFragment = (ViewPagerFragment)
getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT_ARTICLE);
if(pagerFragment != null){
if(pagerFragment.isResumed()){
pagerFragment.triggerReload();
}
} else {
pagerFragment = new ViewPagerFragment();
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.replace(R.id.id_main_root_frame, pagerFragment, TAG_FRAGMENT_ARTICLE);
t.commitAllowingStateLoss();
}
To avoid creating a fragment each time I reload my content, I’m trying to fetch the fragment before the transaction and - if it is found and resumed - trigger some reload on the existing fragment.
Now I rotate my device in this state. To avoid messing with the fragment state I left onSaveInstanceState() inside the fragment untouched. So I guess the fragment is just destroyed and recreated. And everything still works so far. But I think this part has something of a black box.
After that - normal startup, creating fragment and put into main-container, rotating device - I trigger some update. But instead of finding the old (recreated) fragment by tag, nothings found and a new fragment is created and inserted. At least tried to be inserted, because this is where I got the following exception:
java.lang.IllegalStateException: Activity has been destroyed
To be precise, I get the above exception when finish my transaction with a commitAllowingStateLoss(). When I just commit() the transaction I get the following exception:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
So that’s where the error comes up. And after ages of debugging and searching I found some hint on this question/answer to get the SupportFragmentManager on a WeakReference of my MainActivity. And what should I say. Since I implemented that, it works. I had to change my update-process a bit, but it works. But leaves some questions ...
The behaviour seems to be similiar. First creation works perfect. Reload just the same - fragment is found by tag. After rotation, article is still shown. And when I reload the fragment with that state it is not found by tag so a new one is created, but the commit()-request does not throw an exception. A look inside the debugger shows that the WeakReference is some other instance (other id), than the one(this) all of this takes place in. And thats where I lose the plot. ..
If some of you could give me some hints, would be great!
Thanks in advance!
try this:
commitAllowingStateLoss(); instead commit();

Not all Views inside ViewPager's Fragments will update

I have 3 Fragments inside my ViewPager.
Inside my Fragments I added static ToggleButtons.(static, because I didn't find a workaround for a nullpointerexception, if I wanted to use findViewById() inside my Fragment classes in custom methods and not in onCreateView() )
Outside the ViewPager in my MainActivity I have a Reset-Button, that should reset all ToggleButtons (on all 3 Fragments) to "unchecked".
Every time i click on the Reset-Button, only the current Page and every neighbor-page gets updated.
E.G.
Current Page : 0 Updated Pages: 0,1
Current Page : 1 Updated Pages: 0,1,2
Current Page : 2 Updated Pages: 1,2
I think the Problem is the FragmentPagerAdapter. The Documentation says :
Implementation of PagerAdapter that represents each page as a Fragment
that is persistently kept in the fragment manager as long as the user
can return to the page.
When I'm on Page 2 i can not directly return to Page 0 , so I think the Views from Page 0 (= Fragment 0 ) are not in memory anymore?!
So how can I access the ToggleButtons inside a Fragment that is not visible at the moment nor is a neighbor of the current Fragment ? Is there any workaround?
EDIT:
I found out, that in deed all checked()-values of my ToggleButtons (on ALL Fragments) get updated, but not the inflated Views of Fragments, that are not visible and not neighbor Fragments. So when I return to those previous Fragments, the checked()-values of the ToggleButtons are reset to the state last time the fragment was visible. strange...
Example:
Page2 active - ToggleButton_Page2.checked() = true
switch to Page 0
press Reset-Button. Toast ToggleButton_Page2.checked() = false
switch back to Page 2 (or already at Page 1)
ToggleButton_Page2.checked() = true //it should be false
SOURCE:
Example Source Code
So how can I access the ToggleButtons inside a Fragment that is not
visible at the moment nor is a neighbor of the current Fragment ? Is
there any workaround?
You have a lot of problems in your code, you should read a bit more about Fragments and using them in a ViewPager.
In the reset and status buttons OnClickListeners listeners you write _adapter.getItem(x); in an attempt to get a handle to the Fragment representing the page at that position. This will not work as simply calling that method will return a new instance of the Fragment at that position and not the actual Fragment the ViewPager used(by calling getItem() at previous moment). That new instance you get after calling getItem() is not tied to the Activity and its onCreateView method wasn't called so it has no view(and you get the NullPointerException when you access it). You then tried to get around this by making the ToggelButton as a static field in the Fragment which will work as the field will be initialized when the ViewPager properly creates the Fragments at start. But you shouldn't do this, static fields that hold references to the Context(like any View does) are dangerous as you risk leaking that Context.
Also, related to what I said above, you don't need to pass a Context to a Fragment as a Fragment which is tied to an Activity has the getActivity() method which returns a reference to that Activity.
You shouldn't access any fragments from the ViewPager which aren't near to the visible fragment(one on the left/right of the visible position, unless you don't play with the setOffscreenPageLimit() method of the ViewPager). The ViewPager has a mechanism to improve performance(like a ListView does) so it only creates what it needs immediately so it can provide a smooth swipe for the user.
I've made some changes to your project, check them out. For further questions please post the relevant code in your question.
Salut,
Thank you for your good information, and the improved code!
"But you shouldn't do this, static fields that hold references to the
Context(like any View does) are dangerous as you risk leaking that
Context."
I'm new to Fragment implementation, and spent hours of finding out how the input String is correctly used for findFragmentByTag().(Now knowing how to use it I never thought that it would be that complex). So I decided to do that static workaround, which wasn't a good idea...
As I understand you, i CAN access more then +-1 Fragments, for example if I use setOffscreenPageLimit(3). I think this is the answer to my question.

My fragments in viewpager tab dont refresh

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

Categories

Resources