什么是触摸模式,为什么它对 *ActivityTestRule* class 很重要?

What is touch mode and why is it important for the *ActivityTestRule* class?

android.support.test.rule.ActivityTestRule class(参见 here)在其构造函数中接受一个 initialTouchMode 参数。这在 class 参考(或任何在线的地方)中没有解释,除了如下:

initialTouchMode - true if the Activity should be placed into "touch mode" when started

"touch mode"到底是什么意思?将 ActivityTestRule 中的 initialTouchMode 设置为 truefalse 的含义是什么? (我看到这个参数的默认值是false)。

触摸模式会影响视图焦点和选择的工作方式。

The touch mode is a state of the view hierarchy that depends solely on the user interaction with the phone. By itself, the touch mode is something very easy to understand as it simply indicates whether the last user interaction was performed with the touch screen.

...

In touch mode, there is no focus and no selection.

http://android-developers.blogspot.com/2008/12/touch-mode.html

我认为一篇中等文章的以下解释非常适合理解 ActivityTestRule 中的 touch mode

'In touch mode, there is no focus and no selection.'

In other words, when your finger touches the screen it will not produce side effects. E.g., views will not keep the focus. It will not make sense until you recall the behavior of Android OS on non-touchable platforms. The best modern example that does not operate in ‘touch mode’ is Android TV. With D-Pad control, we are capable of selecting or focusing a view, and as soon as the view is focused, we can perform the click.

Be careful with RecyclerView and touch mode! Assume we want to perform click action on the view inside a RecyclerView.

onView(withId(R.id.recyclerView))
  .perform(RecyclerViewActions.actionOnItem(
            hasDescendant(withId(R.id.someAction)), click()))

The code is straightforward unless you will make a mistake and launch Activity under test with a disabled touch mode.

val initialTouchMode = false
val launchActivity = true
@JvmField @Rule var activityRule = ActivityTestRule(
    MainActivity::class.java, initialTouchMode, launchActivity
)

What you will end up with is that your underlying click listener, will not be fired and you need to hack and repeat the click!

onView(withId(R.id.recyclerView))
  .perform(RecyclerViewActions.actionOnItem(
            hasDescendant(withId(R.id.someAction)), click()))
  .perform(RecyclerViewActions.actionOnItem(
            hasDescendant(withId(R.id.someAction)), click()))

The answer to this mystery is the fact that RecyclerView inflated via XML will have setFocusableInTouchMode(true) during a construction phase. Our whole page is launched in non-touch mode and interprets the most first click as a focus event and all other clicks as you would expect in touch mode. The fix is as simple as launching activity with enabled touch mode.

val initialTouchMode = true
val launchActivity = true
@JvmField @Rule var activityRule = ActivityTestRule(
    MainActivity::class.java, initialTouchMode, launchActivity
)

可以在这个link中找到解释:https://medium.com/@tom.koptel/espresso-initialtouchmode-can-shoot-you-in-the-leg-85c5f922754