应用程序暂停后,可滚动的 TextView 不允许 select 文本

Scrollable TextView doesn't allow to select text after the application is paused

我有一个可滚动的 TextView,用户可以在其中 select 文本。我通过将移动方法设置为 ScrollingMovementMethod.

来添加滚动条

问题:除非应用程序暂停(例如,切换应用程序后),否则选择工作正常。应用程序再次激活后 selection 停止工作,我在日志中收到以下消息:

W/TextView: TextView does not support text selection. Selection cancelled.

我的设置:

我有一个 Activity 和 CoordinatorLayout 以及一个 TextView 包裹在 RelativeLayout 中的片段,看起来像这样:

<TextView
    android:id="@+id/text_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentStart="true"
    android:scrollbars="vertical" />

在 Java 代码中我必须做:

textView.setMovementMethod(new ScrollingMovementMethod());
textView.setTextIsSelectable(true);
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

因为根据 this, this and this 问题,这是唯一的工作方式。

编辑:

问题出在下面的调用中

textView.setMovementMethod(new ScrollingMovementMethod());

如果我删除它,它会起作用,但我不明白为什么。

重现问题的最少步骤:

1) 使用以下布局创建一个带有 TextView 的空 Activity。

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_view"
        android:text="Some very very very long text..."
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:scrollbars="vertical" />

</android.support.design.widget.CoordinatorLayout>

2) 在onStart()方法中设置TextView的可见性参数。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        TextView textView = findViewById(R.id.text_view);
        textView.setMovementMethod(new ScrollingMovementMethod());
        textView.setTextIsSelectable(true);
        textView.setFocusable(true);
        textView.setFocusableInTouchMode(true);
    }
}

3) 尝试在暂停应用程序前后使用 TextView 上的上下文菜单。

编辑 2:

删除 setMovementMethod(new ScrollingMovementMethod()) 解决了我的问题,此后功能运行良好。但是我不太确定为什么要添加它,而且我担心如果我将其删除它会刹车。知道为什么可以将 ScrollingMovementMethodandroid:scrollbars="vertical" 结合使用。可能 xml 在某些情况下不起作用?想法?我仍然对为什么使用 ScrollingMovementMethod 刹车 selection 功能感兴趣?

请替换您 XML 中的以下代码。

<TextView
    android:id="@+id/text_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentStart="true"
    android:enabled="true"
    android:textIsSelectable="true"
    android:focusable="true"
    android:longClickable="true" 
    android:scrollbars="vertical" />

从程序中删除以下代码:

textView.setTextIsSelectable(true);
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

setTextIsSelectable()方法的内部代码:

public void setTextIsSelectable(boolean selectable) {
        if (!selectable && mEditor == null) return; // false is default value with no edit data

        createEditorIfNeeded();
        if (mEditor.mTextIsSelectable == selectable) return;

        mEditor.mTextIsSelectable = selectable;
        setFocusableInTouchMode(selectable);
        setFocusable(FOCUSABLE_AUTO);
        setClickable(selectable);
        setLongClickable(selectable);

        // mInputType should already be EditorInfo.TYPE_NULL and mInput should be null

        setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null);
        setText(mText, selectable ? BufferType.SPANNABLE : BufferType.NORMAL);

        // Called by setText above, but safer in case of future code changes
        mEditor.prepareCursorControllers();
    }

在程序上,他们也在做我在 XML 中提到的同样的事情。因此,根据您的要求,我们可以使用它。

您可以通过两种方式使 TextView 可选择

至XML:

 android:textIsSelectable="true"

通过Java代码:

 textView.setTextIsSelectable(true);

设置 ScrollingMovementMethod 使 TextView 能够滚动 "by own",例如当您设置非常长的文本并且它在底部或边缘被切割时。使用 ScrollingMovementMethod 你可以滚动 TextView,不需要将它放在可滚动的容器中,例如在 ScrollViewHorizontalScrollView

android:scrollbars="vertical" 行表示,如果此 View 得到 "scrollablility"(例如通过上述移动方法),则 UI 应该仅显示 vertical 滚动条。来自文档:

Defines which scrollbars should be displayed on scrolling or not.

并且它是 View 文档,而不是 TextView 特别是,因为很少有扩展 "kinds" of Views 可以获得 "scolllability",包括所有 ViewGroupScrollViewListViewRecyclerView

最后,这一行在您的代码中做了什么?在 setTextIsSelectable 里面你有这一行:

setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null);

所以实际上它的覆盖移动方法是您在自己的代码中设置上面的几行。我敢打赌,不久前你的 TextView 是 scollable "by itself" 并且有一天一些聪明的人重写了它并将 TextView 放在例如ScrollView在XML,移动方法留在代码中。

textIsSelectable 一直工作到 Activity 暂停,因为在恢复后你(再次)设置了 ScrollableMovementMethod,但在 setTextIsSelectable 里面你有

 if (mEditor.mTextIsSelectable == selectable) return;

你在 Activity 暂停前的第一个 运行 设置了 mTextIsSelectable 标志,并且这个标志被恢复,所以下面的代码没有被触发(所以移动方法不是使用 ArrowKeyMovementMethod 重新设置,您的 ScrollableMovementMethod 保持不变)。因此,请回答此行在您的代码中的作用:它在暂停和恢复 Activity 后中断 "selectionability",除此之外什么都没有

来自 ScrollingMovementMethod and ArrowKeyMovementMethod 来源的注释:仅在 ArrowKeyMovementMethod 中(像上面那样在 setTextIsSelectable 中设置为移动方法)你已经覆盖了 onTouchEvent 方法并在其中一些行处理选择

编辑:另请注意,在 setTextIsSelectable 中您有设置 "focusability",因此这些行是不必要的:

textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

因此您可以将代码缩短为一行:

textView.setTextIsSelectable(true);

或删除所有引用的 Java 代码并添加一个 XML 行:

android:textIsSelectable="true"

我遇到了非常相似的问题; texview 可以滚动但不能选择。

从您的代码中删除:

texview.setMovementMethod(new ScrollingMovementMethod())

我将这些添加到 xml 中的文本视图中:

android:scrollHorizontally="true" android:textIsSelectable="true"