Loading core/java/android/widget/AbsListView.java +42 −33 Original line number Diff line number Diff line Loading @@ -6500,49 +6500,56 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** * Put a view into the ScrapViews list. These views are unordered. * Puts a view into the list of scrap views. * <p> * If the list data hasn't changed or the adapter has stable IDs, views * with transient state will be preserved for later retrieval. * * @param scrap The view to add * @param position The view's position within its parent */ void addScrapView(View scrap, int position) { AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); if (lp == null) { return; } lp.scrappedFromPosition = position; // Don't put header or footer views or views that should be ignored // into the scrap heap int viewType = lp.viewType; final boolean scrapHasTransientState = scrap.hasTransientState(); if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) { if (mSkippedScrap == null) { mSkippedScrap = new ArrayList<View>(); } mSkippedScrap.add(scrap); // Don't scrap header or footer views, or views that should // otherwise not be recycled. final int viewType = lp.viewType; if (!shouldRecycleViewType(viewType)) { return; } if (scrapHasTransientState) { scrap.dispatchStartTemporaryDetach(); // Don't scrap views that have transient state. final boolean scrapHasTransientState = scrap.hasTransientState(); if (scrapHasTransientState) { if (mAdapter != null && mAdapterHasStableIds) { // If the adapter has stable IDs, we can reuse the view for // the same data. if (mTransientStateViewsById == null) { mTransientStateViewsById = new LongSparseArray<View>(); } mTransientStateViewsById.put(lp.itemId, scrap); } else if (!mDataChanged) { // avoid putting views on transient state list during a data change; // the layout positions may be out of sync with the adapter positions // If the data hasn't changed, we can reuse the views at // their old positions. if (mTransientStateViews == null) { mTransientStateViews = new SparseArray<View>(); } mTransientStateViews.put(position, scrap); } else { // Otherwise, we'll have to remove the view and start over. if (mSkippedScrap == null) { mSkippedScrap = new ArrayList<View>(); } mSkippedScrap.add(scrap); } return; } scrap.dispatchStartTemporaryDetach(); } else { if (mViewTypeCount == 1) { mCurrentScrap.add(scrap); } else { Loading @@ -6550,10 +6557,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } scrap.setAccessibilityDelegate(null); if (mRecyclerListener != null) { mRecyclerListener.onMovedToScrapHeap(scrap); } } } /** * Finish the removal of any views that skipped the scrap heap. Loading core/java/android/widget/ListView.java +33 −63 Original line number Diff line number Diff line Loading @@ -1478,12 +1478,12 @@ public class ListView extends AbsListView { @Override protected void layoutChildren() { final boolean blockLayoutRequests = mBlockLayoutRequests; if (!blockLayoutRequests) { mBlockLayoutRequests = true; } else { if (blockLayoutRequests) { return; } mBlockLayoutRequests = true; try { super.layoutChildren(); Loading @@ -1495,10 +1495,10 @@ public class ListView extends AbsListView { return; } int childrenTop = mListPadding.top; int childrenBottom = mBottom - mTop - mListPadding.bottom; final int childrenTop = mListPadding.top; final int childrenBottom = mBottom - mTop - mListPadding.bottom; final int childCount = getChildCount(); int childCount = getChildCount(); int index = 0; int delta = 0; Loading @@ -1507,8 +1507,6 @@ public class ListView extends AbsListView { View oldFirst = null; View newSel = null; View focusLayoutRestoreView = null; AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; View accessibilityFocusLayoutRestoreView = null; int accessibilityFocusPosition = INVALID_POSITION; Loading Loading @@ -1570,6 +1568,7 @@ public class ListView extends AbsListView { // Remember which child, if any, had accessibility focus. This must // occur before recycling any views, since that will clear // accessibility focus. // TODO: This should rely on transient state. final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost(); Loading @@ -1593,16 +1592,18 @@ public class ListView extends AbsListView { } } // Ensure the child containing focus, if any, has transient state. // If the list data hasn't changed, or if the adapter has stable // IDs, this will maintain focus. final View focusedChild = getFocusedChild(); if (focusedChild != null) { focusedChild.setHasTransientState(true); } // Pull all children into the RecycleBin. // These views will be reused if possible final int firstPosition = mFirstPosition; final RecycleBin recycleBin = mRecycler; // reset the focus restoration View focusLayoutRestoreDirectChild = null; // Don't put header or footer views into the Recycler. Those are // already cached in mHeaderViews; if (dataChanged) { for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); Loading @@ -1611,28 +1612,6 @@ public class ListView extends AbsListView { recycleBin.fillActiveViews(childCount, firstPosition); } // take focus back to us temporarily to avoid the eventual // call to clear focus when removing the focused child below // from messing things up when ViewAncestor assigns focus back // to someone else final View focusedChild = getFocusedChild(); if (focusedChild != null) { // TODO: in some cases focusedChild.getParent() == null // we can remember the focused view to restore after relayout if the // data hasn't changed, or if the focused position is a header or footer if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) { focusLayoutRestoreDirectChild = focusedChild; // remember the specific view that had focus focusLayoutRestoreView = findFocus(); if (focusLayoutRestoreView != null) { // tell it we are going to mess with it focusLayoutRestoreView.onStartTemporaryDetach(); } } requestFocus(); } // Clear out old views detachAllViewsFromParent(); recycleBin.removeSkippedScrap(); Loading Loading @@ -1692,43 +1671,37 @@ public class ListView extends AbsListView { recycleBin.scrapActiveViews(); if (sel != null) { // the current selected item should get focus if items // are focusable if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) { final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild && focusLayoutRestoreView != null && focusLayoutRestoreView.requestFocus()) || sel.requestFocus(); if (!focusWasTaken) { // selected item didn't take focus, fine, but still want // to make sure something else outside of the selected view // has focus final boolean shouldPlaceFocus = mItemsCanFocus && hasFocus(); final boolean maintainedFocus = focusedChild != null && focusedChild.hasFocus(); if (shouldPlaceFocus && !maintainedFocus && !sel.hasFocus()) { if (sel.requestFocus()) { // Successfully placed focus, clear selection. sel.setSelected(false); mSelectorRect.setEmpty(); } else { // Failed to place focus, clear current (invalid) focus. final View focused = getFocusedChild(); if (focused != null) { focused.clearFocus(); } positionSelector(INVALID_POSITION, sel); } else { sel.setSelected(false); mSelectorRect.setEmpty(); } } else { positionSelector(INVALID_POSITION, sel); } mSelectedTop = sel.getTop(); } else { if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) { View child = getChildAt(mMotionPosition - mFirstPosition); if (child != null) positionSelector(mMotionPosition, child); // If the user's finger is down, select the motion position. // Otherwise, clear selection. if (mTouchMode == TOUCH_MODE_TAP || mTouchMode == TOUCH_MODE_DONE_WAITING) { final View child = getChildAt(mMotionPosition - mFirstPosition); if (child != null) { positionSelector(mMotionPosition, child); } } else { mSelectedTop = 0; mSelectorRect.setEmpty(); } // even if there is not selected position, we may need to restore // focus (i.e. something focusable in touch mode) if (hasFocus() && focusLayoutRestoreView != null) { focusLayoutRestoreView.requestFocus(); } } // Attempt to restore accessibility focus. Loading @@ -1753,11 +1726,8 @@ public class ListView extends AbsListView { } } // tell focus view we are done mucking with it, if it is still in // our view hierarchy. if (focusLayoutRestoreView != null && focusLayoutRestoreView.getWindowToken() != null) { focusLayoutRestoreView.onFinishTemporaryDetach(); if (focusedChild != null) { focusedChild.setHasTransientState(false); } mLayoutMode = LAYOUT_NORMAL; Loading Loading
core/java/android/widget/AbsListView.java +42 −33 Original line number Diff line number Diff line Loading @@ -6500,49 +6500,56 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** * Put a view into the ScrapViews list. These views are unordered. * Puts a view into the list of scrap views. * <p> * If the list data hasn't changed or the adapter has stable IDs, views * with transient state will be preserved for later retrieval. * * @param scrap The view to add * @param position The view's position within its parent */ void addScrapView(View scrap, int position) { AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); if (lp == null) { return; } lp.scrappedFromPosition = position; // Don't put header or footer views or views that should be ignored // into the scrap heap int viewType = lp.viewType; final boolean scrapHasTransientState = scrap.hasTransientState(); if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) { if (mSkippedScrap == null) { mSkippedScrap = new ArrayList<View>(); } mSkippedScrap.add(scrap); // Don't scrap header or footer views, or views that should // otherwise not be recycled. final int viewType = lp.viewType; if (!shouldRecycleViewType(viewType)) { return; } if (scrapHasTransientState) { scrap.dispatchStartTemporaryDetach(); // Don't scrap views that have transient state. final boolean scrapHasTransientState = scrap.hasTransientState(); if (scrapHasTransientState) { if (mAdapter != null && mAdapterHasStableIds) { // If the adapter has stable IDs, we can reuse the view for // the same data. if (mTransientStateViewsById == null) { mTransientStateViewsById = new LongSparseArray<View>(); } mTransientStateViewsById.put(lp.itemId, scrap); } else if (!mDataChanged) { // avoid putting views on transient state list during a data change; // the layout positions may be out of sync with the adapter positions // If the data hasn't changed, we can reuse the views at // their old positions. if (mTransientStateViews == null) { mTransientStateViews = new SparseArray<View>(); } mTransientStateViews.put(position, scrap); } else { // Otherwise, we'll have to remove the view and start over. if (mSkippedScrap == null) { mSkippedScrap = new ArrayList<View>(); } mSkippedScrap.add(scrap); } return; } scrap.dispatchStartTemporaryDetach(); } else { if (mViewTypeCount == 1) { mCurrentScrap.add(scrap); } else { Loading @@ -6550,10 +6557,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } scrap.setAccessibilityDelegate(null); if (mRecyclerListener != null) { mRecyclerListener.onMovedToScrapHeap(scrap); } } } /** * Finish the removal of any views that skipped the scrap heap. Loading
core/java/android/widget/ListView.java +33 −63 Original line number Diff line number Diff line Loading @@ -1478,12 +1478,12 @@ public class ListView extends AbsListView { @Override protected void layoutChildren() { final boolean blockLayoutRequests = mBlockLayoutRequests; if (!blockLayoutRequests) { mBlockLayoutRequests = true; } else { if (blockLayoutRequests) { return; } mBlockLayoutRequests = true; try { super.layoutChildren(); Loading @@ -1495,10 +1495,10 @@ public class ListView extends AbsListView { return; } int childrenTop = mListPadding.top; int childrenBottom = mBottom - mTop - mListPadding.bottom; final int childrenTop = mListPadding.top; final int childrenBottom = mBottom - mTop - mListPadding.bottom; final int childCount = getChildCount(); int childCount = getChildCount(); int index = 0; int delta = 0; Loading @@ -1507,8 +1507,6 @@ public class ListView extends AbsListView { View oldFirst = null; View newSel = null; View focusLayoutRestoreView = null; AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; View accessibilityFocusLayoutRestoreView = null; int accessibilityFocusPosition = INVALID_POSITION; Loading Loading @@ -1570,6 +1568,7 @@ public class ListView extends AbsListView { // Remember which child, if any, had accessibility focus. This must // occur before recycling any views, since that will clear // accessibility focus. // TODO: This should rely on transient state. final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost(); Loading @@ -1593,16 +1592,18 @@ public class ListView extends AbsListView { } } // Ensure the child containing focus, if any, has transient state. // If the list data hasn't changed, or if the adapter has stable // IDs, this will maintain focus. final View focusedChild = getFocusedChild(); if (focusedChild != null) { focusedChild.setHasTransientState(true); } // Pull all children into the RecycleBin. // These views will be reused if possible final int firstPosition = mFirstPosition; final RecycleBin recycleBin = mRecycler; // reset the focus restoration View focusLayoutRestoreDirectChild = null; // Don't put header or footer views into the Recycler. Those are // already cached in mHeaderViews; if (dataChanged) { for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); Loading @@ -1611,28 +1612,6 @@ public class ListView extends AbsListView { recycleBin.fillActiveViews(childCount, firstPosition); } // take focus back to us temporarily to avoid the eventual // call to clear focus when removing the focused child below // from messing things up when ViewAncestor assigns focus back // to someone else final View focusedChild = getFocusedChild(); if (focusedChild != null) { // TODO: in some cases focusedChild.getParent() == null // we can remember the focused view to restore after relayout if the // data hasn't changed, or if the focused position is a header or footer if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) { focusLayoutRestoreDirectChild = focusedChild; // remember the specific view that had focus focusLayoutRestoreView = findFocus(); if (focusLayoutRestoreView != null) { // tell it we are going to mess with it focusLayoutRestoreView.onStartTemporaryDetach(); } } requestFocus(); } // Clear out old views detachAllViewsFromParent(); recycleBin.removeSkippedScrap(); Loading Loading @@ -1692,43 +1671,37 @@ public class ListView extends AbsListView { recycleBin.scrapActiveViews(); if (sel != null) { // the current selected item should get focus if items // are focusable if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) { final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild && focusLayoutRestoreView != null && focusLayoutRestoreView.requestFocus()) || sel.requestFocus(); if (!focusWasTaken) { // selected item didn't take focus, fine, but still want // to make sure something else outside of the selected view // has focus final boolean shouldPlaceFocus = mItemsCanFocus && hasFocus(); final boolean maintainedFocus = focusedChild != null && focusedChild.hasFocus(); if (shouldPlaceFocus && !maintainedFocus && !sel.hasFocus()) { if (sel.requestFocus()) { // Successfully placed focus, clear selection. sel.setSelected(false); mSelectorRect.setEmpty(); } else { // Failed to place focus, clear current (invalid) focus. final View focused = getFocusedChild(); if (focused != null) { focused.clearFocus(); } positionSelector(INVALID_POSITION, sel); } else { sel.setSelected(false); mSelectorRect.setEmpty(); } } else { positionSelector(INVALID_POSITION, sel); } mSelectedTop = sel.getTop(); } else { if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) { View child = getChildAt(mMotionPosition - mFirstPosition); if (child != null) positionSelector(mMotionPosition, child); // If the user's finger is down, select the motion position. // Otherwise, clear selection. if (mTouchMode == TOUCH_MODE_TAP || mTouchMode == TOUCH_MODE_DONE_WAITING) { final View child = getChildAt(mMotionPosition - mFirstPosition); if (child != null) { positionSelector(mMotionPosition, child); } } else { mSelectedTop = 0; mSelectorRect.setEmpty(); } // even if there is not selected position, we may need to restore // focus (i.e. something focusable in touch mode) if (hasFocus() && focusLayoutRestoreView != null) { focusLayoutRestoreView.requestFocus(); } } // Attempt to restore accessibility focus. Loading @@ -1753,11 +1726,8 @@ public class ListView extends AbsListView { } } // tell focus view we are done mucking with it, if it is still in // our view hierarchy. if (focusLayoutRestoreView != null && focusLayoutRestoreView.getWindowToken() != null) { focusLayoutRestoreView.onFinishTemporaryDetach(); if (focusedChild != null) { focusedChild.setHasTransientState(false); } mLayoutMode = LAYOUT_NORMAL; Loading