Kotlin中如何为RecyclerView使用registerContextMenu实现上下文菜单
直接对RecyclerView整体调用registerForContextMenu无法生效,系统上下文菜单需要绑定到具体可交互的子View,也就是RecyclerView的每个列表项布局上,以下是完整适配方案:
1 先定义上下文菜单资源
在res/menu目录下新建file_context_menu.xml,编写你需要的菜单选项:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_copy" android:title="复制" /> <item android:id="@+id/action_cut" android:title="剪切" /> <item android:id="@+id/action_delete" android:title="删除" /> <item android:id="@+id/action_rename" android:title="重命名" /> </menu>
2 修改Adapter代码
新增长按回调逻辑,记录当前长按的子项位置:
import ... class MyAdapter(private val fileList: ArrayList<File>): RecyclerView.Adapter<MyAdapter.MyViewHolder>() { private lateinit var clickListener: OnItemClickListener private lateinit var longClickListener: OnItemLongClickListener // 存储当前长按的子项位置,供Activity获取 var currentLongClickPosition: Int = RecyclerView.NO_POSITION private set interface OnItemClickListener { fun onItemClick(position: Int) } interface OnItemLongClickListener { fun onItemLongClick(position: Int, itemView: View) } fun setOnItemClickListener(listener: OnItemClickListener) { clickListener = listener } fun setOnItemLongClickListener(listener: OnItemLongClickListener) { longClickListener = listener } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val itemView = LayoutInflater.from(parent.context).inflate(R.layout.list_items, parent, false) return MyViewHolder(itemView, clickListener, longClickListener) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val currentItem = fileList[position] holder.titleImage.setImageResource(if (currentItem.isDirectory) R.drawable.ic_folder else R.drawable.ic_file) holder.tvHeading.text = currentItem.name } override fun getItemCount(): Int = fileList.size inner class MyViewHolder(itemView: View, clickListener: OnItemClickListener, longClickListener: OnItemLongClickListener): RecyclerView.ViewHolder(itemView) { val titleImage: ShapeableImageView = itemView.findViewById(R.id.title_image) val tvHeading: TextView = itemView.findViewById(R.id.tvHeading) init { itemView.setOnClickListener { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { clickListener.onItemClick(position) } } itemView.setOnLongClickListener { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { currentLongClickPosition = position longClickListener.onItemLongClick(position, itemView) } // 返回true表示消费长按事件,不会触发点击事件 true } } } }
注:这里把你原代码里的News类替换为实际使用的文件实体类即可,示例用的是系统File类
3 在Activity中实现上下文菜单逻辑
import ... class FileManagerActivity : AppCompatActivity() { private lateinit var adapter: MyAdapter private lateinit var recyclerView: RecyclerView // 存储当前操作的文件对象 private lateinit var currentOperateFile: File override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_file_manager) recyclerView = findViewById(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(this) // 初始化当前目录的文件列表 val fileList = getCurrentDirFiles() adapter = MyAdapter(fileList) recyclerView.adapter = adapter // 点击事件处理,打开文件/进入文件夹 adapter.setOnItemClickListener { position -> val file = fileList[position] if (file.isDirectory) { // 进入文件夹,刷新列表 refreshFileList(file) } else { // 打开文件 openFile(file) } } // 长按事件处理,弹出上下文菜单 adapter.setOnItemLongClickListener { position, itemView -> currentOperateFile = fileList[position] // 注册当前itemView的上下文菜单 registerForContextMenu(itemView) // 手动弹出上下文菜单 openContextMenu(itemView) // 用完即注销,避免ViewHolder复用导致的菜单绑定错误 unregisterForContextMenu(itemView) } } // 加载上下文菜单 override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) { super.onCreateContextMenu(menu, v, menuInfo) menuInflater.inflate(R.menu.file_context_menu, menu) // 可根据当前文件类型动态修改菜单,比如文件夹不显示特定操作选项 menu?.setHeaderTitle(currentOperateFile.name) } // 处理菜单点击事件 override fun onContextItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.action_copy -> { // 实现复制逻辑 copyFile(currentOperateFile) true } R.id.action_cut -> { // 实现剪切逻辑 cutFile(currentOperateFile) true } R.id.action_delete -> { // 实现删除逻辑 deleteFile(currentOperateFile) true } R.id.action_rename -> { // 实现重命名逻辑 renameFile(currentOperateFile) true } else -> super.onContextItemSelected(item) } } // 你的业务方法实现 private fun getCurrentDirFiles(): ArrayList<File> { // 实现获取当前目录下的文件列表逻辑 return arrayListOf() } private fun refreshFileList(dir: File) { // 实现切换目录后刷新列表逻辑 val newList = getCurrentDirFiles() adapter = MyAdapter(newList) recyclerView.adapter = adapter } }
4 列表刷新适配说明
由于上下文菜单的注册和弹出是在每次长按子项时动态触发的,列表刷新时只需要更新Adapter的数据源并调用notify相关方法即可,不需要额外处理菜单逻辑,完全适配文件管理器动态切换目录的场景。
内容的提问来源于stack exchange,提问作者Hallen
相关产品推荐
相关产品推荐

