我知道在 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
,有什么解决方案。
欢迎任何意见。
在recyclerview的主父布局中添加属性
android:descendantFocusability="beforeDescendants"
然后在rowitem布局的TextView中添加
android:textIsSelectable="true"
我发现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());
}
您可以在模拟器中调试并跟踪此代码。
如果您在
android:descendantFocusability="blocksDescendants"
或 recyclerview
中添加 listview
,则将其删除。
检查后
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
yourTextView.fixTextSelection()
}
fun TextView.fixTextSelection() {
setTextIsSelectable(false)
post { setTextIsSelectable(true) }
}
添加您的 RecyclerView 适配器:
public ViewHolder(View itemView) {
super(itemView);
txtDate = (TextView) itemView.findViewById(R.id.txtDate);
txtDate.setTextIsSelectable(true);
}
它对我有用..
似乎有很多人对此有问题,并且有迹象表明这可能是 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>
如果您的
TextView
在 ConstraintLayout
内部,请确保宽度不是 wrap_content
。使用 TextView
宽度 0dp
或 match_parent
效果很好。
我发现我必须在一段时间后设置
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)
}
}
}
}
最终和工作解决方案
在你的 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()
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) }
}