You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android列表重排UI测试难题:长按与拖拽动作无法结合实现

Android UI测试:Espresso+Barista实现长按拖拽重排的完整流程卡壳了

我最近在给我的Android应用做UI测试,这个应用里有个列表项重排的功能——用户长按列表项会生成拖拽阴影,拖到目标位置松开就能完成重排。

本来以为这个测试不难,但实际做的时候发现:单独用Barista的longClickOn("ITEM")实现长按生成拖拽阴影,或者单独写自定义Espresso ViewAction做拖拽动作都没问题,但把这两步结合成完整的长按-拖拽-松开流程就搞不定了,试了好多次都失败。

最初尝试的自定义拖拽ViewAction

我一开始写的拖拽动作是这样的:

return new ViewAction() {
    @Override
    public Matcher<View> getConstraints() {
        return isAssignableFrom(ViewGroup.class);
    }
    @Override
    public String getDescription() {
        return "Swiping child " + srcIndex + " to child " + destIndex;
    }
    @Override
    public void perform(UiController uiController, View view) {
        ViewGroup parent = (ViewGroup) view;
        final View srcChild = parent.getChildAt(srcIndex);
        final View destChild = parent.getChildAt(destIndex);
        final CoordinatesProvider srcCoordinatesProvider = new CoordinatesProvider() {
            @Override
            public float[] calculateCoordinates(View view) {
                int[] location = new int[2];
                srcChild.getLocationInWindow(location);
                float x = location[0] + (view.getMeasuredWidth() / 2);
                float y = location[1] + (view.getMeasuredHeight() / 2);
                return new float[] {x, y};
            }
        };
        final CoordinatesProvider destCoordinatesProvider = new CoordinatesProvider() {
            @Override
            public float[] calculateCoordinates(View view) {
                int[] location = new int[2];
                destChild.getLocationInWindow(location);
                float x = location[0] + (view.getMeasuredWidth() / 2);
                float y = location[1] + (view.getMeasuredHeight() / 2);
                return new float[] {x, y};
            }
        };
        GeneralSwipeAction swipe = new GeneralSwipeAction(Swipe.FAST, srcCoordinatesProvider, destCoordinatesProvider, Press.FINGER);
        swipe.perform(uiController, parent);
    }
};

参考改进后的代码

后来参考了@Be_Negative的回答,我重写了拖拽的ViewAction,把长按和拖拽整合到了一起:

private static ViewAction drag(final int srcIndex, final int destIndex) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isAssignableFrom(ViewGroup.class);
        }
        @Override
        public String getDescription() {
            return "Swiping child " + srcIndex + " to child " + destIndex;
        }
        @Override
        public void perform(UiController uiController, View view) {
            ViewGroup parent = (ViewGroup) view;
            uiController.loopMainThreadUntilIdle();
            final View srcChild = parent.getChildAt(srcIndex);
            final View destChild = parent.getChildAt(destIndex);
            final CoordinatesProvider coordinatesProvider = getCoordinatesProdvider();
            float[] precision = Press.PINPOINT.describePrecision();
            MotionEvent downEvent = MotionEvents.sendDown(uiController, coordinatesProvider.calculateCoordinates(srcChild), precision).down;
            try {
                long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5);
                uiController.loopMainThreadForAtLeast(longPressTimeout);
                float[][] steps = interpolateDragging(
                        coordinatesProvider.calculateCoordinates(srcChild),
                        coordinatesProvider.calculateCoordinates(destChild)
                );
                uiController.loopMainThreadUntilIdle();
                for(float[] step : steps) {
                    if( !MotionEvents.sendMovement(uiController, downEvent, step)) {
                        MotionEvents.sendCancel(uiController, downEvent);
                    }
                }
                if(!MotionEvents.sendUp(uiController, downEvent, coordinatesProvider.calculateCoordinates(destChild))) {
                    MotionEvents.sendCancel(uiController, downEvent);
                }
            } catch(Exception e) {
                System.out.println(e);
            } finally {
                downEvent.recycle();
            }
        }
    };
}

(注:这里的getCoordinatesProdvider()和interpolateDragging()是我自己实现的辅助方法,用来获取视图中心坐标和生成拖拽的路径步骤)

现在遇到的错误

结果运行测试的时候,在MotionEvents.sendMovement这一步抛出了错误:

Error performing 'inject motion event (corresponding down event: MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=540.0, y[0]=302.0, toolType[0]=TOOL_TYPE_UNKNOWN, buttonState=BUTTON_PRIMARY, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=34586525, downTime=34586525, deviceId=0, source=0x1002 })' on view 'unknown'.

有没有大佬能帮我看看这问题出在哪,怎么才能顺利实现完整的长按拖拽重排的UI测试流程?

内容的提问来源于stack exchange,提问作者Hopeful Llama

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:51:03