Android Espresso, How can I perform onLongClick an

2019-05-04 18:37发布

I mean, I can perform the two actions sequentially but in the middle there is an ACTION_UP MotionEvent where I removed the OnTouchListener I'm using on the view

In my case I need to avoid trigger that event.

I have a sport field with players, one of the Use Cases is move a player after a longClick, the feature works well but I can’t reproduce the feature in the tests.

No TouchListener until onLongClick

Here is the code of the class I want to test, I tried to condensed all the relevant code associated to the question.

public class FootballFieldLayout extends RelativeLayout implements View.OnTouchListener {
    [...]
    public void addPlayer(FieldPlayer player) {
        final FieldPlayerView fpv = new FieldPlayerView(getContext(), player);
        addView(fpv);
        fpv.setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                v.setOnTouchListener(FootballFieldLayout.this);
                    return true;
                }
            });
    }
    [...]
    public float dX=NO_DELTA, dY=NO_DELTA;

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getAction()) {

            case MotionEvent.ACTION_MOVE:
                if(dX == NO_DELTA || dY == NO_DELTA){ setDelta(view, event); }
                view.animate()
                    .x(event.getRawX() + dX)
                    .y(event.getRawY() + dY)
                    .setDuration(0)
                    .start();
                    break;
            case MotionEvent.ACTION_UP:
                view.setOnTouchListener(null);
                resetDelta();
                break;
            default:
                return false;
        }
        return true;
    }
}

Here is the test

public class MoveOnLongClickActivityTest {
    [...]
    @Test
    public void playerCanBeMovedVerticallyAfterLongClick() throws InterruptedException {
        onView(withId(R.id.activity_field)).check(matches(isDisplayed()));
        View view = mActivityRule.getActivity().findViewById(R.id.test_player);

        float[] beginCoord = {view.getX(), view.getY()};
        onView(withId(R.id.test_player)).perform(longClick());
        onView(withId(R.id.test_player)).perform(swipeDown());
        float[] endCoord = {view.getX(), view.getY()};

        assertThat(beginCoord[1], not(equalTo(endCoord[1])));
    }
}

I always use the ViewActions of the framework or use a recipe from another person changing some things but I never tried to build a complex ViewAction and I'm a bit lost.

1条回答
Luminary・发光体
2楼-- · 2019-05-04 19:04

I have seen your comments under my question. Indeed I have faced the same problem at the moment (with INJECT_EVENTS permission and all my workarounds couldn't handle it). I used that code over half year ago and it worked for me without any problems.

I can propose other solution for you but I am not 100% sure it will work. Because I have created it like super fast now. I might have more time to look at it later.

Yet try this solution:

  1. Try to write your own GeneralSwipeAction. Look at constructor:

    public GeneralSwipeAction(Swiper swiper, CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider, PrecisionDescriber precisionDescriber) {
        this.swiper = swiper;
        this.startCoordinatesProvider = startCoordinatesProvider;
        this.endCoordinatesProvider = endCoordinatesProvider;
        this.precisionDescriber = precisionDescriber;
    }
    

So position of x,y of start/end point are sent via CoordinatesProvider class. And it is an interface that returns array of floats. First float in array represent x and second one represent y.

  1. Create your own class implementing Coordinates provider:

    import android.support.test.espresso.action.CoordinatesProvider;
    import android.view.View;
    
    public class CustomisableCoordinatesProvider implements CoordinatesProvider {
    
        private int x;
        private int y;
    
        public CustomisableCoordinatesProvider(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        @Override
        public float[] calculateCoordinates(View view) {
            return new float[]{x,y};
        }
    }
    
  2. Implement your custom ViewAction that performs drag with usage of this CustomisableCoordinatesProvider:

    public static ViewAction drag(int startX, int startY, int endX, int endY) {
        return new GeneralSwipeAction(
            Swipe.FAST,
            new CustomisableCoordinatesProvider(startX, startY),
            new CustomisableCoordinatesProvider(endX, endY),
            Press.FINGER);
    }
    
  3. And try to use it like this:

    onView(withId(R.id.test_player)).perform(drag(0, 100, 0, 100));
    

I haven't tested it. But it might work or help you by pointing to right direction. Try it and tell me if it works, I am curious myself :)

查看更多
登录 后发表回答