why does calling fragmentpageradapter.notifydatasetchanged () not update its Fragment?
Transferred from: http://www.apkbus.com/android-90417-1-1.html
Aitian published on 2013-1-4 17:31:57browse (386111)in an Android app, I use fragmentpageradapter to handle cross-slide of multiple Fragment pages. But I ran into the problem that when the Fragment corresponding dataset changed, I wanted to be able to trigger the Fragment page to use the new data to adjust or regenerate its contents by calling Madapter.notifydatasetchanged (), but when I called Notifydatasetchanged () and found nothing happened.
after the search found that not only I have encountered this problem, we have given a variety of solutions, some did solve the problem, but I always feel that the problem is not clear. So I decided to figure out what the problem was and what the correct usage was. To understand this problem, it is not enough to read the document, but also to read the implementation of the relevant methods of several related classes to understand its design intent. Here's what you can read through the source code.
"Viewpager"
Viewpager is a View that is responsible for paging, as described in its name. It's exactly a viewgroupthat contains multiple view pages, which are responsible for switching the view when the finger is sliding horizontally across the screen. To generate these view pages, you need to provide a pageradapter to do and data binding and to generate the final view page.
- setadapter ()
- Viewpager through Setadapter () to establish a contact with Pageradapter. This connection is bidirectional, and on the one hand, Viewpager will have Pageradapter object , which allows you to invoke the Pageradapter method when needed; On the other hand, Viewpager will Call Pageradapter's Registerdatasetobserver () method in Setadapter (), register a self-generated Pagerobserver object to When Pageradapter is needed (such as notifydatasetchanged () or notifydatasetinvalidated (), you can call Observer onChanged () or Oninvalidated () method, which enables Pageradapter to send information to Viewpager direction.
- Datasetchanged ()
- Called in pagerobserver. onChanged (), and Pagerobserver.oninvalide (). So when pageradapter.notifydatasetchanged () is triggered, viewpager.datasetchanged () can also be triggered. The function will use the return value of GetItemPosition () to determine if it is position_unchanged, and if it is position_none, call Pageradapter.destroyitem () to remove the object and set it to need to refresh (Needpopulate = True) to trigger the Pageradapter.instantiateitem () to generate a new object.
"Pageradapter"
PageAdapteris a supporter of Viewpager, Viewpager will call it to get the page to be displayed, and PageAdapter will notify Viewpager when the data changes. This class is alsoFragmentpageradapterAndFragmentstatepageradapterThe base class. If inherited from this class, you need to implement at least Instantiateitem (), Destroyitem (), GetCount (), and Isviewfromobject ().
- GetItemPosition ()
- The function is used to return the position of the given object, which is the return value of Instantiateitem ().
- The return value of the function is judged in viewpager.datasetchanged () to determine whether the Pageradapter.instantiateitem () function will eventually be triggered.
- The implementation in Pageradapter is directly passed back to position_unchanged. If the function is not overloaded, it will always return position_unchanged, causing viewpager.datasetchanged () to be called when it is not necessary to trigger Pageradapter.instantiateitem (). Many people cause the call to occur because the function is not overloaded
After pageradapter.notifydatasetchanged (), nothing happened.
- Instantiateitem ()
- The function is viewpager every time Viewpager needs an Object to display. AddNewItem () call.
- Notifydatasetchanged ()
- When the data set changes, the general Activity calls Pageradapter.notifydatasetchanged () to notify Pageradapter, and Pageradapter notifies him that he has registered all the datasetobserver. One of these is the Pageobserver registered in Viewpager.setadapter (). Pageobserver then calls Viewpager.datasetchanged (), which causes the Viewpager to start triggering updates to its included View operation.
"Fragmentpageradapter"
Fragmentpageradapter inherits from Pageradapter. This class is more focused on Fragment per page than the generic Pageradapter. As the document describes,
each generated Fragment in the class will be stored in memory , so it is suitable for those relatively static pages, the number is less, if you need to deal with a lot of pages, and the data is more dynamic, memory-intensive situation, you should use Fragmentstatepageradapter. Fragmentpageradapter overloads implement several necessary functions, so the function from Pageradapter, we only need to implement GetCount (), can. And, because of the implementation of Fragmentpageradapter.instantiateitem (), a new virtual function GetItem () is called, so we also need to implement at least one GetItem (). Therefore, in general, compared to inheriting from Pageradapter, it is more convenient.
- getItem ()
- A new virtual function in the class. The purpose of the function is to generate a new fragment object. You need to be aware of this when overloading the function. When needed, the function is called by Instantiateitem ().
- If you need to pass relative static data to a Fragment object, we typically do this through fragment.setarguments (), which should be placed in GetItem (). They are only executed once when the Fragment object is newly generated.
- If you need to pass some dynamic Data in the dataset to the Fragment after the Fragment object is generated, this part of the code is not suitable for getItem (). Because when the data set changes, often the corresponding Fragment has been generated, if the passing data part of the code into the GetItem (), this part of the code will not be called. This is also why many people find that after calling Pageradapter.notifydatasetchanged (), GetItem () is not called for a reason.
- Instantiateitem ()
- function to determine whether the generated Fragment has been generated, if generated, use the old, the old will be Fragment.attach (), if not, call GetItem () to generate a new , the new object will be Fragmenttransation.add ().
- Fragmentpageradapter will save all the generated Fragment objects through Fragmentmanager, and will be read from Fragmentmanager when the Fragment is needed, and will not be called again GetItem () method .
- If you need to pass some data from the dataset to the Fragment after generating the Fragment object, this part of the code should be placed in the overload of the function. In our inherited subclass, overload the function and call Fragmentpageradapter.instantiateitem () to get the function to return the Fragment object, and then we Fragment the corresponding method in the object to pass the data past, The object is then returned.
- Otherwise, if you put this part of the data passing code into GetItem (), after pageradapter.notifydatasetchanged (), this part of the data set code will not be called.
- Destroyitem ()
- After the function is called, the Fragment is Fragmenttransaction.detach (). This is not remove (), only detach (), so Fragment is still in Fragmentmanager management, and the resources that Fragment occupies are not released.
span>
"Fragmentstatepageradapter"
Fragmentstatepageradapter , like the previous Fragmentpageradapter, is an inherited child pageradapter. However, unlike Fragmentpageradapter, as the "state" in its class name implies, the implementation of the Pageradapter will only retain the current page, and when the page leaves the line of sight, it will be eliminated, releasing its resources, and when the page needs to be displayed, Generate a new page (just like the implementation of a ListView). The benefit of this implementation is that when you have a large number of pages, you do not have to consume large amounts of memory in memory.
- GetItem ()
- A new virtual function in the class.
- The purpose of the function is to generate a new Fragment object.
- Fragment.setarguments () is a parameter passing code that executes only once when a new Fragment is created, and can be placed here.
- Because Fragmentstatepageradapter.instantiateitem () will call GetItem () in most cases to generate a new object, if you place the setter code associated with the dataset in the function, you can basically Instantiateitem () is invoked when executed, but this is inconsistent with the design intent. After all, there may be parts that don't call GetItem (). So this part of the code should be put into Instantiateitem ().
- Instantiateitem ()
- The function will invoke the GetItem () function to generate a new Fragment object unless it encounters fragmentmanager that the corresponding Fragment is restored from the savedstate. The new object will be Fragmenttransaction.add ().
- In this way, Fragmentstatepageradapter creates a new Fragment every time, and frees up its resources as soon as it is not used, to save the memory footprint.
- Destroyitem ()
- Remove Fragment, call fragmenttransaction.remove (), and release its resources.
discussion
Before seeing some solutions, some think it is aBug, should be repaired;RecommendedInstead of Fragmentpageradapter, use Fragmentstatepageradapter and Reload GetItemPosition () and return Position_none to trigger destroying the object and rebuilding the object. From the above analysis, the latter's recommendations do reach the effect of Fragment being re-established with new parameters after calling Notifydatasetchanged ().
But the question is, if we can only solve this problem, wouldn't it be fragmentpageradapter? The most critical is that they correspond to different situations. For a relatively small number of pages, I still want to be able to save the generated Fragment in memory and call it directly when it needs to be displayed, rather than generating additional overhead to generate and destroy objects, which is more efficient. In this case, choosing fragmentpageradapter is more suitable, without considering the choice of fragmentstatepageradapter is inappropriate. We are not able to unworthy.
Therefore, the solution for Fragmentpageradapter is to overload the GetItem () and Instantiateitem () objects respectively. GetItem () is used only to generate new data-independent Fragment, whereas the Instantiateitem () function invokes the Instantiateitem () in the parent class to obtain the corresponding Fragment object, and then, based on the corresponding data, Call the method that corresponds to the object to set the data.
Of course, don't forget to overload the GetItemPosition () function and return to Position_none, which is required for this two-class solution. The difference is that fragmentstatepageradapter re-establishes a new Fragment in the real release of resources in the Destroyitem () that triggered the call because of Position_none; Fragmentpageradapter will only detach this Fragment in Destroyitem (), use the old Instantiateitem when Fragment (), and trigger Attach, Thus there is no process of releasing resources and rebuilding them.
Thus, when notifydatasetchanged () is called, it will eventually trigger Instantiateitem (), regardless of whether GetItem () is called, we are in the overloaded Instantiateitem () The required data has been passed to the corresponding Fragment in the function. In the event of Fragment next Oncreateview (), OnStart () and Onresume (), it can read the new data correctly, and the Fragment is successfully reused.
One thing to note here is that before Fragment is added to Fragmentmanager, we can set the parameters by Fragment.setarguments () and use Getarguments () in Fragment. To get the parameters. This is a common way to pass parameters. But this approach does not apply to the situation we are talking about. Because this data transfer method can only be used once, after Fragment is added to Fragmentmanager, once used, we call Setarguments again () will cause
Java.lang.IllegalStateException:Fragment already activeAbnormal. Therefore, our choice of parameter passing method is that in the inherited Fragment subclass, a few setters are added, and then the data is passed through these setters. The reverse is similar. The relevant information can be found in [5]. Well, these setters should be careful not to manipulate those view, which can only be manipulated after the Oncreateview () event.
The workaround for Fragmentpageradapter is as shown in the following code:
[Java]Plain Text view copy code ?
010203040506070809101112131415161718 |
@Override
public Fragment getItem(
int position) {
MyFragment f =
new MyFragment();
return f;
}
@Override
public Object instantiateItem(ViewGroup container,
int position) {
MyFragment f = (MyFragment)
super
.instantiateItem(container, position);
String title = mList.get(position);
f.setTitle(title);
return f;
}
@Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
|
Reference
Android Documentation:
[1]http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html
[2]http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
[3]http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html
[4]http://developer.android.com/reference/android/support/v4/view/ViewPager.html
[5]http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
Android Source code:
[6]Http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/ Support/v4/view/pageradapter.java#pageradapter
[7]Http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/ Support/v4/app/fragmentpageradapter.java#fragmentpageradapter
[8]Http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/ Support/v4/app/fragmentstatepageradapter.java#fragmentstatepageradapter
[9]Http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/ Support/v4/view/viewpager.java#viewpager
Android Issue List:
[10]http://code.google.com/p/android/issues/detail?id=19001
Why does "Go" call fragmentpageradapter.notifydatasetchanged () and cannot update its Fragment?