Loading core/java/android/widget/AbsListView.java +49 −49 Original line number Diff line number Diff line Loading @@ -2169,20 +2169,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** * @return the direct child that contains accessibility focus, or null if no * @param focusedView view that holds accessibility focus * @return direct child that contains accessibility focus, or null if no * child contains accessibility focus */ View getAccessibilityFocusedChild() { final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl == null) { return null; } View focusedView = viewRootImpl.getAccessibilityFocusedHost(); if (focusedView == null) { return null; } View getAccessibilityFocusedChild(View focusedView) { ViewParent viewParent = focusedView.getParent(); while ((viewParent instanceof View) && (viewParent != this)) { focusedView = (View) viewParent; Loading Loading @@ -2335,12 +2326,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } else { isScrap[0] = true; // Clear any system-managed transient state so that we can // recycle this view and bind it to different data. if (child.isAccessibilityFocused()) { child.clearAccessibilityFocus(); } child.dispatchFinishTemporaryDetach(); } } Loading Loading @@ -6196,18 +6181,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te void clear() { if (mViewTypeCount == 1) { final ArrayList<View> scrap = mCurrentScrap; final int scrapCount = scrap.size(); for (int i = 0; i < scrapCount; i++) { removeDetachedView(scrap.remove(scrapCount - 1 - i), false); } clearScrap(scrap); } else { final int typeCount = mViewTypeCount; for (int i = 0; i < typeCount; i++) { final ArrayList<View> scrap = mScrapViews[i]; final int scrapCount = scrap.size(); for (int j = 0; j < scrapCount; j++) { removeDetachedView(scrap.remove(scrapCount - 1 - j), false); } clearScrap(scrap); } } Loading Loading @@ -6308,7 +6287,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mViewTypeCount == 1) { return retrieveFromScrap(mCurrentScrap, position); } else { int whichScrap = mAdapter.getItemViewType(position); final int whichScrap = mAdapter.getItemViewType(position); if (whichScrap >= 0 && whichScrap < mScrapViews.length) { return retrieveFromScrap(mScrapViews[whichScrap], position); } Loading Loading @@ -6380,13 +6359,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mScrapViews[viewType].add(scrap); } // Clear any system-managed transient state. if (scrap.isAccessibilityFocused()) { scrap.clearAccessibilityFocus(); } scrap.setAccessibilityDelegate(null); if (mRecyclerListener != null) { mRecyclerListener.onMovedToScrapHeap(scrap); } Loading Loading @@ -6460,7 +6432,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te lp.scrappedFromPosition = mFirstActivePosition + i; scrapViews.add(victim); victim.setAccessibilityDelegate(null); if (hasListener) { mRecyclerListener.onMovedToScrapHeap(victim); } Loading Loading @@ -6564,26 +6535,55 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } } static View retrieveFromScrap(ArrayList<View> scrapViews, int position) { int size = scrapViews.size(); private View retrieveFromScrap(ArrayList<View> scrapViews, int position) { final int size = scrapViews.size(); if (size > 0) { // See if we still have a view for this position. // See if we still have a view for this position or ID. for (int i = 0; i < size; i++) { View view = scrapViews.get(i); if (((AbsListView.LayoutParams)view.getLayoutParams()) .scrappedFromPosition == position) { scrapViews.remove(i); return view; final View view = scrapViews.get(i); final AbsListView.LayoutParams params = (AbsListView.LayoutParams) view.getLayoutParams(); if (mAdapterHasStableIds) { final long id = mAdapter.getItemId(position); if (id == params.itemId) { return scrapViews.remove(i); } } else if (params.scrappedFromPosition == position) { final View scrap = scrapViews.remove(i); clearAccessibilityFromScrap(scrap); return scrap; } return scrapViews.remove(size - 1); } final View scrap = scrapViews.remove(size - 1); clearAccessibilityFromScrap(scrap); return scrap; } else { return null; } } private void clearScrap(final ArrayList<View> scrap) { final int scrapCount = scrap.size(); for (int j = 0; j < scrapCount; j++) { removeDetachedView(scrap.remove(scrapCount - 1 - j), false); } } private void clearAccessibilityFromScrap(View view) { if (view.isAccessibilityFocused()) { view.clearAccessibilityFocus(); } view.setAccessibilityDelegate(null); } private void removeDetachedView(View child, boolean animate) { child.setAccessibilityDelegate(null); AbsListView.this.removeDetachedView(child, animate); } } /** * Returns the height of the view for the specified position. * Loading core/java/android/widget/GridView.java +43 −28 Original line number Diff line number Diff line Loading @@ -30,13 +30,13 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo; import android.view.animation.GridLayoutAnimationController; import android.widget.AbsListView.AbsPositionScroller; import android.widget.ListView.ListViewPositionScroller; import android.widget.RemoteViews.RemoteView; import java.lang.annotation.Retention; Loading Loading @@ -1222,22 +1222,32 @@ public class GridView extends AbsListView { setSelectedPositionInt(mNextSelectedPosition); // Remember which child, if any, had accessibility focus. final int accessibilityFocusPosition; final View accessFocusedChild = getAccessibilityFocusedChild(); if (accessFocusedChild != null) { accessibilityFocusPosition = getPositionForView(accessFocusedChild); accessFocusedChild.setHasTransientState(true); } else { accessibilityFocusPosition = INVALID_POSITION; AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; View accessibilityFocusLayoutRestoreView = null; int accessibilityFocusPosition = INVALID_POSITION; // Remember which child, if any, had accessibility focus. This must // occur before recycling any views, since that will clear // accessibility focus. final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { final View focusHost = viewRootImpl.getAccessibilityFocusedHost(); if (focusHost != null) { final View focusChild = getAccessibilityFocusedChild(focusHost); if (focusChild != null) { if (!dataChanged || focusChild.hasTransientState() || mAdapterHasStableIds) { // The views won't be changing, so try to maintain // focus on the current host and virtual view. accessibilityFocusLayoutRestoreView = focusHost; accessibilityFocusLayoutRestoreNode = viewRootImpl .getAccessibilityFocusedVirtualView(); } // 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); // Try to maintain focus at the same position. accessibilityFocusPosition = getPositionForView(focusChild); } } } // Pull all children into the RecycleBin. Loading Loading @@ -1324,13 +1334,22 @@ public class GridView extends AbsListView { mSelectorRect.setEmpty(); } if (accessFocusedChild != null) { accessFocusedChild.setHasTransientState(false); // If we failed to maintain accessibility focus on the previous // view, attempt to restore it to the previous position. if (!accessFocusedChild.isAccessibilityFocused() && accessibilityFocusPosition != INVALID_POSITION) { // Attempt to restore accessibility focus, if necessary. final View newAccessibilityFocusedView = viewRootImpl.getAccessibilityFocusedHost(); if (newAccessibilityFocusedView == null) { if (accessibilityFocusLayoutRestoreView != null && accessibilityFocusLayoutRestoreView.isAttachedToWindow()) { final AccessibilityNodeProvider provider = accessibilityFocusLayoutRestoreView.getAccessibilityNodeProvider(); if (accessibilityFocusLayoutRestoreNode != null && provider != null) { final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId( accessibilityFocusLayoutRestoreNode.getSourceNodeId()); provider.performAction(virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } else { accessibilityFocusLayoutRestoreView.requestAccessibilityFocus(); } } else if (accessibilityFocusPosition != INVALID_POSITION) { // Bound the position within the visible children. final int position = MathUtils.constrain( accessibilityFocusPosition - mFirstPosition, 0, getChildCount() - 1); Loading @@ -1341,10 +1360,6 @@ public class GridView extends AbsListView { } } if (focusedChild != null) { focusedChild.setHasTransientState(false); } mLayoutMode = LAYOUT_NORMAL; mDataChanged = false; if (mPositionScrollAfterLayout != null) { Loading core/java/android/widget/ListView.java +114 −30 Original line number Diff line number Diff line Loading @@ -39,10 +39,12 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.widget.RemoteViews.RemoteView; import java.util.ArrayList; Loading Loading @@ -1567,22 +1569,58 @@ public class ListView extends AbsListView { setSelectedPositionInt(mNextSelectedPosition); // Remember which child, if any, had accessibility focus. final int accessibilityFocusPosition; final View accessFocusedChild = getAccessibilityFocusedChild(); if (accessFocusedChild != null) { accessibilityFocusPosition = getPositionForView(accessFocusedChild); accessFocusedChild.setHasTransientState(true); } else { accessibilityFocusPosition = INVALID_POSITION; AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; View accessibilityFocusLayoutRestoreView = null; int accessibilityFocusPosition = INVALID_POSITION; // Remember which child, if any, had accessibility focus. This must // occur before recycling any views, since that will clear // accessibility focus. final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { final View focusHost = viewRootImpl.getAccessibilityFocusedHost(); if (focusHost != null) { final View focusChild = getAccessibilityFocusedChild(focusHost); if (focusChild != null) { if (!dataChanged || isDirectChildHeaderOrFooter(focusChild) || focusChild.hasTransientState() || mAdapterHasStableIds) { // The views won't be changing, so try to maintain // focus on the current host and virtual view. accessibilityFocusLayoutRestoreView = focusHost; accessibilityFocusLayoutRestoreNode = viewRootImpl .getAccessibilityFocusedVirtualView(); } // 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. // If all else fails, maintain focus at the same // position. accessibilityFocusPosition = getPositionForView(focusChild); } } } View focusLayoutRestoreDirectChild = null; View focusLayoutRestoreView = null; // 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) { focusedChild.setHasTransientState(true); // TODO: in some cases focusedChild.getParent() == null // We can remember the focused view to restore after re-layout // 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(); } // Pull all children into the RecycleBin. Loading Loading @@ -1656,20 +1694,24 @@ public class ListView extends AbsListView { recycleBin.scrapActiveViews(); if (sel != null) { 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. // 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, but we still want to // make sure something else outside of the selected view // has 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); Loading @@ -1687,15 +1729,30 @@ public class ListView extends AbsListView { mSelectedTop = 0; mSelectorRect.setEmpty(); } } if (accessFocusedChild != null) { accessFocusedChild.setHasTransientState(false); // 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(); } } // If we failed to maintain accessibility focus on the previous // view, attempt to restore it to the previous position. if (!accessFocusedChild.isAccessibilityFocused() && accessibilityFocusPosition != INVALID_POSITION) { // Attempt to restore accessibility focus, if necessary. final View newAccessibilityFocusedView = viewRootImpl.getAccessibilityFocusedHost(); if (newAccessibilityFocusedView == null) { if (accessibilityFocusLayoutRestoreView != null && accessibilityFocusLayoutRestoreView.isAttachedToWindow()) { final AccessibilityNodeProvider provider = accessibilityFocusLayoutRestoreView.getAccessibilityNodeProvider(); if (accessibilityFocusLayoutRestoreNode != null && provider != null) { final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId( accessibilityFocusLayoutRestoreNode.getSourceNodeId()); provider.performAction(virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } else { accessibilityFocusLayoutRestoreView.requestAccessibilityFocus(); } } else if (accessibilityFocusPosition != INVALID_POSITION) { // Bound the position within the visible children. final int position = MathUtils.constrain( accessibilityFocusPosition - mFirstPosition, 0, getChildCount() - 1); Loading @@ -1706,8 +1763,11 @@ public class ListView extends AbsListView { } } if (focusedChild != null) { focusedChild.setHasTransientState(false); // 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(); } mLayoutMode = LAYOUT_NORMAL; Loading @@ -1733,6 +1793,30 @@ public class ListView extends AbsListView { } } /** * @param child a direct child of this list. * @return Whether child is a header or footer view. */ private boolean isDirectChildHeaderOrFooter(View child) { final ArrayList<FixedViewInfo> headers = mHeaderViewInfos; final int numHeaders = headers.size(); for (int i = 0; i < numHeaders; i++) { if (child == headers.get(i).view) { return true; } } final ArrayList<FixedViewInfo> footers = mFooterViewInfos; final int numFooters = footers.size(); for (int i = 0; i < numFooters; i++) { if (child == footers.get(i).view) { return true; } } return false; } /** * Obtain the view and add it to our list of children. The view can be made * fresh, converted from an unused view, or used as is if it was in the Loading Loading
core/java/android/widget/AbsListView.java +49 −49 Original line number Diff line number Diff line Loading @@ -2169,20 +2169,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** * @return the direct child that contains accessibility focus, or null if no * @param focusedView view that holds accessibility focus * @return direct child that contains accessibility focus, or null if no * child contains accessibility focus */ View getAccessibilityFocusedChild() { final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl == null) { return null; } View focusedView = viewRootImpl.getAccessibilityFocusedHost(); if (focusedView == null) { return null; } View getAccessibilityFocusedChild(View focusedView) { ViewParent viewParent = focusedView.getParent(); while ((viewParent instanceof View) && (viewParent != this)) { focusedView = (View) viewParent; Loading Loading @@ -2335,12 +2326,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } else { isScrap[0] = true; // Clear any system-managed transient state so that we can // recycle this view and bind it to different data. if (child.isAccessibilityFocused()) { child.clearAccessibilityFocus(); } child.dispatchFinishTemporaryDetach(); } } Loading Loading @@ -6196,18 +6181,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te void clear() { if (mViewTypeCount == 1) { final ArrayList<View> scrap = mCurrentScrap; final int scrapCount = scrap.size(); for (int i = 0; i < scrapCount; i++) { removeDetachedView(scrap.remove(scrapCount - 1 - i), false); } clearScrap(scrap); } else { final int typeCount = mViewTypeCount; for (int i = 0; i < typeCount; i++) { final ArrayList<View> scrap = mScrapViews[i]; final int scrapCount = scrap.size(); for (int j = 0; j < scrapCount; j++) { removeDetachedView(scrap.remove(scrapCount - 1 - j), false); } clearScrap(scrap); } } Loading Loading @@ -6308,7 +6287,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mViewTypeCount == 1) { return retrieveFromScrap(mCurrentScrap, position); } else { int whichScrap = mAdapter.getItemViewType(position); final int whichScrap = mAdapter.getItemViewType(position); if (whichScrap >= 0 && whichScrap < mScrapViews.length) { return retrieveFromScrap(mScrapViews[whichScrap], position); } Loading Loading @@ -6380,13 +6359,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mScrapViews[viewType].add(scrap); } // Clear any system-managed transient state. if (scrap.isAccessibilityFocused()) { scrap.clearAccessibilityFocus(); } scrap.setAccessibilityDelegate(null); if (mRecyclerListener != null) { mRecyclerListener.onMovedToScrapHeap(scrap); } Loading Loading @@ -6460,7 +6432,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te lp.scrappedFromPosition = mFirstActivePosition + i; scrapViews.add(victim); victim.setAccessibilityDelegate(null); if (hasListener) { mRecyclerListener.onMovedToScrapHeap(victim); } Loading Loading @@ -6564,26 +6535,55 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } } static View retrieveFromScrap(ArrayList<View> scrapViews, int position) { int size = scrapViews.size(); private View retrieveFromScrap(ArrayList<View> scrapViews, int position) { final int size = scrapViews.size(); if (size > 0) { // See if we still have a view for this position. // See if we still have a view for this position or ID. for (int i = 0; i < size; i++) { View view = scrapViews.get(i); if (((AbsListView.LayoutParams)view.getLayoutParams()) .scrappedFromPosition == position) { scrapViews.remove(i); return view; final View view = scrapViews.get(i); final AbsListView.LayoutParams params = (AbsListView.LayoutParams) view.getLayoutParams(); if (mAdapterHasStableIds) { final long id = mAdapter.getItemId(position); if (id == params.itemId) { return scrapViews.remove(i); } } else if (params.scrappedFromPosition == position) { final View scrap = scrapViews.remove(i); clearAccessibilityFromScrap(scrap); return scrap; } return scrapViews.remove(size - 1); } final View scrap = scrapViews.remove(size - 1); clearAccessibilityFromScrap(scrap); return scrap; } else { return null; } } private void clearScrap(final ArrayList<View> scrap) { final int scrapCount = scrap.size(); for (int j = 0; j < scrapCount; j++) { removeDetachedView(scrap.remove(scrapCount - 1 - j), false); } } private void clearAccessibilityFromScrap(View view) { if (view.isAccessibilityFocused()) { view.clearAccessibilityFocus(); } view.setAccessibilityDelegate(null); } private void removeDetachedView(View child, boolean animate) { child.setAccessibilityDelegate(null); AbsListView.this.removeDetachedView(child, animate); } } /** * Returns the height of the view for the specified position. * Loading
core/java/android/widget/GridView.java +43 −28 Original line number Diff line number Diff line Loading @@ -30,13 +30,13 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo; import android.view.animation.GridLayoutAnimationController; import android.widget.AbsListView.AbsPositionScroller; import android.widget.ListView.ListViewPositionScroller; import android.widget.RemoteViews.RemoteView; import java.lang.annotation.Retention; Loading Loading @@ -1222,22 +1222,32 @@ public class GridView extends AbsListView { setSelectedPositionInt(mNextSelectedPosition); // Remember which child, if any, had accessibility focus. final int accessibilityFocusPosition; final View accessFocusedChild = getAccessibilityFocusedChild(); if (accessFocusedChild != null) { accessibilityFocusPosition = getPositionForView(accessFocusedChild); accessFocusedChild.setHasTransientState(true); } else { accessibilityFocusPosition = INVALID_POSITION; AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; View accessibilityFocusLayoutRestoreView = null; int accessibilityFocusPosition = INVALID_POSITION; // Remember which child, if any, had accessibility focus. This must // occur before recycling any views, since that will clear // accessibility focus. final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { final View focusHost = viewRootImpl.getAccessibilityFocusedHost(); if (focusHost != null) { final View focusChild = getAccessibilityFocusedChild(focusHost); if (focusChild != null) { if (!dataChanged || focusChild.hasTransientState() || mAdapterHasStableIds) { // The views won't be changing, so try to maintain // focus on the current host and virtual view. accessibilityFocusLayoutRestoreView = focusHost; accessibilityFocusLayoutRestoreNode = viewRootImpl .getAccessibilityFocusedVirtualView(); } // 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); // Try to maintain focus at the same position. accessibilityFocusPosition = getPositionForView(focusChild); } } } // Pull all children into the RecycleBin. Loading Loading @@ -1324,13 +1334,22 @@ public class GridView extends AbsListView { mSelectorRect.setEmpty(); } if (accessFocusedChild != null) { accessFocusedChild.setHasTransientState(false); // If we failed to maintain accessibility focus on the previous // view, attempt to restore it to the previous position. if (!accessFocusedChild.isAccessibilityFocused() && accessibilityFocusPosition != INVALID_POSITION) { // Attempt to restore accessibility focus, if necessary. final View newAccessibilityFocusedView = viewRootImpl.getAccessibilityFocusedHost(); if (newAccessibilityFocusedView == null) { if (accessibilityFocusLayoutRestoreView != null && accessibilityFocusLayoutRestoreView.isAttachedToWindow()) { final AccessibilityNodeProvider provider = accessibilityFocusLayoutRestoreView.getAccessibilityNodeProvider(); if (accessibilityFocusLayoutRestoreNode != null && provider != null) { final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId( accessibilityFocusLayoutRestoreNode.getSourceNodeId()); provider.performAction(virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } else { accessibilityFocusLayoutRestoreView.requestAccessibilityFocus(); } } else if (accessibilityFocusPosition != INVALID_POSITION) { // Bound the position within the visible children. final int position = MathUtils.constrain( accessibilityFocusPosition - mFirstPosition, 0, getChildCount() - 1); Loading @@ -1341,10 +1360,6 @@ public class GridView extends AbsListView { } } if (focusedChild != null) { focusedChild.setHasTransientState(false); } mLayoutMode = LAYOUT_NORMAL; mDataChanged = false; if (mPositionScrollAfterLayout != null) { Loading
core/java/android/widget/ListView.java +114 −30 Original line number Diff line number Diff line Loading @@ -39,10 +39,12 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.widget.RemoteViews.RemoteView; import java.util.ArrayList; Loading Loading @@ -1567,22 +1569,58 @@ public class ListView extends AbsListView { setSelectedPositionInt(mNextSelectedPosition); // Remember which child, if any, had accessibility focus. final int accessibilityFocusPosition; final View accessFocusedChild = getAccessibilityFocusedChild(); if (accessFocusedChild != null) { accessibilityFocusPosition = getPositionForView(accessFocusedChild); accessFocusedChild.setHasTransientState(true); } else { accessibilityFocusPosition = INVALID_POSITION; AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; View accessibilityFocusLayoutRestoreView = null; int accessibilityFocusPosition = INVALID_POSITION; // Remember which child, if any, had accessibility focus. This must // occur before recycling any views, since that will clear // accessibility focus. final ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { final View focusHost = viewRootImpl.getAccessibilityFocusedHost(); if (focusHost != null) { final View focusChild = getAccessibilityFocusedChild(focusHost); if (focusChild != null) { if (!dataChanged || isDirectChildHeaderOrFooter(focusChild) || focusChild.hasTransientState() || mAdapterHasStableIds) { // The views won't be changing, so try to maintain // focus on the current host and virtual view. accessibilityFocusLayoutRestoreView = focusHost; accessibilityFocusLayoutRestoreNode = viewRootImpl .getAccessibilityFocusedVirtualView(); } // 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. // If all else fails, maintain focus at the same // position. accessibilityFocusPosition = getPositionForView(focusChild); } } } View focusLayoutRestoreDirectChild = null; View focusLayoutRestoreView = null; // 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) { focusedChild.setHasTransientState(true); // TODO: in some cases focusedChild.getParent() == null // We can remember the focused view to restore after re-layout // 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(); } // Pull all children into the RecycleBin. Loading Loading @@ -1656,20 +1694,24 @@ public class ListView extends AbsListView { recycleBin.scrapActiveViews(); if (sel != null) { 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. // 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, but we still want to // make sure something else outside of the selected view // has 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); Loading @@ -1687,15 +1729,30 @@ public class ListView extends AbsListView { mSelectedTop = 0; mSelectorRect.setEmpty(); } } if (accessFocusedChild != null) { accessFocusedChild.setHasTransientState(false); // 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(); } } // If we failed to maintain accessibility focus on the previous // view, attempt to restore it to the previous position. if (!accessFocusedChild.isAccessibilityFocused() && accessibilityFocusPosition != INVALID_POSITION) { // Attempt to restore accessibility focus, if necessary. final View newAccessibilityFocusedView = viewRootImpl.getAccessibilityFocusedHost(); if (newAccessibilityFocusedView == null) { if (accessibilityFocusLayoutRestoreView != null && accessibilityFocusLayoutRestoreView.isAttachedToWindow()) { final AccessibilityNodeProvider provider = accessibilityFocusLayoutRestoreView.getAccessibilityNodeProvider(); if (accessibilityFocusLayoutRestoreNode != null && provider != null) { final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId( accessibilityFocusLayoutRestoreNode.getSourceNodeId()); provider.performAction(virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } else { accessibilityFocusLayoutRestoreView.requestAccessibilityFocus(); } } else if (accessibilityFocusPosition != INVALID_POSITION) { // Bound the position within the visible children. final int position = MathUtils.constrain( accessibilityFocusPosition - mFirstPosition, 0, getChildCount() - 1); Loading @@ -1706,8 +1763,11 @@ public class ListView extends AbsListView { } } if (focusedChild != null) { focusedChild.setHasTransientState(false); // 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(); } mLayoutMode = LAYOUT_NORMAL; Loading @@ -1733,6 +1793,30 @@ public class ListView extends AbsListView { } } /** * @param child a direct child of this list. * @return Whether child is a header or footer view. */ private boolean isDirectChildHeaderOrFooter(View child) { final ArrayList<FixedViewInfo> headers = mHeaderViewInfos; final int numHeaders = headers.size(); for (int i = 0; i < numHeaders; i++) { if (child == headers.get(i).view) { return true; } } final ArrayList<FixedViewInfo> footers = mFooterViewInfos; final int numFooters = footers.size(); for (int i = 0; i < numFooters; i++) { if (child == footers.get(i).view) { return true; } } return false; } /** * Obtain the view and add it to our list of children. The view can be made * fresh, converted from an unused view, or used as is if it was in the Loading