diff options
| author | Alan Viverette <alanv@google.com> | 2013-07-18 22:06:21 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-07-18 22:06:22 +0000 |
| commit | f1f8f318bbf1ac202bf3cb65d57b9537d16464be (patch) | |
| tree | 58d1558d5124668fc96d578ecd26655c2e9ccb2c /core/java/android | |
| parent | f889c84f425f2181f50505d011d6821e76299c45 (diff) | |
| parent | d44696c4cfdda1e7e4e10a21b68f54ce5a4a459d (diff) | |
Merge "Set transient state for focus container in ListView"
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 75 | ||||
| -rw-r--r-- | core/java/android/widget/ListView.java | 96 |
2 files changed, 75 insertions, 96 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 52433a57a9c8..19e3905bff19 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -6500,58 +6500,67 @@ 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; + // Don't scrap header or footer views, or views that should + // otherwise not be recycled. + final int viewType = lp.viewType; + if (!shouldRecycleViewType(viewType)) { + return; + } + + scrap.dispatchStartTemporaryDetach(); + + // Don't scrap views that have transient state. final boolean scrapHasTransientState = scrap.hasTransientState(); - if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { - if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) { + 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) { + // 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); } - if (scrapHasTransientState) { - scrap.dispatchStartTemporaryDetach(); - if (mAdapter != null && mAdapterHasStableIds) { - 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 (mTransientStateViews == null) { - mTransientStateViews = new SparseArray<View>(); - } - mTransientStateViews.put(position, scrap); - } + } else { + if (mViewTypeCount == 1) { + mCurrentScrap.add(scrap); + } else { + mScrapViews[viewType].add(scrap); } - return; - } - scrap.dispatchStartTemporaryDetach(); - if (mViewTypeCount == 1) { - mCurrentScrap.add(scrap); - } else { - mScrapViews[viewType].add(scrap); - } + scrap.setAccessibilityDelegate(null); - scrap.setAccessibilityDelegate(null); - if (mRecyclerListener != null) { - mRecyclerListener.onMovedToScrapHeap(scrap); + if (mRecyclerListener != null) { + mRecyclerListener.onMovedToScrapHeap(scrap); + } } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index c44ac3220f59..2f42ae33de65 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -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(); @@ -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; @@ -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; @@ -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(); @@ -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); @@ -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(); @@ -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. @@ -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; |
