下面列出了android.view.accessibility.AccessibilityNodeInfo#isClickable ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* 点击指定的节点
* @param nodeInfo {@link AccessibilityNodeInfo}
* @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点
* @param clickAll 判断是否点击全部节点
* @return {@code true} success, {@code false} fail
*/
public static boolean performClick(final AccessibilityNodeInfo nodeInfo, final boolean clickParent, final boolean clickAll) {
if (nodeInfo == null) return false;
if (clickParent) {
if (nodeInfo.isClickable()) {
return nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
} else {
AccessibilityNodeInfo parent = nodeInfo.getParent();
while (parent != null) {
if (performClick(parent)) {
if (!clickAll) {
return true;
}
}
parent = parent.getParent();
}
return true;
}
} else {
return performClick(nodeInfo);
}
}
/**
* 查找对应文本的View
*
* @param text text
* @param clickable 该View是否可以点击
* @return View
*/
public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return null;
}
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
return nodeInfo;
}
}
}
return null;
}
/**
* 查找对应文本的View
*
* @param text text
* @param clickable 该View是否可以点击
* @return View
*/
public List<AccessibilityNodeInfo> findViewListByText(String text, boolean clickable) {
List<AccessibilityNodeInfo> accessibilityNodeInfoList = new ArrayList<>();
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return null;
}
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
accessibilityNodeInfoList.add(nodeInfo);
}
}
}
return accessibilityNodeInfoList;
}
private void initAccessibilityNodeInfo(AccessibilityNodeInfo info) {
this.className = StringUtil.nonNullString(info.getClassName());
this.packageName = StringUtil.nonNullString(info.getPackageName());
this.resourceId = info.getViewIdResourceName();
this.text = StringUtil.nonNullString(info.getText());
this.description = StringUtil.nonNullString(info.getContentDescription());
Rect rect = new Rect();
info.getBoundsInScreen(rect);
this.nodeBound = rect;
this.isScrollable = info.isScrollable();
this.visible = info.isVisibleToUser();
this.isClickable = info.isClickable();
this.isFocusable = info.isFocusable();
this.isEditable = info.isEditable();
}
public void recycle(AccessibilityNodeInfo info) {
try {
if (info.getChildCount() == 0) {
if (info.getText() != null) {
if ("领取红包".equals(info.getText().toString())) {
if (info.isClickable()) {
info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
AccessibilityNodeInfo parent = info.getParent();
while (parent != null) {
if (parent.isClickable()) {
parents.add(parent);
break;
}
parent = parent.getParent();
}
}
}
} else {
for (int i = 0; i < info.getChildCount(); i++) {
if (info.getChild(i) != null) {
recycle(info.getChild(i));
}
}
}
} catch (Exception e) {
}
}
/**
* We're looking for UI controls that are enabled, clickable but have no text nor
* content-description. Such controls configuration indicate an interactive control is present
* in the UI and is most likely not accessibility friendly. We refer to such controls here as
* NAF controls (Not Accessibility Friendly)
*
* @return false if a node fails the check, true if all is OK
*/
private static boolean nafCheck(AccessibilityNodeInfo node) {
boolean isNaf = node.isClickable() && node.isEnabled() && safeCharSeqToString(node.getContentDescription()).isEmpty() && safeCharSeqToString(node.getText()).isEmpty();
if (!isNaf) return true;
// check children since sometimes the containing element is clickable
// and NAF but a child's text or description is available. Will assume
// such layout as fine.
return childNafCheck(node);
}
/**
* 点击指定的节点
* @param nodeInfo {@link AccessibilityNodeInfo}
* @return {@code true} success, {@code false} fail
*/
public static boolean performClick(final AccessibilityNodeInfo nodeInfo) {
if (nodeInfo != null && nodeInfo.isClickable()) {
return preformAction(nodeInfo, AccessibilityNodeInfo.ACTION_CLICK);
}
return false;
}
/**
* 长按指定的节点
* @param nodeInfo {@link AccessibilityNodeInfo}
* @return {@code true} success, {@code false} fail
*/
public static boolean performLongClick(final AccessibilityNodeInfo nodeInfo) {
if (nodeInfo != null && nodeInfo.isClickable()) {
return preformAction(nodeInfo, AccessibilityNodeInfo.ACTION_LONG_CLICK);
}
return false;
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
if (eventType == TYPE_NOTIFICATION_STATE_CHANGED) {
unlockScreen();
luckyClicked = false;
}
if (eventType == TYPE_WINDOW_CONTENT_CHANGED) {
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
if (null == rootNode) return;
List<AccessibilityNodeInfo> list = rootNode.findAccessibilityNodeInfosByText(getString(R.string.get_lucky));
if (null == list || list.size() == 0) return;
AccessibilityNodeInfo parent = list.get(list.size() - 1);
while (null != parent) {
if (parent.isClickable() && !luckyClicked) {
parent.performAction(ACTION_CLICK);
luckyClicked = true;
break;
}
parent = parent.getParent();
}
}
if (eventType == TYPE_WINDOW_STATE_CHANGED) {
String clazzName = event.getClassName().toString();
if (clazzName.equals(UI_RECEIVE)) {
traverseNode(event.getSource());
}
if (clazzName.equals(UI_DETAIL) && hasLucky) {
hasLucky = false;
handler.sendEmptyMessageDelayed(MSG_BACK, 1000);
}
}
}
private void performClick(String resourceId) {
Log.i("mService","点击执行");
AccessibilityNodeInfo nodeInfo = this.getRootInActiveWindow();
AccessibilityNodeInfo targetNode = null;
targetNode = findNodeInfosById(nodeInfo,"com.youmi.android.addemo:id/"+resourceId);
if (targetNode.isClickable()) {
targetNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
/**
* 模拟点击事件
*
* @param nodeInfo nodeInfo
*/
public void performViewClick(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null) {
return;
}
while (nodeInfo != null) {
if (nodeInfo.isClickable()) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;
}
nodeInfo = nodeInfo.getParent();
}
}
/**
* performClickAction
* @param aAccessibilityNodeInfo aAccessibilityNodeInfo
* @return
*/
private boolean performClickAction(AccessibilityNodeInfo aAccessibilityNodeInfo) {
int targetAction = AccessibilityNodeInfo.ACTION_CLICK;
if ((aAccessibilityNodeInfo != null) && aAccessibilityNodeInfo.isEnabled() && aAccessibilityNodeInfo.isClickable()
&& aAccessibilityNodeInfo.performAction(targetAction)) {
return true;
}
return false;
}
/**
* 获取一个可以点击的节点
*/
private AccessibilityNodeInfo getClickableNode(AccessibilityNodeInfo node) {
AccessibilityNodeInfo parent = node;
while (parent != null) {
if (parent.isClickable()) {
break;
}
parent = parent.getParent();
}
return parent;
}
private void nextClick(List<AccessibilityNodeInfo> infos) {
if (infos != null)
for (AccessibilityNodeInfo info : infos) {
if (info.isEnabled() && info.isClickable())
info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
/**
* 在检测到空通知体的时候 enable flag
* 在之后的10次 onContentChange 都去检查微信登录
*/
private void autoLoginWX() {
while (App.WeChatAutoLoginTimes > 0) {
AccessibilityNodeInfo root = getRootInActiveWindow();
if (root == null) {
Log.d(TAG, "autoLoginWX: root is null, return");
return;
}
App.WeChatAutoLoginTimes--;
Log.v(TAG, "autoLoginWX");
if (root.getChildCount() != 1) {
Log.v(TAG, "autoLoginWX: 1");
return;
}
AccessibilityNodeInfo node = root.getChild(0);
if (node.getChildCount() != 5) {
Log.v(TAG, "autoLoginWX: 2");
return;
}
//不直接判断字符串是因为多语言适应
AccessibilityNodeInfo loginBtn = node.getChild(3);
if (!loginBtn.isClickable()) {
Log.v(TAG, "autoLoginWX: 3");
return;
}
if (!node.getChild(0).isClickable()) {
Log.v(TAG, "autoLoginWX: 4");
return;
}
if (!node.getChild(4).isClickable()) {
Log.v(TAG, "autoLoginWX: 5");
return;
}
loginBtn.performAction(AccessibilityNodeInfo.ACTION_CLICK);
Log.w(TAG, "autoLoginWX: Perform Click");
App.WeChatAutoLoginTimes = 0;
}
}
private void traverseNode(AccessibilityNodeInfo node) {
if (null == node) return;
final int count = node.getChildCount();
if (count > 0) {
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo childNode = node.getChild(i);
if (null != childNode && childNode.getClassName().equals("android.widget.Button") && childNode.isClickable()) {
childNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
hasLucky = true;
}
traverseNode(childNode);
}
}
}
/**
* Checks if the UI element's <code>clickable</code> property is currently true.
*
* @return true if it is else false
* @throws UiObjectNotFoundException
* @since API Level 16
*/
public boolean isClickable() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if(node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
return node.isClickable();
}
/** 点击事件*/
public static void performClick(AccessibilityNodeInfo nodeInfo) {
if(nodeInfo == null) {
return;
}
if(nodeInfo.isClickable()) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
} else {
performClick(nodeInfo.getParent());
}
}
/**
* 获取一个可以点击的节点
*/
private AccessibilityNodeInfo getClickableNode(AccessibilityNodeInfo node)
{
AccessibilityNodeInfo parent=node;
while (parent != null)
{
if (parent.isClickable())
{
break;
}
parent = parent.getParent();
}
return parent;
}
Builder(int id, @Nullable ViewHierarchyElementAndroid parent, AccessibilityNodeInfo fromInfo) {
// Bookkeeping
this.id = id;
this.parentId = (parent != null) ? parent.getId() : null;
// API 18+ properties
this.resourceName = AT_18 ? fromInfo.getViewIdResourceName() : null;
this.editable = AT_18 ? fromInfo.isEditable() : null;
// API 16+ properties
this.visibleToUser = AT_16 ? fromInfo.isVisibleToUser() : null;
// API 21+ properties
if (AT_21) {
ImmutableList.Builder<ViewHierarchyActionAndroid> actionBuilder =
new ImmutableList.Builder<>();
actionBuilder.addAll(
Lists.transform(
fromInfo.getActionList(),
action -> ViewHierarchyActionAndroid.newBuilder(action).build()));
this.actionList = actionBuilder.build();
}
// API 24+ properties
this.drawingOrder = AT_24 ? fromInfo.getDrawingOrder() : null;
// API 29+ properties
this.hasTouchDelegate = AT_29 ? (fromInfo.getTouchDelegateInfo() != null) : null;
// Base properties
this.className = fromInfo.getClassName();
this.packageName = fromInfo.getPackageName();
this.accessibilityClassName = fromInfo.getClassName();
this.contentDescription = SpannableStringAndroid.valueOf(fromInfo.getContentDescription());
this.text = SpannableStringAndroid.valueOf(fromInfo.getText());
this.importantForAccessibility = true;
this.clickable = fromInfo.isClickable();
this.longClickable = fromInfo.isLongClickable();
this.focusable = fromInfo.isFocusable();
this.scrollable = fromInfo.isScrollable();
this.canScrollForward =
((fromInfo.getActions() & AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) != 0);
this.canScrollBackward =
((fromInfo.getActions() & AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) != 0);
this.checkable = fromInfo.isCheckable();
this.checked = fromInfo.isChecked();
this.touchDelegateBounds = new ArrayList<>(); // Populated after construction
android.graphics.Rect tempRect = new android.graphics.Rect();
fromInfo.getBoundsInScreen(tempRect);
this.boundsInScreen = new Rect(tempRect.left, tempRect.top, tempRect.right, tempRect.bottom);
this.nonclippedHeight = null;
this.nonclippedWidth = null;
this.textSize = null;
this.textColor = null;
this.backgroundDrawableColor = null;
this.typefaceStyle = null;
this.enabled = fromInfo.isEnabled();
}