下面列出了怎么用android.support.v4.view.accessibility.AccessibilityNodeInfoCompat的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Determines if the current item is at the edge of a list by checking the
* scrollable predecessors of the items on either or both sides.
*
* @param context The parent context.
* @param node The node to check.
* @param direction The direction in which to check, one of:
* <ul>
* <li>{@code -1} to check backward
* <li>{@code 0} to check both backward and forward
* <li>{@code 1} to check forward
* </ul>
* @param filter (Optional) Filter used to validate list-type ancestors.
* @return true if the current item is at the edge of a list.
*/
public static boolean isEdgeListItem(
Context context, AccessibilityNodeInfoCompat node, int direction, NodeFilter filter) {
if (node == null) {
return false;
}
if ((direction <= 0) && isMatchingEdgeListItem(context, node,
NodeFocusFinder.SEARCH_BACKWARD, FILTER_SCROLL_BACKWARD.and(filter))) {
return true;
}
if ((direction >= 0) && isMatchingEdgeListItem(context, node,
NodeFocusFinder.SEARCH_FORWARD, FILTER_SCROLL_FORWARD.and(filter))) {
return true;
}
return false;
}
@Override
protected void onPopulateNodeForVirtualView(
int virtualViewId, AccessibilityNodeInfoCompat node) {
if (mVirtualViews == null || mVirtualViews.size() <= virtualViewId) {
// TODO(clholgat): Remove this work around when the Android bug is fixed.
// crbug.com/420177
node.setBoundsInParent(mPlaceHolderRect);
node.setContentDescription(PLACE_HOLDER_STRING);
return;
}
VirtualView view = mVirtualViews.get(virtualViewId);
view.getTouchTarget(mTouchTarget);
node.setBoundsInParent(rectToPx(mTouchTarget));
node.setContentDescription(view.getAccessibilityDescription());
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
node.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS);
node.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK);
}
/**
* This should really be in AccessibilityNodeInfoCompat, but there unfortunately
* seem to be a few elements that are not easily cloneable using the underlying API.
* Leave it private here as it's not general-purpose useful.
*/
private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
AccessibilityNodeInfoCompat src) {
final Rect rect = mTmpRect;
src.getBoundsInParent(rect);
dest.setBoundsInParent(rect);
src.getBoundsInScreen(rect);
dest.setBoundsInScreen(rect);
dest.setVisibleToUser(src.isVisibleToUser());
dest.setPackageName(src.getPackageName());
dest.setClassName(src.getClassName());
dest.setContentDescription(src.getContentDescription());
dest.setEnabled(src.isEnabled());
dest.setClickable(src.isClickable());
dest.setFocusable(src.isFocusable());
dest.setFocused(src.isFocused());
dest.setAccessibilityFocused(src.isAccessibilityFocused());
dest.setSelected(src.isSelected());
dest.setLongClickable(src.isLongClickable());
dest.addAction(src.getActions());
}
private boolean moveFocus(AccessibilityNodeInfoCompat from,
int direction) {
int searchDirection = (direction == DIRECTION_BACKWARD)
? FocusFinder.SEARCH_BACKWARD
: FocusFinder.SEARCH_FORWARD;
AccessibilityNodeInfoCompat next = null;
next = mFocusFinder.linear(from, searchDirection);
try {
if (next != null) {
return next.performAction(
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
}
} finally {
AccessibilityNodeInfoUtils.recycleNodes(next);
}
return false;
}
private boolean handleIncrementalSearchAction() {
AccessibilityNodeInfoCompat currentNode = getFocusedNode(true);
try {
if (currentNode != null
&& WebInterfaceUtils.hasLegacyWebContent(currentNode)
&& mFeedbackManager.emitOnFailure(
WebInterfaceUtils.performSpecialAction(currentNode,
ACTION_TOGGLE_INCREMENTAL_SEARCH),
FeedbackManager.TYPE_COMMAND_FAILED)) {
return true;
}
return false;
} finally {
AccessibilityNodeInfoUtils.recycleNodes(currentNode);
}
}
@Override
public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
ViewGroup.LayoutParams lp = host.getLayoutParams();
if (!(lp instanceof LayoutParams)) {
super.onInitializeAccessibilityNodeInfoForItem(host, info);
return;
}
LayoutParams glp = (LayoutParams) lp;
int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewPosition());
if (mOrientation == HORIZONTAL) {
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
glp.getSpanIndex(), glp.getSpanSize(),
spanGroupIndex, 1,
mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
} else { // VERTICAL
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
spanGroupIndex , 1,
glp.getSpanIndex(), glp.getSpanSize(),
mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
}
}
/**
* This should really be in AccessibilityNodeInfoCompat, but there unfortunately
* seem to be a few elements that are not easily cloneable using the underlying API.
* Leave it private here as it's not general-purpose useful.
*/
private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
AccessibilityNodeInfoCompat src) {
final Rect rect = mTmpRect;
src.getBoundsInParent(rect);
dest.setBoundsInParent(rect);
src.getBoundsInScreen(rect);
dest.setBoundsInScreen(rect);
dest.setVisibleToUser(src.isVisibleToUser());
dest.setPackageName(src.getPackageName());
dest.setClassName(src.getClassName());
dest.setContentDescription(src.getContentDescription());
dest.setEnabled(src.isEnabled());
dest.setClickable(src.isClickable());
dest.setFocusable(src.isFocusable());
dest.setFocused(src.isFocused());
dest.setAccessibilityFocused(src.isAccessibilityFocused());
dest.setSelected(src.isSelected());
dest.setLongClickable(src.isLongClickable());
dest.addAction(src.getActions());
}
@Override
public void format(Editable result,
Context context,
AccessibilityNodeInfoCompat node) {
boolean empty = (node.getChildCount() == 0);
int res;
if (AccessibilityNodeInfoUtils.nodeMatchesClassByType(context, node,
GridView.class)) {
res = empty ? R.string.type_emptygridview : R.string.type_gridview;
} else if (AccessibilityNodeInfoUtils.nodeMatchesClassByType(
context, node, ScrollView.class)) {
res = empty ? R.string.type_emptyscrollview
: R.string.type_scrollview;
} else {
res = empty ? R.string.type_emptylistview : R.string.type_listview;
}
result.append(context.getString(res));
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
super.onInitializeAccessibilityNodeInfo(host, superNode);
info.setSource(host);
final ViewParent parent = ViewCompat.getParentForAccessibility(host);
if (parent instanceof View) {
info.setParent((View) parent);
}
copyNodeInfoNoChildren(info, superNode);
superNode.recycle();
addChildrenForAccessibility(info, (ViewGroup) host);
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (canScrollHorizontally(1)) {
setCurrentItem(mCurItem + 1);
return true;
}
} return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (canScrollHorizontally(-1)) {
setCurrentItem(mCurItem - 1);
return true;
}
} return false;
}
return false;
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
super.onInitializeAccessibilityNodeInfo(host, superNode);
info.setSource(host);
final ViewParent parent = ViewCompat.getParentForAccessibility(host);
if (parent instanceof View) {
info.setParent((View) parent);
}
copyNodeInfoNoChildren(info, superNode);
superNode.recycle();
addChildrenForAccessibility(info, (ViewGroup) host);
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (canScrollHorizontally(1)) {
setCurrentItem(mCurItem + 1);
return true;
}
} return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (canScrollHorizontally(-1)) {
setCurrentItem(mCurItem - 1);
return true;
}
} return false;
}
return false;
}
/**
* Necessary for accessibility, to ensure we support "scrolling" forward and backward
* in the circle.
*/
@Override
@SuppressWarnings("deprecation")
public void onInitializeAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (Build.VERSION.SDK_INT >= 21) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
}
else if (Build.VERSION.SDK_INT >= 16) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
} else {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
}
}
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
NestedScrollView nsvHost = (NestedScrollView) host;
info.setClassName(ScrollView.class.getName());
if (nsvHost.isEnabled()) {
int scrollRange = nsvHost.getScrollRange();
if (scrollRange > 0) {
info.setScrollable(true);
if (nsvHost.getScrollY() > 0) {
info.addAction(8192);
}
if (nsvHost.getScrollY() < scrollRange) {
info.addAction(4096);
}
}
}
}
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
if (DrawerLayout.CAN_HIDE_DESCENDANTS) {
super.onInitializeAccessibilityNodeInfo(host, info);
} else {
AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
super.onInitializeAccessibilityNodeInfo(host, superNode);
info.setSource(host);
ViewParent parent = ViewCompat.getParentForAccessibility(host);
if (parent instanceof View) {
info.setParent((View) parent);
}
copyNodeInfoNoChildren(info, superNode);
superNode.recycle();
addChildrenForAccessibility(info, (ViewGroup) host);
}
info.setClassName(DrawerLayout.class.getName());
info.setFocusable(false);
info.setFocused(false);
info.removeAction(AccessibilityActionCompat.ACTION_FOCUS);
info.removeAction(AccessibilityActionCompat.ACTION_CLEAR_FOCUS);
}
/**
* Returns whether a node is actionable. That is, the node supports one of
* the following actions:
* <ul>
* <li>{@link AccessibilityNodeInfoCompat#isClickable()}
* <li>{@link AccessibilityNodeInfoCompat#isFocusable()}
* <li>{@link AccessibilityNodeInfoCompat#isLongClickable()}
* </ul>
* This parities the system method View#isActionableForAccessibility(), which
* was added in JellyBean.
*
* @param node The node to examine.
* @return {@code true} if node is actionable.
*/
public static boolean isActionableForAccessibility(AccessibilityNodeInfoCompat node) {
if (node == null) {
return false;
}
// Nodes that are clickable are always actionable.
if (isClickable(node) || isLongClickable(node)) {
return true;
}
if (node.isFocusable()) {
return true;
}
return supportsAnyAction(node, AccessibilityNodeInfoCompat.ACTION_FOCUS,
AccessibilityNodeInfoCompat.ACTION_NEXT_HTML_ELEMENT,
AccessibilityNodeInfoCompat.ACTION_PREVIOUS_HTML_ELEMENT);
}
private CharSequence getFallbackText(
Context context,
AccessibilityNodeInfoCompat node) {
// Order is important below because of class inheritance.
if (matchesAny(context, node, Button.class, ImageButton.class)) {
return context.getString(R.string.type_button);
}
if (matchesAny(context, node, QuickContactBadge.class)) {
return context.getString(R.string.type_quickcontact);
}
if (matchesAny(context, node, ImageView.class)) {
return context.getString(R.string.type_image);
}
if (matchesAny(context, node, EditText.class)) {
return context.getString(R.string.type_edittext);
}
if (matchesAny(context, node, AbsSeekBar.class)) {
return context.getString(R.string.type_seekbar);
}
return "";
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
super.onInitializeAccessibilityNodeInfo(host, superNode);
info.setSource(host);
final ViewParent parent = ViewCompat.getParentForAccessibility(host);
if (parent instanceof View) {
info.setParent((View) parent);
}
copyNodeInfoNoChildren(info, superNode);
superNode.recycle();
addChildrenForAccessibility(info, (ViewGroup) host);
}
/**
* Returns whether a node matches the class specified by
* {@code className} and exactly match the text or content description
* specified by {@code text}.
*/
private static boolean nodeMatchesFilter(Context context, AccessibilityNodeInfoCompat node,
CharSequence referenceClassName, String findText) {
final ClassLoadingManager loader = ClassLoadingManager.getInstance();
final CharSequence nodeClass = node.getClassName();
final CharSequence nodePackage = node.getPackageName();
if (!loader.checkInstanceOf(context, nodeClass, nodePackage, referenceClassName)) {
return false;
}
final CharSequence nodeText = node.getText();
if (TextUtils.equals(findText, nodeText)) {
return true;
}
final CharSequence nodeDesc = node.getContentDescription();
if (TextUtils.equals(findText, nodeDesc)) {
return true;
}
return false;
}
/**
* This should really be in AccessibilityNodeInfoCompat, but there unfortunately
* seem to be a few elements that are not easily cloneable using the underlying API.
* Leave it private here as it's not general-purpose useful.
*/
private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
AccessibilityNodeInfoCompat src) {
final Rect rect = mTmpRect;
src.getBoundsInParent(rect);
dest.setBoundsInParent(rect);
src.getBoundsInScreen(rect);
dest.setBoundsInScreen(rect);
dest.setVisibleToUser(src.isVisibleToUser());
dest.setPackageName(src.getPackageName());
dest.setClassName(src.getClassName());
dest.setContentDescription(src.getContentDescription());
dest.setEnabled(src.isEnabled());
dest.setClickable(src.isClickable());
dest.setFocusable(src.isFocusable());
dest.setFocused(src.isFocused());
dest.setAccessibilityFocused(src.isAccessibilityFocused());
dest.setSelected(src.isSelected());
dest.setLongClickable(src.isLongClickable());
dest.addAction(src.getActions());
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (internalCanScrollVertically(1)) {
setCurrentItem(mCurItem + 1);
return true;
}
}
return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (internalCanScrollVertically(-1)) {
setCurrentItem(mCurItem - 1);
return true;
}
}
return false;
}
return false;
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (canScrollHorizontally(1)) {
setCurrentItem(mCurItem + 1);
return true;
}
} return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (canScrollHorizontally(-1)) {
setCurrentItem(mCurItem - 1);
return true;
}
} return false;
}
return false;
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (canScrollHorizontally(1)) {
setCurrentItem(mCurItem + 1);
return true;
}
} return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (canScrollHorizontally(-1)) {
setCurrentItem(mCurItem - 1);
return true;
}
} return false;
}
return false;
}
private static AccessibilityNodeInfoCompat refreshFromChild(
AccessibilityNodeInfoCompat node) {
if (node.getChildCount() > 0) {
AccessibilityNodeInfoCompat firstChild = node.getChild(0);
if (firstChild != null) {
AccessibilityNodeInfoCompat parent = firstChild.getParent();
firstChild.recycle();
if (node.equals(parent)) {
return parent;
} else {
recycleNodes(parent);
}
}
}
return null;
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(ViewPager.class.getName());
info.setScrollable(canScroll());
if (canScrollHorizontally(1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
}
if (canScrollHorizontally(-1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
}
}
/**
* Logs the tree using the input node as the root.
*/
public static void logNodeTree(AccessibilityNodeInfoCompat node) {
if (node == null) {
return;
}
HashSet<AccessibilityNodeInfoCompat> seen = new HashSet<AccessibilityNodeInfoCompat>();
logNodeTree(AccessibilityNodeInfoCompat.obtain(node), "", seen);
for (AccessibilityNodeInfoCompat n : seen) {
n.recycle();
}
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
if (!mRecyclerViewDelegate.shouldIgnore()
&& mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
.onInitializeAccessibilityNodeInfoForItem(host, info);
}
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(ViewPager.class.getName());
info.setScrollable(canScroll());
if (canScrollHorizontally(1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
}
if (canScrollHorizontally(-1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
}
}
/**
* Returns the result of applying a filter using breadth-first traversal.
*
* @param context The parent context.
* @param node The root node to traverse from.
* @param filter The filter to satisfy.
* @return The first node reached via BFS traversal that satisfies the
* filter.
*/
public static AccessibilityNodeInfoCompat searchFromBfs(
Context context, AccessibilityNodeInfoCompat node, NodeFilter filter) {
if (node == null) {
return null;
}
final LinkedList<AccessibilityNodeInfoCompat> queue =
new LinkedList<AccessibilityNodeInfoCompat>();
queue.add(AccessibilityNodeInfoCompat.obtain(node));
while (!queue.isEmpty()) {
final AccessibilityNodeInfoCompat item = queue.removeFirst();
if (filter.accept(context, item)) {
return AccessibilityNodeInfoCompat.obtain(item);
}
final int childCount = item.getChildCount();
for (int i = 0; i < childCount; i++) {
final AccessibilityNodeInfoCompat child = item.getChild(i);
if (child != null) {
queue.addLast(child);
}
}
}
return null;
}
private void addChildrenForAccessibility(AccessibilityNodeInfoCompat info, ViewGroup v) {
final int childCount = v.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = v.getChildAt(i);
if (filter(child)) {
continue;
}
// Adding children that are marked as not important for
// accessibility will break the hierarchy, so we need to check
// that value and re-parent views if necessary.
final int importance = ViewCompat.getImportantForAccessibility(child);
switch (importance) {
case ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS:
// Always skip NO_HIDE views and their descendants.
break;
case ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO:
// Re-parent children of NO view groups, skip NO views.
if (child instanceof ViewGroup) {
addChildrenForAccessibility(info, (ViewGroup) child);
}
break;
case ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO:
// Force AUTO views to YES and add them.
ViewCompat.setImportantForAccessibility(
child, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
case ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES:
info.addChild(child);
break;
}
}
}