Skip to content

Commit 30ee76c

Browse files
committed
Restore accessibility focus after ListView layout.
Bug: 6439454 Change-Id: Ia61f5153b32c6ce5d18301f74e7e79c86349b987
1 parent 5851c6e commit 30ee76c

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

core/java/android/widget/ListView.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import android.graphics.Rect;
3030
import android.graphics.drawable.Drawable;
3131
import android.util.AttributeSet;
32+
import android.util.MathUtils;
3233
import android.util.SparseBooleanArray;
3334
import android.view.FocusFinder;
3435
import android.view.KeyEvent;
@@ -1490,6 +1491,10 @@ protected void layoutChildren() {
14901491

14911492
View focusLayoutRestoreView = null;
14921493

1494+
AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
1495+
View accessibilityFocusLayoutRestoreView = null;
1496+
int accessibilityFocusPosition = INVALID_POSITION;
1497+
14931498
// Remember stuff we will need down below
14941499
switch (mLayoutMode) {
14951500
case LAYOUT_SET_SELECTION:
@@ -1584,6 +1589,25 @@ protected void layoutChildren() {
15841589
requestFocus();
15851590
}
15861591

1592+
// Remember which child, if any, had accessibility focus.
1593+
final View accessFocusedView = getViewRootImpl().getAccessibilityFocusedHost();
1594+
if (accessFocusedView != null) {
1595+
final View accessFocusedChild = findAccessibilityFocusedChild(accessFocusedView);
1596+
if (accessFocusedChild != null) {
1597+
if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
1598+
// If the views won't be changing, try to maintain focus
1599+
// on the current view host and (if applicable) its
1600+
// virtual view.
1601+
accessibilityFocusLayoutRestoreView = accessFocusedView;
1602+
accessibilityFocusLayoutRestoreNode = getViewRootImpl()
1603+
.getAccessibilityFocusedVirtualView();
1604+
} else {
1605+
// Otherwise, try to maintain focus at the same position.
1606+
accessibilityFocusPosition = getPositionForView(accessFocusedChild);
1607+
}
1608+
}
1609+
}
1610+
15871611
// Clear out old views
15881612
detachAllViewsFromParent();
15891613
recycleBin.removeSkippedScrap();
@@ -1682,6 +1706,22 @@ protected void layoutChildren() {
16821706
}
16831707
}
16841708

1709+
// Attempt to restore accessibility focus.
1710+
if (accessibilityFocusLayoutRestoreNode != null) {
1711+
accessibilityFocusLayoutRestoreNode.performAction(
1712+
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
1713+
} else if (accessibilityFocusLayoutRestoreView != null) {
1714+
accessibilityFocusLayoutRestoreView.requestAccessibilityFocus();
1715+
} else if (accessibilityFocusPosition != INVALID_POSITION) {
1716+
// Bound the position within the visible children.
1717+
final int position = MathUtils.constrain(
1718+
(accessibilityFocusPosition - mFirstPosition), 0, (getChildCount() - 1));
1719+
final View restoreView = getChildAt(position);
1720+
if (restoreView != null) {
1721+
restoreView.requestAccessibilityFocus();
1722+
}
1723+
}
1724+
16851725
// tell focus view we are done mucking with it, if it is still in
16861726
// our view hierarchy.
16871727
if (focusLayoutRestoreView != null
@@ -1712,6 +1752,22 @@ protected void layoutChildren() {
17121752
}
17131753
}
17141754

1755+
/**
1756+
* @param focusedView the view that has accessibility focus.
1757+
* @return the direct child that contains accessibility focus.
1758+
*/
1759+
private View findAccessibilityFocusedChild(View focusedView) {
1760+
ViewParent viewParent = focusedView.getParent();
1761+
while ((viewParent instanceof View) && (viewParent != this)) {
1762+
focusedView = (View) viewParent;
1763+
viewParent = viewParent.getParent();
1764+
}
1765+
if (!(viewParent instanceof View)) {
1766+
return null;
1767+
}
1768+
return focusedView;
1769+
}
1770+
17151771
/**
17161772
* @param child a direct child of this list.
17171773
* @return Whether child is a header or footer view.

0 commit comments

Comments
 (0)