试图在android中以编程方式模拟滑动

时间:2015-11-13 17:28:37

标签: android

我希望我的应用程序在单击按钮时模拟滑动触摸事件(向上/向下/向左/向右),然后TextView将向下/向上滚动。

我尝试使用Motion Event,但在分别发送ACTION_DOWN,ACTION_MOVE和ACTION_UP的3个动作事件后没有任何反应。

可以模拟滑动事件吗?

public void simulation(View view){
    swipe(Direction.Bot);
}
public enum Direction {
    Top, Bot, Left, Right;
}
protected void swipe(Direction direction) {
    Point size = new Point();
    this.getWindowManager().getDefaultDisplay().getSize(size);
    int height = size.y; // height will be at top of the screen
    int width = size.x; // width will be rightmost location of the screen
    float xStart = size.x-50;
    float xEnd = size.x-50;
    float yStart = size.y-50;
    float yEnd = size.y-50;
    long downTime = SystemClock.uptimeMillis();

    if(direction == Direction.Top || direction == Direction.Bot){
        yStart = ((direction == Direction.Top) ? 50 : (height - 50));
        yEnd = ((direction == Direction.Top) ? (height - 50) : 50);
    }else {
        xStart = ((direction == Direction.Left) ? (width - 50) : 50); // true: xStart = w-10; else: = 10
        xEnd = ((direction == Direction.Left) ? 50 : (width - 50)); // true: xEnd = 10; else: = w-10
    }
    findViewById(R.id.my_id).dispatchTouchEvent(MotionEvent.obtain(downTime, SystemClock.uptimeMillis(),MotionEvent.ACTION_DOWN, xStart/2, yStart/2, 0));
    System.out.println("ACTION_DOWN");
    findViewById(R.id.my_id).dispatchTouchEvent(MotionEvent.obtain(downTime, SystemClock.uptimeMillis() + 500, MotionEvent.ACTION_MOVE, xEnd / 2, yEnd / 2, 0));
    System.out.println("ACTION_MOVE");
    findViewById(R.id.my_id).dispatchTouchEvent(MotionEvent.obtain(downTime, SystemClock.uptimeMillis() + 1000, MotionEvent.ACTION_UP, xEnd / 2, yEnd / 2, 0));
    System.out.println("ACTION_UP");

}

3 个答案:

答案 0 :(得分:0)

我能够在滚动活动演示中以编程方式模拟fling事件。

enter image description here

这是我尝试的模拟逃避事件的示例,并且有效。

蓝色虚线是我模拟的猛击事件:

enter image description here

 class ScrollingActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_scrolling)
    setSupportActionBar(toolbar)
    fab.setOnClickListener { view ->
        Thread(Runnable {
            try {

                fling(500f ,900f ,530f ,20f, 5);
               // emulateMptionEvent()

            } catch (e: Exception) {
            }
        }).start()
    }
}

/** * Simulate touching a specific location and dragging to a new location.
 *
 * @param fromX X coordinate of the initial touch, in screen coordinates
 * @param toX Xcoordinate of the drag destination, in screen coordinates
 * @param fromY X coordinate of the initial touch, in screen coordinates
 * @param toY Y coordinate of the drag destination, in screen coordinates
 * @param stepCount How many move steps to include in the drag
 */
fun fling(
    fromX: Float, toX: Float, fromY: Float,
    toY: Float, stepCount: Int
) {

    val inst = Instrumentation()

    val downTime = SystemClock.uptimeMillis()
    var eventTime = SystemClock.uptimeMillis()

    var y = fromY
    var x = fromX

    val yStep = (toY - fromY) / stepCount
    val xStep = (toX - fromX) / stepCount

    var event = MotionEvent.obtain(
        downTime, eventTime,
        MotionEvent.ACTION_DOWN, fromX, fromY, 0
    )
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
        event.source = InputDevice.SOURCE_TOUCHSCREEN
    }
    inst.sendPointerSync(event)



    for (i in 0 until stepCount) {
        y += yStep
        x += xStep
        eventTime = SystemClock.uptimeMillis()
        event = MotionEvent.obtain(
            downTime, eventTime + stepCount,
            MotionEvent.ACTION_MOVE, x, y, 0
        )
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
            event.source = InputDevice.SOURCE_TOUCHSCREEN
        }
        inst.sendPointerSync(event)
    }

    eventTime = SystemClock.uptimeMillis() + stepCount.toLong() + 2
    event = MotionEvent.obtain(
        downTime, eventTime,
        MotionEvent.ACTION_UP, toX, toY, 0
    )

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
        event.source = InputDevice.SOURCE_TOUCHSCREEN
    }
    inst.sendPointerSync(event)
}
}

