Loading core/java/android/widget/AbsListView.java +36 −10 Original line number Original line Diff line number Diff line Loading @@ -2322,7 +2322,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * * @param position The position to display * @param position The position to display * @param isScrap Array of at least 1 boolean, the first entry will become true if * @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 * @return A view displaying the data associated with the specified position */ */ Loading Loading @@ -2362,10 +2363,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Failed to re-bind the data, return scrap to the heap. // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); mRecycler.addScrapView(scrapView, position); } else { } else { if (child.isTemporarilyDetached()) { isScrap[0] = true; isScrap[0] = true; // Finish the temporary detach started in addScrapView(). // Finish the temporary detach started in addScrapView(). child.dispatchFinishTemporaryDetach(); 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; } } } } } Loading Loading @@ -5152,6 +5161,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te fillGap(down); fillGap(down); } } mRecycler.fullyDetachScrapViews(); if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { if (childIndex >= 0 && childIndex < getChildCount()) { Loading Loading @@ -6861,8 +6871,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te scrapViews = mScrapViews[whichScrap]; scrapViews = mScrapViews[whichScrap]; } } victim.dispatchStartTemporaryDetach(); lp.scrappedFromPosition = mFirstActivePosition + i; lp.scrappedFromPosition = mFirstActivePosition + i; removeDetachedView(victim, false); scrapViews.add(victim); scrapViews.add(victim); if (hasListener) { if (hasListener) { Loading @@ -6871,10 +6881,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } } } } pruneScrapViews(); 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<View>[] scrapViews = mScrapViews; for (int i = 0; i < viewTypeCount; ++i) { final ArrayList<View> 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 * Makes sure that the size of mScrapViews does not exceed the size of * mActiveViews, which can happen if an adapter does not recycle its * mActiveViews, which can happen if an adapter does not recycle its Loading @@ -6888,10 +6916,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te for (int i = 0; i < viewTypeCount; ++i) { for (int i = 0; i < viewTypeCount; ++i) { final ArrayList<View> scrapPile = scrapViews[i]; final ArrayList<View> scrapPile = scrapViews[i]; int size = scrapPile.size(); int size = scrapPile.size(); final int extras = size - maxViews; while (size > maxViews) { size--; scrapPile.remove(--size); for (int j = 0; j < extras; j++) { removeDetachedView(scrapPile.remove(size--), false); } } } } Loading core/java/android/widget/ListView.java +38 −0 Original line number Original line Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.widget.RemoteViews.RemoteView; import android.widget.RemoteViews.RemoteView; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; /* /* * Implementation Notes: * Implementation Notes: Loading Loading @@ -1763,6 +1764,10 @@ public class ListView extends AbsListView { // Flush any cached views that did not get reused above // Flush any cached views that did not get reused above recycleBin.scrapActiveViews(); recycleBin.scrapActiveViews(); // remove any header/footer that has been temp detached and not re-attached removeUnusedFixedViews(mHeaderViewInfos); removeUnusedFixedViews(mFooterViewInfos); if (sel != null) { if (sel != null) { // The current selected item should get focus if items are // The current selected item should get focus if items are // focusable. // focusable. Loading Loading @@ -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<FixedViewInfo> 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. * @param child a direct child of this list. * @return Whether child is a header or footer view. * @return Whether child is a header or footer view. Loading Loading @@ -3179,6 +3214,9 @@ public class ListView extends AbsListView { last = getChildAt(--lastIndex); last = getChildAt(--lastIndex); } } } } recycleBin.fullyDetachScrapViews(); removeUnusedFixedViews(mHeaderViewInfos); removeUnusedFixedViews(mFooterViewInfos); } } private View addViewAbove(View theView, int position) { private View addViewAbove(View theView, int position) { Loading Loading
core/java/android/widget/AbsListView.java +36 −10 Original line number Original line Diff line number Diff line Loading @@ -2322,7 +2322,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * * @param position The position to display * @param position The position to display * @param isScrap Array of at least 1 boolean, the first entry will become true if * @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 * @return A view displaying the data associated with the specified position */ */ Loading Loading @@ -2362,10 +2363,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Failed to re-bind the data, return scrap to the heap. // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); mRecycler.addScrapView(scrapView, position); } else { } else { if (child.isTemporarilyDetached()) { isScrap[0] = true; isScrap[0] = true; // Finish the temporary detach started in addScrapView(). // Finish the temporary detach started in addScrapView(). child.dispatchFinishTemporaryDetach(); 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; } } } } } Loading Loading @@ -5152,6 +5161,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te fillGap(down); fillGap(down); } } mRecycler.fullyDetachScrapViews(); if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { if (childIndex >= 0 && childIndex < getChildCount()) { Loading Loading @@ -6861,8 +6871,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te scrapViews = mScrapViews[whichScrap]; scrapViews = mScrapViews[whichScrap]; } } victim.dispatchStartTemporaryDetach(); lp.scrappedFromPosition = mFirstActivePosition + i; lp.scrappedFromPosition = mFirstActivePosition + i; removeDetachedView(victim, false); scrapViews.add(victim); scrapViews.add(victim); if (hasListener) { if (hasListener) { Loading @@ -6871,10 +6881,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } } } } pruneScrapViews(); 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<View>[] scrapViews = mScrapViews; for (int i = 0; i < viewTypeCount; ++i) { final ArrayList<View> 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 * Makes sure that the size of mScrapViews does not exceed the size of * mActiveViews, which can happen if an adapter does not recycle its * mActiveViews, which can happen if an adapter does not recycle its Loading @@ -6888,10 +6916,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te for (int i = 0; i < viewTypeCount; ++i) { for (int i = 0; i < viewTypeCount; ++i) { final ArrayList<View> scrapPile = scrapViews[i]; final ArrayList<View> scrapPile = scrapViews[i]; int size = scrapPile.size(); int size = scrapPile.size(); final int extras = size - maxViews; while (size > maxViews) { size--; scrapPile.remove(--size); for (int j = 0; j < extras; j++) { removeDetachedView(scrapPile.remove(size--), false); } } } } Loading
core/java/android/widget/ListView.java +38 −0 Original line number Original line Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.widget.RemoteViews.RemoteView; import android.widget.RemoteViews.RemoteView; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; /* /* * Implementation Notes: * Implementation Notes: Loading Loading @@ -1763,6 +1764,10 @@ public class ListView extends AbsListView { // Flush any cached views that did not get reused above // Flush any cached views that did not get reused above recycleBin.scrapActiveViews(); recycleBin.scrapActiveViews(); // remove any header/footer that has been temp detached and not re-attached removeUnusedFixedViews(mHeaderViewInfos); removeUnusedFixedViews(mFooterViewInfos); if (sel != null) { if (sel != null) { // The current selected item should get focus if items are // The current selected item should get focus if items are // focusable. // focusable. Loading Loading @@ -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<FixedViewInfo> 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. * @param child a direct child of this list. * @return Whether child is a header or footer view. * @return Whether child is a header or footer view. Loading Loading @@ -3179,6 +3214,9 @@ public class ListView extends AbsListView { last = getChildAt(--lastIndex); last = getChildAt(--lastIndex); } } } } recycleBin.fullyDetachScrapViews(); removeUnusedFixedViews(mHeaderViewInfos); removeUnusedFixedViews(mFooterViewInfos); } } private View addViewAbove(View theView, int position) { private View addViewAbove(View theView, int position) { Loading