暂停应用后,可滚动的文本视图不允许选择文本

10

我有一个可滚动的 TextView,用户可以选择文本。我通过设置Movement Method为ScrollingMovementMethod来添加滚动条。

问题:选择功能正常工作,除非应用程序被暂停(例如在切换应用程序之后)。一旦应用程序再次处于活动状态,选择将停止工作,并且我会在日志中收到以下消息:

W/TextView: TextView不支持文本选择。选择已取消。

我的设置:

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

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

由于这个这个这个问题的原因,这是唯一有效的方法。

编辑:

问题在以下调用中。

textView.setMovementMethod(new ScrollingMovementMethod());

如果我移除它,它就能正常工作,但我不知道原因。

重现问题的最小步骤:

1) Create an empty Activity with a TextView using the following layout.

<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) Set up visibility parameters of the TextView in onStart() method.

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()) 可以解决我的问题,在那之后,功能可以正常工作。但我不太确定为什么要添加它,如果我删除它,可能会出现问题。有什么想法吗?为什么有人会在 android:scrollbars="vertical" 中使用 ScrollingMovementMethod。也许 XML 在某些情况下无法正常工作?你们有什么想法吗?我还想知道为什么使用 ScrollingMovementMethod 会破坏选择功能?


2
你把那段Java代码放在哪里了?试着将它放在onResume()下面。另外,为什么不尝试使用android:textIsSelectable XML属性呢? - TheWanderer
@TheWanderer 我的代码现在在´onCreate()´中,这是因为如果我将它添加到xml中,它根本不起作用(请检查我添加的链接,这很奇怪,但这是现实)。稍后我会尝试将它放到‘onResume()’中(现在离开我的笔记本电脑有点远)。 - Sasha Shpota
@TheWanderer 在 onResume() 中这样做不幸没有帮助。 - Sasha Shpota
请问您能否添加一个最小化、完整化和可验证的示例吗?我无法重现这样的问题:使用android:textIsSelectable="true"属性时,选择功能正常工作。 - ozbek
@ozbek,请查看带有详细测试用例的编辑。 - Sasha Shpota
将相同的代码块放在onCreate内部 - shb
4个回答

4
请将以下代码替换为您的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中提到的同样的事情。因此,根据您的需求,我们可以使用它。


谢谢。不幸的是,这个方法并没有起作用,我已经尝试过了。正如我所提到的,“我只在Java代码中设置了参数,因为这是唯一可行的方式”(至少部分可行)。使用您添加的代码,我在日志中得到“W / TextView:TextView不支持文本选择。选择已取消。” 我会尝试缩小问题范围并添加更多细节。 - Sasha Shpota
请查看带有一些新发现的编辑内容以及如何重现问题的详细测试案例。 - Sasha Shpota
@OleksandrShpota:请查看更新后的答案。 - Jitesh Mohite
我不确定你想让我尝试什么。正如我所提到的,XML方式不起作用,最终我发现问题在于设置“ScrollingMovementMethod”。你有阅读编辑吗? - Sasha Shpota

3

使用ScrollingMovementMethod可以使TextView实现自己的滚动功能,例如当您设置了非常长的文本并且它被截断在底部或边缘时。使用ScrollingMovementMethod,您可以滚动TextView,无需将其放置在可滚动容器中,例如ScrollViewHorizontalScrollView

android:scrollbars="vertical" 表示如果这个View具有“可滚动性”(例如通过上述移动方法),则UI应该仅显示vertical 滚动条。来自文档:

指定是否显示滚动条。

这是View文档,不是特别针对TextView,因为几种扩展的“类型”的View都可以获得“可滚动性”,包括所有ViewGroup,例如ScrollViewListViewRecyclerView等。

最后,在您的代码中,这行是干什么的?在setTextIsSelectable内部,您有这行:

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

事实上,它覆盖了你自己代码中设置的移动方法。我敢打赌,以前你的TextView可以自己滚动,但某天有个聪明人重写了它,并将TextView放在XML中的ScrollView中,而移动方法仍保留在代码中。
textIsSelectable在Activity暂停之前是有效的,因为在恢复后,您又(再次)设置了ScrollableMovementMethod,但在setTextIsSelectable内部您有…
 if (mEditor.mTextIsSelectable == selectable) return;

在第一次运行之前,您设置了mTextIsSelectable标志,并且此标志已恢复,因此下面的代码不会触发(因此移动方法不会使用ArrowKeyMovementMethod重新设置,您的ScrollableMovementMethod保持不变)。 因此,对于您的代码中这行代码的问题的答案是:在暂停和恢复Activity后打破“可选择性”,除此之外没有其他内容。
来自ScrollingMovementMethodArrowKeyMovementMethod源代码的说明:只有在ArrowKeyMovementMethod中(像上面一样设置为setTextIsSelectable内的移动方法)才会覆盖onTouchEvent方法,并在其中处理某些处理选择的行。
编辑:还要注意,在setTextIsSelectable内部,您设置了“焦点能力”,因此这些行是不必要的:
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

所以你可以将代码缩短为一行:

textView.setTextIsSelectable(true);

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

android:textIsSelectable="true"

非常感谢您提供如此详细的解释。在我的情况下,我没有ScrollView,只有一个RelativeLayout。但是现在我想知道,即使没有textView.setMovementMethod(new ScrollingMovementMethod())调用,它是否仍然有效,为什么例如在这个高评分答案中建议使用 https://dev59.com/oHI-5IYBdhLWcg3wpaF1#3256305?基本上,`android:scrollbars="vertical"`可以在没有任何Java代码的情况下完成工作。 - Sasha Shpota
android:scrollbars="vertical" 属性只在内容(文本)大于 View 时显示垂直滚动条,它不会使 TextView 自身可滚动,例如在触摸/滑动时。创建一些虚拟的 TextView,设置固定宽度(例如100dp)、高度(40dp)和非常长的文本。如果没有 maxLinessingleLineellipsize 和其他“缩短”方法,文本将无法适应并被切断在底部。使用 android:scrollbars="vertical"TextView 将显示垂直滚动条,表示它有更多内容,但是如果没有 ScrollingMovementMethod,它将无法通过触摸滚动。 - snachmsm
在我的情况下,即使没有这个调用,它也可以滚动。 - Sasha Shpota
检查是否可滚动,而不需要 setTextIsSelectable(true); (甚至在末尾强制使用 setMovementMethod(null) )- ArrowKeyMovementMethodonTouch 中也会调用 Touch.onTouchEvent(...),所以它也有可能处理滚动手势。 - snachmsm
1
你是对的,如果我移除android:textIsSelectable="true",滚动将停止工作。 - Sasha Shpota

3
我遇到了非常类似的问题;TextView可以滚动但无法选择。 请从您的代码中删除以下内容: texview.setMovementMethod(new ScrollingMovementMethod()) 我在您的xml中为TextView添加了以下内容: android:scrollHorizontally="true" android:textIsSelectable="true"

很奇怪,它说滚动应该是水平的,但是使用android:scrollbars="vertical"却还是竖直滚动。哈哈,谢谢。 - A1m

0

你可以通过两种方式使TextView可选

一种是通过XML:

 android:textIsSelectable="true"

通过Java代码:

 textView.setTextIsSelectable(true);

2
正如您所看到的,它已经存在于我的代码中,但对我而言它并不起作用。 - Sasha Shpota
请查看带有一些新发现和详细测试用例的编辑,了解如何重现该问题。 - Sasha Shpota

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接