diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 6e1dff9ea0def..28ade80a260e1 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2322,7 +2322,8 @@ public abstract class AbsListView extends AdapterView implements Te * * @param position The position to display * @param isScrap Array of at least 1 boolean, the first entry will become true if - * the returned view was taken from the scrap heap, false if otherwise. + * the returned view was taken from the "temporary detached" scrap heap, false if + * otherwise. * * @return A view displaying the data associated with the specified position */ @@ -2362,10 +2363,18 @@ public abstract class AbsListView extends AdapterView implements Te // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); } else { - isScrap[0] = true; + if (child.isTemporarilyDetached()) { + isScrap[0] = true; + + // Finish the temporary detach started in addScrapView(). + child.dispatchFinishTemporaryDetach(); + } else { + // we set isScrap to "true" only if the view is temporarily detached. + // if the view is fully detached, it is as good as a view created by the + // adapter + isScrap[0] = false; + } - // Finish the temporary detach started in addScrapView(). - child.dispatchFinishTemporaryDetach(); } } @@ -5152,6 +5161,7 @@ public abstract class AbsListView extends AdapterView implements Te fillGap(down); } + mRecycler.fullyDetachScrapViews(); if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { @@ -6861,8 +6871,8 @@ public abstract class AbsListView extends AdapterView implements Te scrapViews = mScrapViews[whichScrap]; } - victim.dispatchStartTemporaryDetach(); lp.scrappedFromPosition = mFirstActivePosition + i; + removeDetachedView(victim, false); scrapViews.add(victim); if (hasListener) { @@ -6871,10 +6881,28 @@ public abstract class AbsListView extends AdapterView implements Te } } } - pruneScrapViews(); } + /** + * At the end of a layout pass, all temp detached views should either be re-attached or + * completely detached. This method ensures that any remaining view in the scrap list is + * fully detached. + */ + void fullyDetachScrapViews() { + final int viewTypeCount = mViewTypeCount; + final ArrayList[] scrapViews = mScrapViews; + for (int i = 0; i < viewTypeCount; ++i) { + final ArrayList scrapPile = scrapViews[i]; + for (int j = scrapPile.size() - 1; j >= 0; j--) { + final View view = scrapPile.get(j); + if (view.isTemporarilyDetached()) { + removeDetachedView(view, false); + } + } + } + } + /** * Makes sure that the size of mScrapViews does not exceed the size of * mActiveViews, which can happen if an adapter does not recycle its @@ -6888,10 +6916,8 @@ public abstract class AbsListView extends AdapterView implements Te for (int i = 0; i < viewTypeCount; ++i) { final ArrayList scrapPile = scrapViews[i]; int size = scrapPile.size(); - final int extras = size - maxViews; - size--; - for (int j = 0; j < extras; j++) { - removeDetachedView(scrapPile.remove(size--), false); + while (size > maxViews) { + scrapPile.remove(--size); } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index bb1ffcb2d5c6a..0e04e3003d7c9 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -54,6 +54,7 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.widget.RemoteViews.RemoteView; import java.util.ArrayList; +import java.util.List; /* * Implementation Notes: @@ -1763,6 +1764,10 @@ public class ListView extends AbsListView { // Flush any cached views that did not get reused above recycleBin.scrapActiveViews(); + // remove any header/footer that has been temp detached and not re-attached + removeUnusedFixedViews(mHeaderViewInfos); + removeUnusedFixedViews(mFooterViewInfos); + if (sel != null) { // The current selected item should get focus if items are // focusable. @@ -1879,6 +1884,36 @@ public class ListView extends AbsListView { } } + @Override + boolean trackMotionScroll(int deltaY, int incrementalDeltaY) { + final boolean result = super.trackMotionScroll(deltaY, incrementalDeltaY); + removeUnusedFixedViews(mHeaderViewInfos); + removeUnusedFixedViews(mFooterViewInfos); + return result; + } + + /** + * Header and Footer views are not scrapped / recycled like other views but they are still + * detached from the ViewGroup. After a layout operation, call this method to remove such views. + * + * @param infoList The info list to be traversed + */ + private void removeUnusedFixedViews(@Nullable List infoList) { + if (infoList == null) { + return; + } + for (int i = infoList.size() - 1; i >= 0; i--) { + final FixedViewInfo fixedViewInfo = infoList.get(i); + final View view = fixedViewInfo.view; + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + if (view.getParent() == null && lp != null && lp.recycledHeaderFooter) { + removeDetachedView(view, false); + lp.recycledHeaderFooter = false; + } + + } + } + /** * @param child a direct child of this list. * @return Whether child is a header or footer view. @@ -3179,6 +3214,9 @@ public class ListView extends AbsListView { last = getChildAt(--lastIndex); } } + recycleBin.fullyDetachScrapViews(); + removeUnusedFixedViews(mHeaderViewInfos); + removeUnusedFixedViews(mFooterViewInfos); } private View addViewAbove(View theView, int position) {