简书链接:一种免root的点击方案探索改进
文章字数:507,阅读全文大约需要2分钟

网上的文章很少,我感觉这个东西搞得好也是完全可以和adb 的触摸媲美的。
这东西主要是用在神之手实现不需要root的情况下自动点击app.也不需要开启那些无障碍服务。
目前我已经用这个做了键盘自动输入验证码各种页面自动填表。

要点击子view,使用dispatchTouchEvent
如果只需要点击当前,而且能找到当前view,则直接调用此view的onTouchEvent方法。

点击屏幕中的某个view通过父view方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
View decorView = mainActivity.getWindow().getDecorView();
int[] outlocation = new int[2];
btn_btn1.getLocationInWindow(outlocation);
int y = outlocation[1];



final long downTime = SystemClock.uptimeMillis();
Log.w(TAG, "按下时间:");
int rawX=outlocation[0];
int rawY=outlocation[1];
rawY=rawY+10;
int finalRawY = rawY;
int width = decorView.getWidth();
MotionEvent obtain = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, rawX, rawY, 0);
decorView.dispatchTouchEvent(obtain);
decorView.postDelayed(new Runnable() {
@Override
public void run() {
final long upTime = SystemClock.uptimeMillis();
MotionEvent obtainUp = MotionEvent.obtain(upTime, upTime, MotionEvent.ACTION_MOVE, width, finalRawY, 0);
decorView.dispatchTouchEvent(obtainUp);

Log.w(TAG, "长按完成,模拟抬起事件");
}
}, 2000);
decorView.postDelayed(new Runnable() {
@Override
public void run() {
final long upTime = SystemClock.uptimeMillis();
MotionEvent obtainUp = MotionEvent.obtain(upTime, upTime, MotionEvent.ACTION_UP, width, finalRawY, 0);
decorView.dispatchTouchEvent(obtainUp);
Log.w(TAG, "长按完成,模拟抬起事件");
}
}, 6000);

Log.w(TAG, "Y:" + y + ",screenHeight:" + decorView.getHeight() + "");
}
},1200);

测试触摸效果的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package cn.qssq666.main;

import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.Random;

/**
* Created by qssq on 2018/4/5 [email protected]
*/
public class UiClickUtil {
private static final String TAG = "UiClickUtil";



public static void clickView(View paramX) {
performViewOnTouchEvent(paramX, 0);
}

public static void longPressView(View paramX) {
longPressView(paramX, 2 * 1000);
}

/**
* @param paramX
* @param actionUpTime 长按时间
*/
public static void longPressView(View paramX, int actionUpTime) {

performViewOnTouchEvent(paramX, actionUpTime);
}

public static void performViewOnTouchEvent(View paramX, int actionUpTime) {
int[] actualRawX = getActualRawX(paramX);
performViewOnTouchEvent(paramX, actualRawX[0], actualRawX[1], actionUpTime);

}

public static void performViewOnTouchEvent(View view, final int rawX, final int rawY, int actionUpTime) {
final long downTime = SystemClock.uptimeMillis();
Log.w(TAG, "按下时间:");
MotionEvent obtain = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, rawX, rawY, 0);
view.onTouchEvent(obtain);

view.postDelayed(new Runnable() {
@Override
public void run() {
final long upTime = SystemClock.uptimeMillis();
MotionEvent obtainUp = MotionEvent.obtain(upTime, upTime, MotionEvent.ACTION_UP, rawX, rawY, 0);
view.onTouchEvent(obtainUp);
Log.w(TAG, "长按完成,模拟抬起事件");
}
}, actionUpTime);

}

public static void performClickChildView(View view, final int rawX, final int rawY, int actionUpTime) {
final long downTime = SystemClock.uptimeMillis();
Log.w(TAG, "按下时间:");
MotionEvent obtain = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, rawX, rawY, 0);
view.dispatchTouchEvent(obtain);

