“android:textIsSelectable=”true”不适用于 RecyclerView 中的 TextView

问题描述 投票:0回答:10

我知道在 xml 中为

android:textIsSelectable="true"
设置
TextView
将显示本机文本选择弹出窗口,我一直在我的应用程序中使用它。但我发现,当我尝试在附加到
RecyclerView
的视图中设置相同的属性时,它不再起作用。 每当我尝试选择文本时,都会出现以下日志 -

TextView: TextView does not support text selection. Action mode cancelled.

我不知道为什么?为什么它可以在其他屏幕上运行,而不能在

RecyclerView
上运行。我读了很多帖子 -

带有 android:textIsSelectable="true" 的 TextView 在列表视图中不起作用

textview textIsSelectable="true" 在列表视图中不起作用

android:textIsSelectable="true" 对于 Listview 内的 TextView 不起作用

但是后来我遇到了这篇文章 -

Android:“TextView不支持文本选择。操作模式已取消”

@hungkk
的回复对我有用。他的解决方案建议将
TextView
宽度从
wrap_content
更改为
match_parent

我知道我可以做到这一点,但我的问题是如何解决这个问题,因为它对我来说看起来很奇怪。另外,如果我想将宽度保持为

match_parent
,有什么解决方案。

欢迎任何意见。

android android-recyclerview textview
10个回答
14
投票

在recyclerview的主父布局中添加属性

android:descendantFocusability="beforeDescendants"

然后在rowitem布局的TextView中添加

android:textIsSelectable="true"

13
投票

我发现RecyclerView中的TextView第一次可以选择,但是当ViewHolder被回收或适配器notifyDataSetChanged时,所有文本视图将无法选择。 我发现这个解决方案对我有用。

yourTextView.setText("your text");
yourTextView.setTextIsSelectable(false);
yourTextView.measure(-1, -1);//you can specific other values.
yourTextView.setTextIsSelectable(true);

为什么要这样做?因为我调试过,在android源码中发现了一些逻辑:

TextView.java:

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

编辑器.java

void prepareCursorControllers() {
    boolean windowSupportsHandles = false;

    ViewGroup.LayoutParams params = mTextView.getRootView().getLayoutParams();
    if (params instanceof WindowManager.LayoutParams) {
        WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
        windowSupportsHandles = windowParams.type < WindowManager.LayoutParams.FIRST_SUB_WINDOW
                || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW;
    }

    boolean enabled = windowSupportsHandles && mTextView.getLayout() != null;
    mInsertionControllerEnabled = enabled && isCursorVisible();
    **mSelectionControllerEnabled = enabled && mTextView.textCanBeSelected();**

    if (!mInsertionControllerEnabled) {
        hideInsertionPointCursorController();
        if (mInsertionPointCursorController != null) {
            mInsertionPointCursorController.onDetached();
            mInsertionPointCursorController = null;
        }
    }

    if (!mSelectionControllerEnabled) {
        stopTextActionMode();
        if (mSelectionModifierCursorController != null) {
            mSelectionModifierCursorController.onDetached();
            mSelectionModifierCursorController = null;
        }
    }
}

---> TextView.java

/**
 * Test based on the <i>intrinsic</i> charateristics of the TextView.
 * The text must be spannable and the movement method must allow for arbitary selection.
 *
 * See also {@link #canSelectText()}.
 */
boolean textCanBeSelected() {
    // prepareCursorController() relies on this method.
    // If you change this condition, make sure prepareCursorController is called anywhere
    // the value of this condition might be changed.
    if (mMovement == null || !mMovement.canSelectArbitrarily()) return false;
    return isTextEditable()
            || (isTextSelectable() && mText instanceof Spannable && isEnabled());
}

您可以在模拟器中调试并跟踪此代码。


6
投票

如果您在

android:descendantFocusability="blocksDescendants"​
recyclerview
中添加
listview
,则将其删除。 检查后


5
投票
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    yourTextView.fixTextSelection()
}

fun TextView.fixTextSelection() {
    setTextIsSelectable(false)
    post { setTextIsSelectable(true) }
}

1
投票

添加您的 RecyclerView 适配器:

public ViewHolder(View itemView) {
            super(itemView);
            txtDate = (TextView) itemView.findViewById(R.id.txtDate);
            txtDate.setTextIsSelectable(true);
}

它对我有用..


1
投票

似乎有很多人对此有问题,并且有迹象表明这可能是 Android 代码中的错误,但我没有问题。这对于

OnClickListener()
和本机选择弹出窗口都适用。 (在 KitKat 4.4、Lollipop 5.1 和 Nougat 7.1 上测试)

在适配器中

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    TextView textView;
    ImageView imageView;

    MyViewHolder(View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.my_text_view);
        imageView = (ImageView) itemView.findViewById(R.id.my_image_view);

        itemView.setOnClickListener(this);
        textView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        // this shows 'my_text_view' when the text is clicked or 
        //     'my_item' if elsewhere is clicked
        Log.d(TAG, "view = " + view.toString());
        switch (view.getId()) {
            case R.id.my_item:
                break;
            case R.id.my_text_view:
                break;
        }
    }
}

还有我的物品布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/my_item"
    >

    <ImageView
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:background="@color/colorPrimary"
        android:id="@+id/my_image_view"
        />

    <!-- this works for me with either "match_parent" or "wrap_content" for width -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:text="My text view"
        android:textIsSelectable="true"
        android:id="@+id/my_text_view"
        />
</LinearLayout>

0
投票

如果您的

TextView
ConstraintLayout
内部,请确保宽度不是
wrap_content
。使用
TextView
宽度
0dp
match_parent
效果很好。


0
投票

我发现我必须在一段时间后设置

TextView
文本及其宽度。所以我把这个属性(
android:textIsSelectable="true"
)放在
TextView
的xml布局中,并将
post{}
的宽度和文本放在
onBindViewHolder
适配器的
recyclerView
方法中,如下所示:

class ContentAdapter(): ListAdapter<Verse, ContentAdapter.ViewHolder>(DiffCallback()) {
    .
    .
    .
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val item = getItem(position)
            holder.bind(item)
    }

    class ViewHolder(val binding: ItemBinding): RecyclerView.ViewHolder(binding.root) {

        fun bind(item: Verse){
            binding.myTextView.apply{
                val params = layoutParams as ConstraintLayout.LayoutParams
                params.width = 100 /*any value you want for the width of your TextView*/
                post{
                    layoutParams = params
                    text = item.text
                }
            }
        }

        companion object {
            fun from(parent: ViewGroup): ViewHolder{
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = ItemBinding.inflate(layoutInflater, parent, false)
                return ViewHolder(binding)
            }
        }
    }

}

0
投票

最终和工作解决方案

在你的 onBindView 中像这样写你的代码!

    textView.text = "the content"

    textView.setTextIsSelectable(false)
    textView.post { txtContent.setTextIsSelectable(true) }

或者高级版本可以在TextView上编写扩展功能

fun TextView.fixTextSelection(){
   setTextIsSelectable(false)
   post { setTextIsSelectable(true) }
}

然后像这样使用它

    textView.text = "the content"

    textView.fixTextSelection()

0
投票

我根据 @Artem 的回答创建了一个辅助函数

onBindViewHolder
中的用法:

textView.setSelectableText("hello world!")

Extensions.kt
中的辅助函数:

/**
 * https://stackoverflow.com/a/61126872/2898715
 */
fun TextView.setSelectableText(text:CharSequence?)
{
    setText(text)
    setTextIsSelectable(false)
    post { setTextIsSelectable(true) }
}
© www.soinside.com 2019 - 2024. All rights reserved.