希望对别人有帮助

答案 1 :(得分:0)

我编写了不需要Instrumentation的扩展功能,因此它们不仅可以在androidTest中使用,而且可以在robolectric tests中使用,甚至可以在发行版中使用:

fun ViewGroup.performSwipeToLeft(target: View) {
    this.performSwipe(target, distanceX = -this.width * .5f, distanceY = 0f)
}
fun ViewGroup.performSwipeToRight(target: View) {
    this.performSwipe(target, distanceX = +this.width * .5f, distanceY = 0f)
}
fun ViewGroup.performSwipeToTop(target: View) {
    this.performSwipe(target, distanceX = 0f, distanceY = -this.height * .5f)
}
fun ViewGroup.performSwipeToBottom(target: View) {
    this.performSwipe(target, distanceX = 0f, distanceY = +this.width * .5f)
}

fun ViewGroup.performSwipe(target: View, distanceX: Float, distanceY: Float) {
    val parentCoords = intArrayOf(0, 0)
    this.getLocationInWindow(parentCoords)

    val childCoords = intArrayOf(0, 0)
    target.getLocationInWindow(childCoords)

    val initGlobalX = childCoords[0].toFloat() + 1f
    val initGlobalY = childCoords[1].toFloat() + 1f

    val initLocalX = (childCoords[0] - parentCoords[0]).toFloat() + 1f
    val initLocalY = (childCoords[1] - parentCoords[1]).toFloat() + 1f

    val downTime = SystemClock.uptimeMillis()
    var eventTime = SystemClock.uptimeMillis()

    this.dispatchTouchEvent(
        MotionEvent.obtain(
            downTime,
            eventTime,
            MotionEvent.ACTION_DOWN,
            initGlobalX,
            initGlobalY,
            0
        ).apply {
            setLocation(initLocalX, initLocalY)
            source = InputDevice.SOURCE_TOUCHSCREEN
        }
    )

    val steps = 20
    var i = 0
    while (i in 0..steps) {
        val globalX = initGlobalX + i * distanceX / steps
        val globalY = initGlobalY + i * distanceY / steps
        val localX = initLocalX + i * distanceX / steps
        val localY = initLocalY + i * distanceY / steps
        if (globalX <= 10f || globalY <= 10f) {
            break
        }
        this.dispatchTouchEvent(
            MotionEvent.obtain(
                downTime,
                ++eventTime,
                MotionEvent.ACTION_MOVE,
                globalX,
                globalY,
                0
            ).apply {
                setLocation(localX, localY)
                source = InputDevice.SOURCE_TOUCHSCREEN
            }
        )
        i++
    }

    this.dispatchTouchEvent(
        MotionEvent.obtain(
            downTime,
            ++eventTime,
            MotionEvent.ACTION_UP,
            initGlobalX + i * distanceX,
            initGlobalY + i * distanceY,
            0
        ).apply {
            setLocation(initLocalX + i * distanceX, initLocalY + i * distanceY)
            source = InputDevice.SOURCE_TOUCHSCREEN
        }
    )
}

要使用它,您需要将textView作为参数传递,并且textView的父级应该是该函数的接收者:

val textView = ...
(textView.parent as ViewGroup).performSwipeToTop(textView)

答案 2 :(得分:0)

对于 RecyclerView,您还想看看 smoothScollBy 函数,您可以在其中传递一个 Interpolator,如下所示。

recyclerView.smoothScrollBy(0, 500, AccelerateDecelerateInterpolator())

足够接近“滑动scoll”