view.postDelayed(new Runnable() {
@Override
public void run() {
final long upTime = SystemClock.uptimeMillis();
MotionEvent obtainUp = MotionEvent.obtain(upTime, upTime, MotionEvent.ACTION_UP, rawX, rawY, 0);
view.dispatchTouchEvent(obtainUp);
Log.w(TAG, "长按完成,模拟抬起事件");
}
}, actionUpTime);

}


public static int[] getActualRawX(View view) {
int[] location = new int[2];
view.getLocationOnScreen(location);
location[0] = location[0] + new Random().nextInt(view.getWidth());
location[1] = location[1] + new Random().nextInt(view.getHeight());
return location;
}

public static void performUpEvent(View paramX) {
int[] actualRawX = getActualRawX(paramX);
performUpEvent(paramX, actualRawX[0], actualRawX[1]);
}

public static void performUpEvent(View paramX, int rawX, int rawY) {
MotionEvent obtain = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 5000, MotionEvent.ACTION_UP, rawX, rawY, 0);
paramX.onTouchEvent(obtain);


}

public static void performMoveEvent(View paramX) {
int[] actualRawX = getActualRawX(paramX);
performMoveEvent(paramX, actualRawX[0], actualRawX[1]);
}

public static void performMoveEvent(View paramX, int rawX, int rawY) {
MotionEvent obtain = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 5000, MotionEvent.ACTION_MOVE, rawX, rawY, 0);
paramX.onTouchEvent(obtain);
}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

package cn.qssq666.ui;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import android.view.View;

public class MyView extends SurfaceView implements Callback, View.OnTouchListener {

private static final String TAG = "MyView";
private Paint p = new Paint();
private Path path = new Path();

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
p.setColor(Color.RED);
p.setTextSize(10);
p.setAntiAlias(true);
p.setStyle(Paint.Style.STROKE);
setOnTouchListener(this);

}

public void draw() {
Canvas canvas = getHolder().lockCanvas();
canvas.drawColor(Color.WHITE);
canvas.drawPath(path, p);
getHolder().unlockCanvasAndPost(canvas);
}


public void clear() {
path.reset();
draw();
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
draw();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub

}

@Override
public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(event.getX(), event.getY());
draw();
break;

case MotionEvent.ACTION_MOVE:
path.lineTo(event.getX(), event.getY());
Log.w(TAG,"屏幕Y"+event.getRawY());
draw();
break;
}

return true;
}

}

getRawY 配合getLocationInWindow()
因此父view点击子view,用这两个进行测试才比较合理,精准。
getY是相对于view自身空间,
一个某自定义绘制的abcd索引view点击第一个条目,忽略标题高度,经过计算标题高度17dp 所以加上17+第一个条目的高度减去一半就点了第一个条目中间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  try {
int rl_content = ResourcesUtil.getId(decorView.getContext(), "rl_content");
ViewGroup currentView = (ViewGroup) decorView.findViewById(rl_content);
if (currentView == null || currentView.getChildCount() == 0) {
ToastUtils.showToast("无法完成自动选择,请手动选择");

Commons.finishAndclearFirstTask();
} else {

}
int[] outlocation = new int[2];
currentView.getLocationInWindow(outlocation);
int y = outlocation[1] +AppUtil.dip2px(AppContext.getInstance(), 20);//这里的30是为了找到这个容器往下面一点点击一个item.
int x=outlocation[0];
UiClickUtil.performClickChildView(decorView, x+currentView.getWidth()/2, y, 2000);
// UiClickUtil.performClickChildView(decorView, x+currentView.getWidth()/2, 455, 2000);
// View view = viewGroup.getChildAt(0);
// view.callOnClick();
Log.w(TAG, "getRawY:" + y + ",screenHeight:" + decorView.getHeight() + "rawx:"+x+",rawY:"+outlocation[1]);
ToastUtils.showToast("已点击完成");

} catch (Throwable e) {
Log.w(TAG, "ERROR", e);
}