在使用Espresso进行UI测试时,通常使用ViewActions.click()方法来点击控件。如果要测试TextView中的url链接,还可以用另外一个方法ViewAction.openLinkWithText(),但是如果这些链接是自定义的ClickSpan,或者需要点击非链接的文字区域,使用以上的方法无法做到。
我们先来看看系统原生apiViewActions.click()的实现。
public static ViewAction click() {
return actionWithAssertions(
new GeneralClickAction(
Tap.SINGLE,
GeneralLocation.VISIBLE_CENTER,
Press.FINGER,
InputDevice.SOURCE_UNKNOWN,
MotionEvent.BUTTON_PRIMARY));
}
查看上面的源码可以发现GeneralClickAction构造函数传的是GeneralLocation.VISIBLE_CENTER,click()方法默认点击的是控件的中心位置。此外GeneralLocation枚举中还提供了其它值用来点击控件的不同位置。GeneralLocation这其实是CoordinatesProvider的实现类。GeneralLocation.VISIBLE_CENTER实现了CoordinatesProvider接口的calculateCoordinates(View view)方法,最终是返回一个坐标数组,点击事件最后就是针对这个位置模拟发送点击事件。因此我们就可以通过实现自己的CoordinatesProvider来返回任意想要点击的坐标。
下面我们要实现一个点击TextView中指定文字的CoordinatesProvider。
class TextCoordinatesProvider constructor(val text: String): CoordinatesProvider {
companion object {
const val TAG = "TextCoordinatesProvider"
}
override fun calculateCoordinates(view: View?): FloatArray {
//需要判断控件类型
if (view is TextView) {
// 指定文字开始的索引位置
val offset = view.text.indexOf(text)
Log.v(TAG, "Full text = ${view.text}, content = ${text}")
Log.v(TAG, "Offset = ${offset}")
view.layout.let {
// 获取TextView在屏幕上的坐标(控件左上角为原点)
val xy = IntArray(2)
view.getLocationOnScreen(xy)
val lineOfText = it.getLineForOffset(offset)
// 指定文字起始处的x坐标
val xCoord = xy[0] + it.getPrimaryHorizontal(offset) + Press.FINGER.describePrecision()[0]
// 指定文字起始处所在行的y坐标
val yCoord = xy[1] + it.getLineTop(lineOfText) + (it.getLineBottom(lineOfText) - it.getLineTop(lineOfText)) / 2
Log.d(TAG, "LineOfText = ${lineOfText}, xCoord = ${xCoord}, yCoord = ${yCoord}")
return floatArrayOf(xCoord, yCoord.toFloat())
}
} else {
throw Exception("View is not TextView.")
}
}
}
使用上面定义的CoordinatesProvider封装一下点击TextView中指定文字的方法:
fun clickText(text: String): ViewAction? {
return ViewActions.actionWithAssertions(
GeneralClickAction(
Tap.SINGLE,
TextCoordinatesProvider(text),
Press.FINGER,
InputDevice.SOURCE_UNKNOWN,
MotionEvent.BUTTON_PRIMARY))
}
这是长按的方法:
fun longClickText(text: String): ViewAction? {
return ViewActions.actionWithAssertions(
GeneralClickAction(
Tap.LONG,
TextCoordinatesProvider(text),
Press.FINGER,
InputDevice.SOURCE_UNKNOWN,
MotionEvent.BUTTON_PRIMARY))
}
要注意这里点击的是指定字符串首字符的位置,而且TextView中如果有多个相同的字符串,计算第一个匹配到的字符串的坐标。