【问题标题】:Click on EditText's drawable right with Espresso用 Espresso 点击 EditText 的可绘制权限
【发布时间】:2015-10-27 12:36:38
【问题描述】:

怎么可能点击 EditText 的右侧可绘制对象(查看屏幕截图)?我尝试了几种方法,但总是卡住。

public static Matcher<View> withEditTextDrawable(final int resourceId) {
    return new BoundedMatcher<View, EditText>(EditText.class) {
        @Override
        protected boolean matchesSafely(final EditText editText) {
            // ????

            return false;
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("with drawable from resource id: ");
            description.appendValue(resourceId);
        }
    };
}

【问题讨论】:

标签: android android-edittext android-drawable android-espresso


【解决方案1】:

必须开发这个自定义动作和匹配器来测试我们在某些文本字段右侧的可丢弃可绘制对象, 这将在 Espresso 测试中单击 TextView 的任何可绘制对象(左、上、右、下)。

用法:

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

方法:

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));
                    }
            }
        }
    };
}



这是我为选择要点击的特定可绘制对象所做的改进:

用法:

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

方法:

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{}
}

【讨论】:

  • 将此标记为答案就像我的自定义 CallToActionEdit 文本字段的魅力一样!
【解决方案2】:

我使用 Material Component TextInputLayout and TextInputEditText

解决了这个问题

在你的 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>

这里的技巧是设置app:endIconMode="custom",然后根据需要添加你的drawable。

在java类中:

您可以使用setEndIconOnClickListener 并按照以下方式做您想做的事情:

tilLocation.setEndIconOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //do click
        }
    });

【讨论】:

  • 简单而精湛的答案,无需编写太多代码
【解决方案3】:

适用于截至 2021 年 1 月 7 日(欧洲格式)目前遇到此问题的人。 我遇到了同样的问题并这样解决了:

在你的 xml 中,也许你有这样的东西:

<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>

正如您看到上面的 xml 代码,我添加了app:endIconContentDescription="clear text"。 从 xml 中获取 app:endIconContentDescription="clear text" 并在您的 Instrumented 测试方法中添加 onView(withContentDescription("clear text")).perform(click());,在您的测试类中,例如:

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

忘了说这个onView(withId(R.id.note_title)).perform(typeText("Type something."));是你必须先在edittext中写一些东西的那一行,这样结束图标(eddittext的右边drawable)才会出现然后点击它。

【讨论】:

    【解决方案4】:

    @dareniot 的完美回答。

    我刚刚在 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 "
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-07-24
      • 1970-01-01
      • 2015-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-29
      相关资源
      最近更新 更多