Click on EditText's drawable right with Espresso

前端 未结 4 635
小鲜肉
小鲜肉 2021-02-10 00:54

How could be possible to click on EditText\'s right drawable (check the screenshot)? I have tried several ways but I always get stuck.

public static Matcher

        
相关标签:
4条回答
  • 2021-02-10 01:30

    For people that have this problem right now as of 1/7/2021 (European format). I had the same problem and solved it like this:

    In your xml, maybe you have something like this:

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/edittext_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/edittext_hint"
        app:endIconMode="clear_text"
        app:endIconCheckable="true"
        app:endIconTint="@color/colorSemiTranspBlack"
        app:endIconContentDescription="clear text">
    
        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/eddittext"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </com.google.android.material.textfield.TextInputLayout>
    

    As you saw the above xml code, i added app:endIconContentDescription="clear text". grab from the xml, app:endIconContentDescription="clear text" and add onView(withContentDescription("clear text")).perform(click()); in your Instrumented test method, in your test class, like:

        @Test
        public void testEdittext(){
        onView(withId(R.id.eddittext)).perform(typeText("Type something."));
        onView(withContentDescription("clear text")).perform(click());
        }
    

    Forgot to say that this onView(withId(R.id.note_title)).perform(typeText("Type something.")); is the line that you must first write something in the edittext, so that the end icon(eddittext's right drawable) will show up and then click it.

    0 讨论(0)
  • 2021-02-10 01:38

    Had to develop this custom action and matcher to test our discard drawables on the right of some text fields, this will click any drawable (left, top, right, bottom) of a TextView in an Espresso test.

    Usage:

    onView(withId(id)).perform(clickDrawables());
    

    Method:

    public static ViewAction clickDrawables()
    {
        return new ViewAction()
        {
            @Override
            public Matcher<View> getConstraints()//must be a textview with drawables to do perform
            {
                return allOf(isAssignableFrom(TextView.class), new BoundedMatcher<View, TextView>(TextView.class)
                {
                    @Override
                    protected boolean matchesSafely(final TextView tv)
                    {
                        if(tv.requestFocusFromTouch())//get fpocus so drawables become visible
                            for(Drawable d : tv.getCompoundDrawables())//if the textview has drawables then return a match
                                if(d != null)
                                    return true;
    
                        return false;
                    }
    
                    @Override
                    public void describeTo(Description description)
                    {
                        description.appendText("has drawable");
                    }
                });
            }
    
            @Override
            public String getDescription()
            {
                return "click drawables";
            }
    
            @Override
            public void perform(final UiController uiController, final View view)
            {
                TextView tv = (TextView)view;
                if(tv != null && tv.requestFocusFromTouch())//get focus so drawables are visible
                {
                    Drawable[] drawables = tv.getCompoundDrawables();
    
                    Rect tvLocation = new Rect();
                    tv.getHitRect(tvLocation);
    
                    Point[] tvBounds = new Point[4];//find textview bound locations
                    tvBounds[0] = new Point(tvLocation.left, tvLocation.centerY());
                    tvBounds[1] = new Point(tvLocation.centerX(), tvLocation.top);
                    tvBounds[2] = new Point(tvLocation.right, tvLocation.centerY());
                    tvBounds[3] = new Point(tvLocation.centerX(), tvLocation.bottom);
    
                    for(int location = 0; location < 4; location++)
                        if(drawables[location] != null)
                        {
                            Rect bounds = drawables[location].getBounds();
                            tvBounds[location].offset(bounds.width() / 2, bounds.height() / 2);//get drawable click location for left, top, right, bottom
                            if(tv.dispatchTouchEvent(MotionEvent.obtain(android.os.SystemClock.uptimeMillis(), android.os.SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, tvBounds[location].x, tvBounds[location].y, 0)))
                                tv.dispatchTouchEvent(MotionEvent.obtain(android.os.SystemClock.uptimeMillis(), android.os.SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, tvBounds[location].x, tvBounds[location].y, 0));
                        }
                }
            }
        };
    }
    



    This is a refinement I made to select a specific drawable to click:

    Usage:

    onView(withId(id)).perform(new ClickDrawableAction(ClickDrawableAction.Right));
    

    Method:

    public static class ClickDrawableAction implements ViewAction
    {
        public static final int Left = 0;
        public static final int Top = 1;
        public static final int Right = 2;
        public static final int Bottom = 3;
    
        @Location
        private final int drawableLocation;
    
        public ClickDrawableAction(@Location int drawableLocation)
        {
            this.drawableLocation = drawableLocation;
        }
    
        @Override
        public Matcher<View> getConstraints()
        {
            return allOf(isAssignableFrom(TextView.class), new BoundedMatcher<View, TextView>(TextView.class)
            {
                @Override
                protected boolean matchesSafely(final TextView tv)
                {
                    //get focus so drawables are visible and if the textview has a drawable in the position then return a match
                    return tv.requestFocusFromTouch() && tv.getCompoundDrawables()[drawableLocation] != null;
    
                }
    
                @Override
                public void describeTo(Description description)
                {
                    description.appendText("has drawable");
                }
            });
        }
    
        @Override
        public String getDescription()
        {
            return "click drawable ";
        }
    
        @Override
        public void perform(final UiController uiController, final View view)
        {
            TextView tv = (TextView)view;//we matched
            if(tv != null && tv.requestFocusFromTouch())//get focus so drawables are visible
            {
                //get the bounds of the drawable image
                Rect drawableBounds = tv.getCompoundDrawables()[drawableLocation].getBounds();
    
                //calculate the drawable click location for left, top, right, bottom
                final Point[] clickPoint = new Point[4];
                clickPoint[Left] = new Point(tv.getLeft() + (drawableBounds.width() / 2), (int)(tv.getPivotY() + (drawableBounds.height() / 2)));
                clickPoint[Top] = new Point((int)(tv.getPivotX() + (drawableBounds.width() / 2)), tv.getTop() + (drawableBounds.height() / 2));
                clickPoint[Right] = new Point(tv.getRight() + (drawableBounds.width() / 2), (int)(tv.getPivotY() + (drawableBounds.height() / 2)));
                clickPoint[Bottom] = new Point((int)(tv.getPivotX() + (drawableBounds.width() / 2)), tv.getBottom() + (drawableBounds.height() / 2));
    
                if(tv.dispatchTouchEvent(MotionEvent.obtain(android.os.SystemClock.uptimeMillis(), android.os.SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, clickPoint[drawableLocation].x, clickPoint[drawableLocation].y, 0)))
                    tv.dispatchTouchEvent(MotionEvent.obtain(android.os.SystemClock.uptimeMillis(), android.os.SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, clickPoint[drawableLocation].x, clickPoint[drawableLocation].y, 0));
            }
        }
    
        @IntDef({ Left, Top, Right, Bottom })
        @Retention(RetentionPolicy.SOURCE)
        public @interface Location{}
    }
    
    0 讨论(0)
  • 2021-02-10 01:41

    I solved it using the Material Component TextInputLayout and TextInputEditText

    In your xml:

     <com.google.android.material.textfield.TextInputLayout
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
                android:id="@+id/til_location"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:endIconMode="custom"
                app:endIconDrawable="@drawable/ic_edit_location"
                app:endIconTint="@color/colorAccent"
                app:hintEnabled="false"
                app:endIconContentDescription="open map icon">
    
                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/et_address"
                    android:textColor="@color/colorAccent"
                    android:layout_height="wrap_content"
                    android:layout_width="match_parent"
                    tools:text="3 streenasdasdasd"/>
    
            </com.google.android.material.textfield.TextInputLayout>
    

    The trick here is to set the app:endIconMode="custom" then add your drawable as u want.

    In java class:

    you can use the setEndIconOnClickListener and do what you want as below:

    tilLocation.setEndIconOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //do click
            }
        });
    
    0 讨论(0)
  • 2021-02-10 01:41

    Perfect answer from @dareniott .

    I just made it in kotlin:

    import android.graphics.Point
    import android.support.annotation.IntDef
    import android.support.test.espresso.UiController
    import android.support.test.espresso.ViewAction
    import android.support.test.espresso.matcher.BoundedMatcher
    import android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom
    import android.view.MotionEvent
    import android.view.View
    import android.widget.TextView
    import org.hamcrest.Description
    import org.hamcrest.Matcher
    import org.hamcrest.Matchers.allOf
    
    class ClickDrawableAction(@param:Location @field:Location private val drawableLocation: Int) : ViewAction {
        override fun getConstraints(): Matcher<View> {
            return allOf(isAssignableFrom(TextView::class.java), object : BoundedMatcher<View, TextView>(TextView::class.java) {
                override fun matchesSafely(tv: TextView): Boolean {
                    return tv.requestFocusFromTouch() && tv.compoundDrawables[drawableLocation] != null
                }
    
                override fun describeTo(description: Description) {
                    description.appendText(DESCRIPTION_HAS_DRAWABLE)
                }
            })
        }
    
        override fun getDescription(): String {
            return DESCRIPTION_CLICK_DRAWABLE
        }
    
        override fun perform(uiController: UiController, view: View) {
            val tv = view as TextView
            if (tv.requestFocusFromTouch())
            {
                val drawableBounds = tv.compoundDrawables[drawableLocation].bounds
                val clickPoint = arrayOfNulls<Point>(SIZE_CLICK_POINT)
                clickPoint[LEFT] = Point(tv.left + drawableBounds.width() / HALF_DIVISOR, (tv.pivotY + drawableBounds.height() / HALF_DIVISOR).toInt())
                clickPoint[TOP] = Point((tv.pivotX + drawableBounds.width() / HALF_DIVISOR).toInt(), tv.top + drawableBounds.height() / HALF_DIVISOR)
                clickPoint[RIGHT] = Point(tv.right + drawableBounds.width() / HALF_DIVISOR, (tv.pivotY + drawableBounds.height() / HALF_DIVISOR).toInt())
                clickPoint[BOTTOM] = Point((tv.pivotX + drawableBounds.width() / HALF_DIVISOR).toInt(), tv.bottom + drawableBounds.height() / HALF_DIVISOR)
                clickPoint[drawableLocation]?.let { point ->
                    if (tv.dispatchTouchEvent(
                                    MotionEvent.obtain(
                                            android.os.SystemClock.uptimeMillis(),
                                            android.os.SystemClock.uptimeMillis(),
                                            MotionEvent.ACTION_DOWN,
                                            point.x.toFloat(),
                                            point.y.toFloat(),
                                            0)
                            )) {
                        tv.dispatchTouchEvent(
                                MotionEvent.obtain(
                                        android.os.SystemClock.uptimeMillis(),
                                        android.os.SystemClock.uptimeMillis(),
                                        MotionEvent.ACTION_UP,
                                        point.x.toFloat(),
                                        point.y.toFloat(),
                                        0))
                    }
                }
    
            }
        }
    
        @IntDef(LEFT, TOP, RIGHT, BOTTOM)
        @Retention(AnnotationRetention.SOURCE)
        annotation class Location
    
        companion object {
            const val LEFT = 0
            const val TOP = 1
            const val RIGHT = 2
            const val BOTTOM = 3
            const val SIZE_CLICK_POINT = 4
            const val HALF_DIVISOR = 2
            const val DESCRIPTION_HAS_DRAWABLE = "has drawable"
            const val DESCRIPTION_CLICK_DRAWABLE = "click drawable "
        }
    }
    
    0 讨论(0)
提交回复
热议问题