# #解决 Viewpager Call notifydatasetchanged () No refresh: Principle, Workaround # #

Source: Internet
Author: User

First, the principle

Transferred from: http://www.cnblogs.com/maoyu417/p/3740209.html

Reprint http://www.67tgb.com/?p=624

Recently the project was over and a code share was made. One of the students shared some of their experience in the process of solving problems, feeling benefited. Sort it out and share it with everyone.

It is recommended that you use your own compiled Android OS and virtual machines so that you can debug any component on your Android system. To put it simply, go deep into the Android source code to find answers to your questions. It's a simple thing to say, and it's still a little difficult to actually do. I have also tried to see, not a moment to faint.

So there are targeted to see the source code, the efficiency will be higher.

Don't say much nonsense, first look at the first example.

Viewpager the interface is not refreshed when calling notifydatasetchanged ().

I believe many students who have done Viewpager must have encountered this problem, this is a bug or Android is so designed, we do not discuss. In short, it does affect the implementation of our function.

Many students may choose to reset the adapter adapter for Viewpager, to achieve the purpose of the refresh. But this approach is problematic in most cases.

TrackingSource Code:

Why call the method of data update, Viewpager but not update it, we follow up the source of the method to look at.

First look at the super.notifydatasetchanged () of the adapter call, which is transferred to the abstract base class pageradapter.notifydatasetchanged ():

    /**
     * This method should is called by the application if the data backing this adapter have changed
     * and associated views should update.
     */
    void Notifydatasetchanged () {
        Mobservable. notifychanged ();
    }

Note that when the data attached to the adapter changes, the method should be called to refresh the data. The method calls a mobservable. notifychanged ();

We continue to follow this method into the Datasetobservable class and find such a piece of code:

  /**
     * Invokes {@link datasetobserver#onchanged} on each observer.
     * Called when the contents of the data set has changed.  The recipient
     * would obtain the new contents the next time it queries the data set.
     */
    void Notifychanged () {
        Synchronized (mobservers) {
            Since OnChanged () is implemented by the app, it could do anything, including
            Removing itself from {@link Mobservers}-and that could cause problems if
            An iterator was used on the ArrayList {@link mobservers}.
            To avoid such problems, just March thru the list in the reverse order.
            for (int i = mobservers. Size ()-1; I >= 0; i--) {
                Mobservers.get (i). onChanged ();
            }
        }
    }

That's not the point. The mobservers type is an abstract class datasetobserver, there are only two non-implemented methods, who use this abstract class, shortcut keys CTRL + ALT + H, among the many callers, We found the Viewpager figure.

Entering Viewpager, we finally found the key method of controlling data change in Viewpager datasetchanged, this method is as follows:

      datasetchanged () {
        This method only gets called if we observer is attached, so madapter is non-null.
        Boolean needpopulate = mitems. Size () < Moffscreenpagelimit * 2 + 1 &&
                Mitems.size () < Madapter.getcount ();
        int newcurritem = Mcuritem;
        False
        for (int i = 0; i < mitems.size (); i++) {
            Final ItemInfo II = Mitems. get (i);
            int newpos = madapter.getitemposition (Ii.  object);
            if (Newpos = = pageradapter.position_unchanged) {
                Continue
            }
            if (Newpos = = Pageradapter.position_none) {
                Mitems.remove (i);
                i--;
                if (!isupdating) {
                    this);
                    True
                }
                This, Ii.position, II. object);
                True
                if (Mcuritem = = ii.position) {
                    Keep the current item in the valid range
                    Newcurritem = Math. Max (0, Math.min (Mcuritem, Madapter.getcount ()-1));
                    True
                }
                Continue
            }
            if (ii.position! = Newpos) {
                if (ii.position = = Mcuritem) {
                    Our current item changed position. Follow it.
                    Newcurritem = Newpos;
                }
                II. Position = Newpos;
                True
            }
        }
        if (isupdating) {
            this);
        }
        Collections. Sort (Mitems, COMPARATOR);
        if (needpopulate) {
            Reset our known page widths; Populate'll recompute them.
            int childCount = Getchildcount ();
            for (int i = 0; i < ChildCount; i++) {
                Final View child = Getchildat (i);
                Final Layoutparams LP = (layoutparams) child.getlayoutparams ();
                if (!lp.isdecor) {
                    Lp. Widthfactor = 0.F;
                }
            }
            true);
            Requestlayout ();
        }
    }

Focus on this line of code:

int newpos = madapter.getitemposition (Ii.  object);
   if (Newpos = = pageradapter.position_unchanged) {
         Continue;
   }

The official explanation for GetItemPosition () is:

Called when the host view was attempting to determine if a item ' s position has changed. Returns position_unchanged If the POSITION of the given item has not changed Orposition_none if the item is no longer pres ENT in the adapter.

The default implementation assumes that items would never change position and always returns position_unchanged.

This means that if the position of the item does not change, the position_unchanged is returned. If Position_none is returned, the item for that location does not already exist. The default implementation is to assume that the position of item will never change and return position_unchanged

Solution:

So we can try to modify the adapter notation, overwriting the GetItemPosition () method, when calling Notifydatasetchanged, let the GetItemPosition method artificially return Position_none, Thus achieving the purpose of forcing Viewpager to redraw all item.

The specific code is as follows:

