NestedScrollView嵌套RecyclerView中TextView自定义SelectionActionModeCallback遇问题求助
NestedScrollView嵌套RecyclerView文本选择菜单问题解决
场景说明
布局结构为NestedScrollView嵌套RecyclerView,每个RecyclerView条目包含:
- TextView one
- ImageView
- TextView two
通过Kotlin扩展函数自定义TextView的文本选择操作,原代码如下:
fun TextView.customMenuCallBack() { var selectAll = false customSelectionActionModeCallback = object : ActionMode.Callback { override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean { return true } override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { return true } override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { item?.let { return when (it.itemId) { android.R.id.selectAll -> { selectAll = true false } android.R.id.copy -> { if (selectAll) { //TODO set content try { val clipboard: ClipboardManager = context.getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(context.toString(), content) clipboard.setPrimaryClip(clip) } catch (e: Exception) { e.printStackTrace() } } //make custom action work return true true } else -> { false } } } return false } override fun onDestroyActionMode(mode: ActionMode?) { } } }
遇到的问题
- 点击菜单项后菜单无法隐藏;
- 滚动异常:向前滚动时会突然跳回选中文本附近的位置;
- 在MIUI系统中无法正常工作。
针对性解决方案
问题1:点击菜单项后菜单无法隐藏
原因:处理完菜单项逻辑后未主动结束ActionMode。
修改代码:在onActionItemClicked中完成逻辑后调用mode?.finish():
fun TextView.customMenuCallBack() { var selectAll = false customSelectionActionModeCallback = object : ActionMode.Callback { override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean { return true } override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { return true } override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { item?.let { return when (it.itemId) { android.R.id.selectAll -> { selectAll = true false // 交给系统处理全选逻辑 } android.R.id.copy -> { if (selectAll) { try { val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("文本复制", text.toString()) clipboard.setPrimaryClip(clip) } catch (e: Exception) { e.printStackTrace() } } mode?.finish() // 结束ActionMode,隐藏菜单 return true } else -> false } } return false } override fun onDestroyActionMode(mode: ActionMode?) { selectAll = false // 重置状态 } } }
问题2:滚动时跳回选中文本位置
原因:文本选中后,TextView会持有焦点并设置滚动锚点,干扰NestedScrollView的滚动逻辑。
解决方案:
- 在RecyclerView的Adapter中,回收条目时清除TextView选中状态并结束ActionMode:
override fun onViewRecycled(holder: YourViewHolder) { super.onViewRecycled(holder) // 清除文本选中状态 holder.textViewOne.apply { textSelectionMode = TextView.SELECTION_MODE_NONE setSelection(0, 0) } holder.textViewTwo.apply { textSelectionMode = TextView.SELECTION_MODE_NONE setSelection(0, 0) } // 结束当前ActionMode (holder.itemView.context as? AppCompatActivity)?.supportActionMode?.finish() }
- 给NestedScrollView添加属性,避免子View抢占焦点:
<androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="blocksDescendants"> <!-- 内部RecyclerView等布局 --> </androidx.core.widget.NestedScrollView>
问题3:MIUI系统中无法正常工作
原因:MIUI对系统ActionMode有自定义修改,默认customSelectionActionModeCallback可能被系统逻辑覆盖。
解决方案:拦截长按事件,自定义ActionMode菜单:
- 新建自定义菜单文件
res/menu/custom_text_select_menu.xml:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_select_all" android:title="@string/select_all"/> <item android:id="@+id/action_copy" android:title="@string/copy"/> </menu>
- 修改扩展函数,通过长按触发自定义ActionMode:
fun TextView.customMenuCallBack() { setOnLongClickListener { // 先选中全部文本 setSelection(0, text.length) // 启动自定义ActionMode (context as? AppCompatActivity)?.startActionMode(object : ActionMode.Callback { override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean { mode?.menuInflater?.inflate(R.menu.custom_text_select_menu, menu) return true } override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { return true } override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { item?.let { when (it.itemId) { R.id.action_select_all -> { setSelection(0, text.length) mode?.finish() return true } R.id.action_copy -> { try { val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("文本复制", text.toString()) clipboard.setPrimaryClip(clip) } catch (e: Exception) { e.printStackTrace() } mode?.finish() return true } } } return false } override fun onDestroyActionMode(mode: ActionMode?) { // 清除选中状态 setSelection(0, 0) } }) true } }
内容的提问来源于stack exchange,提问作者shuabing
相关产品推荐
相关产品推荐

