summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2013-07-18 22:06:21 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-07-18 22:06:22 +0000
commitf1f8f318bbf1ac202bf3cb65d57b9537d16464be (patch)
tree58d1558d5124668fc96d578ecd26655c2e9ccb2c /core/java/android
parentf889c84f425f2181f50505d011d6821e76299c45 (diff)
parentd44696c4cfdda1e7e4e10a21b68f54ce5a4a459d (diff)
Merge "Set transient state for focus container in ListView"
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/widget/AbsListView.java75
-rw-r--r--core/java/android/widget/ListView.java96
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;