|
29 | 29 | import android.graphics.Rect; |
30 | 30 | import android.graphics.drawable.Drawable; |
31 | 31 | import android.util.AttributeSet; |
| 32 | +import android.util.MathUtils; |
32 | 33 | import android.util.SparseBooleanArray; |
33 | 34 | import android.view.FocusFinder; |
34 | 35 | import android.view.KeyEvent; |
@@ -1490,6 +1491,10 @@ protected void layoutChildren() { |
1490 | 1491 |
|
1491 | 1492 | View focusLayoutRestoreView = null; |
1492 | 1493 |
|
| 1494 | + AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null; |
| 1495 | + View accessibilityFocusLayoutRestoreView = null; |
| 1496 | + int accessibilityFocusPosition = INVALID_POSITION; |
| 1497 | + |
1493 | 1498 | // Remember stuff we will need down below |
1494 | 1499 | switch (mLayoutMode) { |
1495 | 1500 | case LAYOUT_SET_SELECTION: |
@@ -1584,6 +1589,25 @@ protected void layoutChildren() { |
1584 | 1589 | requestFocus(); |
1585 | 1590 | } |
1586 | 1591 |
|
| 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 | + |
1587 | 1611 | // Clear out old views |
1588 | 1612 | detachAllViewsFromParent(); |
1589 | 1613 | recycleBin.removeSkippedScrap(); |
@@ -1682,6 +1706,22 @@ protected void layoutChildren() { |
1682 | 1706 | } |
1683 | 1707 | } |
1684 | 1708 |
|
| 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 | + |
1685 | 1725 | // tell focus view we are done mucking with it, if it is still in |
1686 | 1726 | // our view hierarchy. |
1687 | 1727 | if (focusLayoutRestoreView != null |
@@ -1712,6 +1752,22 @@ protected void layoutChildren() { |
1712 | 1752 | } |
1713 | 1753 | } |
1714 | 1754 |
|
| 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 | + |
1715 | 1771 | /** |
1716 | 1772 | * @param child a direct child of this list. |
1717 | 1773 | * @return Whether child is a header or footer view. |
|
0 commit comments