Recently, the project was completed and a code sharing was conducted. One of the students shared some of their experiences in solving the problem. Sort it out and share it with you.
We recommend that you use your own android OS and virtual machine to debug any components in the android system. Simply put, go deep into the android source code to find the answer to the problem. This is simple to say, but it is still difficult to do it. I also tried to see it, and I was dizzy after a while.
Therefore, it is more efficient to view the source code.
Not to mention nonsense. Let's look at the first example first.
When Viewpager calls notifyDataSetChanged (), the interface is refreshed.
I believe that many people who have done Viewpager must have encountered this problem. This is a bug or android design. We will not discuss it. In short, it does affect our function implementation.
Many may choose to reset the adapter for Viewpager to refresh the page. However, this method is problematic in most cases.
Tracing source code:
Why does Viewpager fail to update the method of data update? Let's take a look at the source code of this method.
First, check the super. notifyDataSetChanged () method called by the adapter. This method is adjusted to the abstract base class PagerAdapter. notifyDataSetChanged:
/** * This method should be called by the application if the data backing this adapter has changed * and associated views should update. */ public void notifyDataSetChanged() { mObservable.notifyChanged(); }
This method should be called to refresh the data when the data appended to the adapter changes. This method calls a mObservable. policychanged ();
We continue to follow up on this method and enter the DataSetObservable class. We found the following code:
/** * Invokes {@link DataSetObserver#onChanged} on each observer. * Called when the contents of the data set have changed. The recipient * will obtain the new contents the next time it queries the data set. */ public 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 is 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(); } } }
Let's take a look at who used this abstract class. The shortcut key ctrl + alt + H has found Viewpager among many callers:
void dataSetChanged () { // This method only gets called if our observer is attached, so mAdapter is non-null. boolean needPopulate = mItems .size() < mOffscreenPageLimit * 2 + 1 && mItems.size() < mAdapter.getCount(); int newCurrItem = mCurItem ; boolean isUpdating = false; for (int i = 0; i < mItems.size(); i++) { final ItemInfo ii = mItems .get(i); final int newPos = mAdapter.getItemPosition(ii.object ); if (newPos == PagerAdapter.POSITION_UNCHANGED ) { continue; } if (newPos == PagerAdapter.POSITION_NONE) { mItems.remove(i); i--; if (!isUpdating) { mAdapter.startUpdate( this); isUpdating = true; } mAdapter.destroyItem( this, ii.position , ii.object); needPopulate = true; if (mCurItem == ii.position ) { // Keep the current item in the valid range newCurrItem = Math. max(0, Math.min(mCurItem, mAdapter.getCount() - 1)); needPopulate = true; } continue; } if (ii.position != newPos) { if (ii.position == mCurItem ) { // Our current item changed position. Follow it. newCurrItem = newPos; } ii. position = newPos; needPopulate = true; } } if (isUpdating) { mAdapter.finishUpdate( this); } Collections. sort(mItems, COMPARATOR); if (needPopulate) { // Reset our known page widths; populate will recompute them. final 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; } } setCurrentItemInternal(newCurrItem, false, true); requestLayout(); } }
Focus on this line of code:
final int newPos = mAdapter.getItemPosition(ii.object ); if (newPos == PagerAdapter.POSITION_UNCHANGED ) { continue ; }
I carefully read the general idea of this Code. The official explanation is:
Called when the host view is attempting to determine if an item's position has changed. returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.
Solution: we can try to modify the method of the adapter to overwrite the getItemPosition () method. When notifyDataSetChanged is called, The getItemPosition method returns POSITION_NONE manually, this forces viewpager to redraw all items.
The Code is as follows:
class SearchAdapter extends PagerAdapter { private int mChildCount = 0; @Override public void notifyDataSetChanged() { mChildCount = getCount(); super.notifyDataSetChanged(); } @Override public int getItemPosition(Object object) { if ( mChildCount > 0) { mChildCount --; return POSITION_NONE; } return super.getItemPosition(object); } }
You can try it.