下载完apk并安装后,希望能不用人为操作就实现特定位置的点击跟滑动。
这个需求要实现有几种方法,下面讲讲尝试过的使用MotionEvent去模拟用户手指点击跟滑动屏幕事件的实现。
**
**
主要用到的是onTouch常用的4个事件:
1、ACTION_DOWN:
表示按下了屏幕,第一个执行也是必然执行的方法。
2、ACTION_MOVE:
表示为移动手势,会不断的执行直到触摸停止。
3、ACTION_UP :
表示为离开屏幕,触摸停止的时候执行。
4、ACTION_CANCEL:
表示取消手势,不会由用户产生,而是由程序产生的。
一个Action_DOWN, 多个ACTION_MOVE, 1个ACTION_UP,就构成了Android中众多的事件。
思路挺简单的:
1.获得事件对象MotionEvent
MotionEvent eventUp = MotionEvent.obtain(System.currentTimeMillis(),
System.currentTimeMillis() + 100, MotionEvent.ACTION_UP, x, y, 0);
2.分发该事件对象
activity.dispatchTouchEvent(eventUp);
三、代码实现:
封装成工具类如下,传入参数就能直接使用:
/**
* 模拟点击屏幕、滑动屏幕等操作
* Created by Jim斌 on 2017/9/9.
*/
public class TouchEvent {
/**
* 模仿手指点击控件事件
* @param view 控件
* @param x 相对控件的X坐标
* @param y 相对控件的Y坐标
*/
private static void simulateClick(View view, float x, float y) {
long downTime = SystemClock.uptimeMillis();
final MotionEvent downEvent = MotionEvent.obtain(downTime, downTime,MotionEvent.ACTION_DOWN, x, y, 0);
downTime += 1000;
final MotionEvent upEvent = MotionEvent.obtain(downTime, downTime,MotionEvent.ACTION_UP, x, y, 0);
view.onTouchEvent(downEvent);
view.onTouchEvent(upEvent);
downEvent.recycle();
upEvent.recycle();
}
/**
* 模仿手机点击屏幕事件
* @param x X坐标
* @param y Y坐标
* @param activity 传进去的活动对象
*/
public static void setFingerClick(int x, int y, Activity activity){
MotionEvent evenDownt = MotionEvent.obtain(System.currentTimeMillis(),
System.currentTimeMillis() + 100, MotionEvent.ACTION_DOWN, x, y, 0);
activity.dispatchTouchEvent(evenDownt);
MotionEvent eventUp = MotionEvent.obtain(System.currentTimeMillis(),
System.currentTimeMillis() + 100, MotionEvent.ACTION_UP, x, y, 0);
activity.dispatchTouchEvent(eventUp);
evenDownt.recycle();
eventUp.recycle();
Log.d(TAG, "setFingerClick: ");
}
/**
* 模拟向下滑动事件
* @param distance 滑动的距离
* @param activity 传进去的活动对象
*/
public static void setMoveToBottom(int distance,Activity activity){
activity.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN, 400, 500, 0));
activity.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_MOVE, 400, 500-distance, 0));
activity.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP, 400, 500-distance, 0));
Log.d(TAG, "setMoveToBottom: ");
}
/**
* 模拟向上滑动事件
* @param distance 滑动的距离
* @param activity 传进去的活动对象
*/
public static void setMoveToTop(int distance,Activity activity){
activity.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN, 400, 500, 0));
activity.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_MOVE, 400, 500+distance, 0));
activity.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP, 400, 500+distance, 0));
Log.d(TAG, "setMoveToTop: ");
}
}
当然,这个方法需要获取到手机屏幕的坐标,下面提供个拿坐标的方法,其实就是弄个自定义view,通过监听触摸事件拿到手指坐标。
1.自定义TextView
/**
* Created by Jim斌 on 2017/9/9.
*/
public class CustomTextView extends android.support.v7.widget.AppCompatTextView{
private LogListener mLogListener;
public CustomTextView(Context context) {
super(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setLogListener(LogListener pListener) {
mLogListener = pListener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
float rawX = event.getRawX();
float rawY = event.getRawY();
float x = event.getX();
float y = event.getY();
if (mLogListener != null) {
mLogListener.output("rawX = " + rawX
+ "\n rawY = " + rawY
+ "\n x = " + x
+ "\n Y = " + y);
}
break;
}
return true;
}
/**
* 用于在Actvity中实时Touch位置输出信息
*/
public interface LogListener {
public void output(String pOutput);
}
}
2.在Activity初始化这个view,设置监听:
CustomTextView customTextView = (CustomTextView) findViewById(R.id.custom_textview);
customTextView.setLogListener((CustomTextView.LogListener) new CustomLogListener());
3.监听到变化时把坐标显示在textview上
/**
* 用于获取TouchEvent中位置信息
*/
private class CustomLogListener implements CustomTextView.LogListener {
@Override
public void output(String pOutput) {
tv.setText(pOutput);
}
}
4.然后把自定义view设置到布局文件上。
<com.example.jim.motionevent.CustomTextView
android:id="@+id/custom_textview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:background="#666666"/>
**
**
这里遇到了一个**
**,我想把事件组合起来,比如点击完按钮后等待几秒,再去滑动屏幕。刚开始的时候我使用的是Thread.sleep()方法,调用完点击方法后sleep几秒、再调用滑动方法。
然而出来的效果并不是我想要的,展示出来的效果是先sleep后点击跟滑动的效果同时出现。猜想着可能受事件的分发机制影响,查了一阵子查不到原因后,决定先放一边以后再了解,有明白这个原因的大佬方便的话可以交流一下。后来我就使用java的定时器,间隔几秒去执行某个功能操作,这样出来效果就符合要求了。
然而… 我发现这个做出来后只能在demo里面实现模拟点击、模拟滑动操作,就是说,当另一个程序获得焦点,而我们在本程序跑模拟点击的逻辑时,模拟点击的代码还是有实现的,只不过它不会点到另一个程序当前占据屏幕的页面,而是在它自己的程序页面去模拟点击。所以其实并不符合我的需求,解决方案可能是找找看能不能获取到其他程序的活动对象(感觉不是很可行),或者考虑使用辅助功能(AccessibilityService)去实现。
http://blog.sina.com.cn/s/blog_7256fe8f01016tyz.html
http://www.android-doc.com/guide/components/android7.0.html?q=AlarmManager#q=AlarmManager