場景:第三方彈窗被透明 Activity 覆蓋,需點擊彈窗按鈕但拿不到實例。
// 拿到按鈕引用 (需通過反射/遍歷 View 樹/AccessibilityService 獲取)
Button targetBtn = findTargetButton();
if (targetBtn != null) {
targetBtn.performClick(); // 觸發 OnClickListener、觸摸反饋、狀態變化,完全等同手動點擊
}
限制:必須拿到 View 實例。覆蓋層攔截觸摸事件時,下層 View 無法接收觸摸,但 performClick() 繞過觸摸分發直接調用監聽器。
// 覆蓋層 Activity/View 攔截觸摸並轉發
@Override
public boolean onTouchEvent(MotionEvent event) {
// 1. 計算目標按鈕在屏幕上的絕對座標區域
Rect btnRect = getTargetButtonScreenRect(); // 需預先知道或動態計算
// 2. 判斷觸摸點是否在按鈕區域
if (btnRect.contains((int)event.getRawX(), (int)event.getRawY())) {
// 3. 構造轉發事件 (座標轉換到目標 View 坐標系)
MotionEvent forwarded = MotionEvent.obtain(event);
forwarded.offsetLocation(-btnRect.left, -btnRect.top); // 轉為相對座標
// 4. 發送給目標 View (需持有引用或通過 WindowManager 全局分發)
targetView.dispatchTouchEvent(forwarded);
forwarded.recycle();
return true; // 消費事件
}
return super.onTouchEvent(event);
}
// AccessibilityService 中
AccessibilityNodeInfo node = findNodeByText("確定按鈕"); // 或 ID/類名
if (node != null) {
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
優點:跨應用、無需 View 引用、系統級權限。缺點:需用戶在設置開啟無障礙服務,審核嚴格。
Dialog/PopupWindow 而非全屏 ActivityWindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 允許觸摸穿透| 方法 | 需 View 引用 | 觸發完整點擊流程 | 跨進程 | 適用場景 |
|---|---|---|---|---|
view.performClick() | 是 | 是 (OnClickListener + 狀態 + 反饋) | 否 | 持有引用、同進程 |
view.dispatchTouchEvent() | 是 | 是 (完整觸摸分發) | 否 | 需座標轉換、複雜手勢 |
| AccessibilityService | 否 (查找節點) | 是 | 是 | 跨應用、無引用、用戶授權 |
| UI 重設計 | - | - | - | 根治、最穩健 |
優先推薦:UI 重設計避免覆蓋 → performClick() 持有引用 → AccessibilityService 無引用。觸摸分發轉發複雜且易出兼容性問題,非必要不建議。