Click by bounds / coordinates

11,747

Solution 1

Espresso has the GeneralClickAction, this is the underlying implementation of ViewActions click(), doubleClick(), and longClick().

The GeneralClickAction's constructor takes a CoordinatesProvider as second argument. So the basic idea is to create a static ViewAction getter which provides a custom CoordinatesProvider. Something like this:

public static ViewAction clickXY(final int x, final int y){
    return new GeneralClickAction(
        Tap.SINGLE,
        new CoordinatesProvider() {
            @Override
            public float[] calculateCoordinates(View view) {

               final int[] screenPos = new int[2];
               view.getLocationOnScreen(screenPos);

               final float screenX = screenPos[0] + x;
               final float screenY = screenPos[1] + y;
               float[] coordinates = {screenX, screenY};

               return coordinates;
            }
        },
        Press.FINGER);
}

A general advice with Espresso: instead of looking for documentation (there's virtually none), look at the source code. Espresso is open source and the source code itself is of really good quality.

Solution 2

The valid answer helped me although that method is deprecated. Now you have to specify the inputDevice (for example InputDevice.SOURCE_MOUSE) and buttonState (for example MotionEvent.BUTTON_PRIMARY

Example in Kotlin:

companion object {
    fun clickIn(x: Int, y: Int): ViewAction {
        return GeneralClickAction(
                Tap.SINGLE,
                CoordinatesProvider { view ->
                    val screenPos = IntArray(2)
                    view?.getLocationOnScreen(screenPos)

                    val screenX = (screenPos[0] + x).toFloat()
                    val screenY = (screenPos[1] + y).toFloat()

                    floatArrayOf(screenX, screenY)
                },
                Press.FINGER,
                InputDevice.SOURCE_MOUSE,
                MotionEvent.BUTTON_PRIMARY)
    }
}

Solution 3

@haffax's answer is great and works well.

However, if you want to click in a certain part of a View that may change from screen to screen, it might be useful to click based on percents (or ratios) as even the dp numbers may not be stable across all screens. So, I made an easy modification to it:

    public static ViewAction clickPercent(final float pctX, final float pctY){
    return new GeneralClickAction(
            Tap.SINGLE,
            new CoordinatesProvider() {
                @Override
                public float[] calculateCoordinates(View view) {

                    final int[] screenPos = new int[2];
                    view.getLocationOnScreen(screenPos);
                    int w = view.getWidth();
                    int h = view.getHeight();

                    float x = w * pctX;
                    float y = h * pctY;

                    final float screenX = screenPos[0] + x;
                    final float screenY = screenPos[1] + y;
                    float[] coordinates = {screenX, screenY};

                    return coordinates;
                }
            },
            Press.FINGER);
}

I thought I would share it here so others can benefit.

Share:
11,747
Chad Bingham
Author by

Chad Bingham

Arguing with a software engineer is like wrestling with a pig in the mud; after a while you realize the pig likes it. -Unknown

Updated on June 27, 2022

Comments

  • Chad Bingham
    Chad Bingham about 2 years

    I know it is possible for Espresso to click by bounds the way UiAutomator does. (x and y coordinates) I have read through the documentation but I can't seem to find it. Any help is appreciated. Thanks

    Edit
    I found this link, but no examples how to use it, My main concern with this is the UiController is or how to use it.

  • Chad Bingham
    Chad Bingham over 10 years
    Thanks for your answer! When I get a chance to test it out, I will accept it.
  • Chad Bingham
    Chad Bingham about 10 years
    Sorry for the late request for help, but this is not clicking. I am not getting any errors, but it is also not clicking anywhere. Is there anything I don't understand about it?
  • haffax
    haffax about 10 years
    One thing to remember is, that x, y are coordinates relative to view left/top, not screen left/top. But else I can't think of more right now. We use this kind of custom CoordinatesProviders a lot and so far didn't run into any issues. You can also try to log screenX/screenY to the logcat and see if the values make any sense.
  • Chad Bingham
    Chad Bingham about 10 years
    So what view is it referring to?
  • haffax
    haffax about 10 years
    The view you are performing it on. onView(..).perform(clickXY(10, 20)); The view that is matched by the matcher given to onView.
  • Chad Bingham
    Chad Bingham about 10 years
    Ah. Gotcha. Thats what I missunderstood
  • kiruwka
    kiruwka about 9 years
    @haffax I'd like to use screen absolute coordinates (say, 0,0 of screen). What should I put to onView(...) ? I tried `onView(isRoot()). and it failed with no match. Note : I am currently showing dialog in front
  • sat
    sat about 8 years
    Thanks. If anyone uses this code, value for pctX and pctY, ranges from 0 to 1.