Class Searchadapter extends Pageradapter {
    
     int mchildcount = 0;
     @Override
     void Notifydatasetchanged () {         
           Mchildcount = GetCount ();
           Super.notifydatasetchanged ();
     }
     @Override
     object)   {          
           if (Mchildcount > 0) {
           Mchildcount--;
           return Position_none;
           }
           Return Super.getitemposition (object);
     }
}



Ii. Solutions
Transferred from: http://www.aiuxian.com/article/p-2786024.html

Google's Viewpager control in Android 3.0SDK largely meets the developer's ability to move around the page, which is easy to use. However, it is found in use that, when deleting or modifying data, Pageradapter is unable to refresh the view only through notifydatasetchanged methods as Baseadapter.
The most basic method :
For the simple case of child view (for example, only TextView, ImageView, etc., without the display of data such as a ListView), you can add code to your own adapter:

[Java] View plain copy print?
    1. @Override
    2. public int getitemposition(Object object) {
    3. return Position_none;
    4. }
This will not only achieve the desired effect under normal circumstances.
Problems that exist:
This is not a bug in Pageradapter, typically, calling the Notifydatasetchanged method will allow Viewpager to query through all child view through adapter's GetItemPosition method, in which case , all child view locations are Position_none, indicating that all child view does not exist, Viewpager calls the Destroyitem method to destroy, rebuilds, increases overhead, and causes logic problems in some complex situations. In particular, there is a completely unnecessary overhead when you just want to update the child view content.
More Efficient methods:
A more reliable approach is to adapt to local conditions, according to their own needs to achieve notifydatasetchanged function, for example, when only need to update a view content, at Instantiateitem (), Using the View.settag method to add the flag, when the need to update information, through the Findviewwithtag method to find the corresponding view to update.

When you use Viewpager to slide the effect of a picture, if the picture is downloaded from the network, then we use handler to notify the UI thread when the picture is downloaded from the child thread, and then the UI thread can call Mviewpager.getadapter (). Notifydatasetchanged () To refresh the page, but Viewpager is different from the ListView, you will find that simply calling notifydatasetchanged () does not refresh the page. Let's talk about the Viewpager refresh process: The GetItemPosition (Object object) method is activated each time notifydatasetchanged () is called. The method iterates through all of the Viewpager's item (according to my debug results, only the current page and its left and right add up to a total of 3 pages are traversed, to be determined), return a status value (position_none/position_unchanged) for each item, If it is none, then the item will be removed by the Destroyitem (ViewGroup container, int position, Object object) method, then reloaded, and if it is unchanged, it will not reload , the default is unchanged, so our country we do not rewrite GetItemPosition (Object object), you cannot see the refresh effect. There are two ways to resolve this:
The first kind of online comparison is easy to find: Rewrite Pageradapter's GetItemPosition (Object object) method to return it Position_none

Java Code link Address
    1. @Override
    2. public int getitemposition(Object object) {
    3. return Position_none;
    4. }

The drawbacks of this method are easy to see, I do not need to refresh the project has been reloaded, wasting system resources;
The second is more reasonable, of course, compared to the previous one to do more things: the idea is to add tag to each view when Instantiateitem, Then, when the page needs to be refreshed by View.gettag () to determine whether we want to refresh the page, only the current page to return Position_none.
Java Code link Address
  1. /**
  2. * Dispimgadapter.java
  3. */
  4. @Override
  5. Public Object Instantiateitem (ViewGroup container, int position) {
  6. IV = new ImageView (mcontext);
  7. Iv.settag (position); //ADD tag
  8. try {
  9. Bitmap BM = CACHEIMG2 (position);
  10. Iv.setimagebitmap (BM);
  11. } catch (OutOfMemoryError e) {
  12. E.printstacktrace ();
  13. }
  14. ((Viewpager) container). AddView (iv);
  15. return IV;
  16. }
  17. @Override
  18. public int GetItemPosition (Object object) {
  19. View view = (view) object;
  20. int currentpage = ((dispimgactivity) mcontext). Getcurrentpageridx (); //Get Current page index
  21. if (currentpage = = (Integer) View.gettag ()) {
  22. return position_none;
  23. }else{
  24. return position_unchanged;
  25. }
  26. return position_none;
  27. }

The key currentpageidx needs to be captured in the activity, and if your adapter is the inner class of activity, you can use the index as a global variable in adapter, if it's a separate two class, Then you provide an interface for you to pass index to adapter.
Java Code link Address
  1. /**
  2. * Dispimgactivity.java
  3. */
  4. Get Current page Index
  5. Mviewpager.setonpagechangelistener (new Onpagechangelistener () {
  6. @Override
  7. Public void onpagescrolled (int i, float f, Int j) {
  8. }
  9. @Override
  10. Public void onpageselected (int position) {
  11. Dispimgactivity. this.position = position;
  12. }
  13. @Override
  14. Public void onpagescrollstatechanged (int i) {
  15. }
  16. });
  17. Return Current Index to Adapter
  18. public int getcurrentpageridx () {
  19. return position;
  20. }
PS: I also added the function of the picture download progress bar in my project, when I use the second method, in some extreme cases there will be a little problem,
Assuming that all the images need to be downloaded from the Web, when the page is very fast sliding, it is found that occasionally the asynchronous download to the image is not refreshed display, the slide over a few pages back to the page when the picture is refreshed,
Here is the key issue is "viewpager pre-loading mechanism + image asynchronous download +getitemposition in the judgment of tag", I think it is the combination of these mechanisms and then quickly switch the page caused by the problem,



# #解决 Viewpager Call notifydatasetchanged () No refresh: Principle, Workaround # #

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.