RecyclerView Selection 库 - 点击太快会让点击传递到我的 OnClickListener
RecyclerView Selection library - clicking too fast lets the click pass through to my OnClickListener
我正在使用 RecyclerView selection 库,但我的 ViewHolder 也有自己的 OnClickListeners。 当我 select 一个接一个地快速地点击一个项目,或者双击一个项目,而不是 selecting/unselecting 项目时,点击被传递到我自己的 OnClickListener。这种行为真的很烦人。这是错误还是我的设置有问题?
这是我的代码(我删除了不感兴趣的部分)。
适配器:
class UserListAdapter : ListAdapter<User, UserListAdapter.UserViewHolder>(USER_COMPARATOR) {
private var listener: ((User) -> Unit)? = null
[...]
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = getItem(position)
val tracker = tracker
if (user != null && tracker != null) {
holder.bind(user, tracker.isSelected(user.uid))
}
}
inner class UserViewHolder(private val binding: ItemUserListBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
val position = bindingAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = getItem(position)
listener?.invoke(item)
}
}
}
fun bind(user: User, isSelected: Boolean) {
binding.apply {
[...]
root.isActivated = isSelected
}
}
fun getItemDetails() = object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition() = bindingAdapterPosition
override fun getSelectionKey() = getItem(bindingAdapterPosition).uid
}
}
class KeyProvider(private val adapter: UserListAdapter) :
ItemKeyProvider<String>(SCOPE_CACHED) {
override fun getKey(position: Int) = adapter.currentList[position].uid
override fun getPosition(key: String) =
adapter.currentList.indexOfFirst { it.uid == key }
}
class DetailsLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<String>() {
override fun getItemDetails(e: MotionEvent): ItemDetails<String>? {
val view = recyclerView.findChildViewUnder(e.x, e.y)
if (view != null) {
return (recyclerView.getChildViewHolder(view) as UserListAdapter.UserViewHolder).getItemDetails()
}
return null
}
}
var tracker: SelectionTracker<String>? = null
fun setOnClickListener(listener: (User) -> Unit) {
this.listener = listener
}
[...]
}
片段:
@AndroidEntryPoint
class UserListFragment : Fragment(R.layout.fragment_user_list) {
[...]
private lateinit var selectionTracker: SelectionTracker<String>
private var actionMode: ActionMode? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
[...]
val userAdapter = UserListAdapter()
binding.apply {
recyclerView.apply {
adapter = userAdapter
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
addItemDecoration(
DividerItemDecoration(
requireContext(),
DividerItemDecoration.VERTICAL
)
)
}
selectionTracker = SelectionTracker.Builder(
"user selection",
recyclerView,
UserListAdapter.KeyProvider(userAdapter),
UserListAdapter.DetailsLookup(recyclerView),
StorageStrategy.createStringStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
userAdapter.tracker = selectionTracker
selectionTracker.addObserver(selectionObserver)
}
if (savedInstanceState != null) {
selectionTracker.onRestoreInstanceState(savedInstanceState)
if (selectionTracker.hasSelection()) {
selectionObserver.onSelectionChanged()
}
}
userAdapter.setOnClickListener { user ->
viewModel.onUserClick(user)
}
viewModel.userList.observe(viewLifecycleOwner) { userList ->
userAdapter.submitList(userList)
}
[...]
}
private val selectionObserver = object : SelectionTracker.SelectionObserver<String>() {
override fun onSelectionChanged() {
if (selectionTracker.selection.size() > 0) {
if (actionMode == null) {
actionMode =
(requireActivity() as AppCompatActivity).startSupportActionMode(
actionModeCallback
)
}
actionMode?.title = selectionTracker.selection.size().toString()
} else {
actionMode?.finish()
}
selectionTracker.copySelection(viewModel.selection)
}
}
private val actionModeCallback = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
mode?.menuInflater?.inflate(R.menu.menu_action_mode_user_list_fragment, menu)
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
override fun onActionItemClicked(
mode: ActionMode?,
item: MenuItem?
): Boolean {
return when (item?.itemId) {
R.id.action_new_group -> {
viewModel.onNewGroupClick()
actionMode?.finish()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
actionMode = null
selectionTracker.clearSelection()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
if (::selectionTracker.isInitialized) {
selectionTracker.onSaveInstanceState(outState)
}
}
override fun onDestroyView() {
super.onDestroyView()
actionMode?.finish()
}
}
我正在使用 RecyclerView selection 库,但我的 ViewHolder 也有自己的 OnClickListeners。 当我 select 一个接一个地快速地点击一个项目,或者双击一个项目,而不是 selecting/unselecting 项目时,点击被传递到我自己的 OnClickListener。这种行为真的很烦人。这是错误还是我的设置有问题?
这是我的代码(我删除了不感兴趣的部分)。
适配器:
class UserListAdapter : ListAdapter<User, UserListAdapter.UserViewHolder>(USER_COMPARATOR) {
private var listener: ((User) -> Unit)? = null
[...]
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = getItem(position)
val tracker = tracker
if (user != null && tracker != null) {
holder.bind(user, tracker.isSelected(user.uid))
}
}
inner class UserViewHolder(private val binding: ItemUserListBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
val position = bindingAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = getItem(position)
listener?.invoke(item)
}
}
}
fun bind(user: User, isSelected: Boolean) {
binding.apply {
[...]
root.isActivated = isSelected
}
}
fun getItemDetails() = object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition() = bindingAdapterPosition
override fun getSelectionKey() = getItem(bindingAdapterPosition).uid
}
}
class KeyProvider(private val adapter: UserListAdapter) :
ItemKeyProvider<String>(SCOPE_CACHED) {
override fun getKey(position: Int) = adapter.currentList[position].uid
override fun getPosition(key: String) =
adapter.currentList.indexOfFirst { it.uid == key }
}
class DetailsLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<String>() {
override fun getItemDetails(e: MotionEvent): ItemDetails<String>? {
val view = recyclerView.findChildViewUnder(e.x, e.y)
if (view != null) {
return (recyclerView.getChildViewHolder(view) as UserListAdapter.UserViewHolder).getItemDetails()
}
return null
}
}
var tracker: SelectionTracker<String>? = null
fun setOnClickListener(listener: (User) -> Unit) {
this.listener = listener
}
[...]
}
片段:
@AndroidEntryPoint
class UserListFragment : Fragment(R.layout.fragment_user_list) {
[...]
private lateinit var selectionTracker: SelectionTracker<String>
private var actionMode: ActionMode? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
[...]
val userAdapter = UserListAdapter()
binding.apply {
recyclerView.apply {
adapter = userAdapter
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
addItemDecoration(
DividerItemDecoration(
requireContext(),
DividerItemDecoration.VERTICAL
)
)
}
selectionTracker = SelectionTracker.Builder(
"user selection",
recyclerView,
UserListAdapter.KeyProvider(userAdapter),
UserListAdapter.DetailsLookup(recyclerView),
StorageStrategy.createStringStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
userAdapter.tracker = selectionTracker
selectionTracker.addObserver(selectionObserver)
}
if (savedInstanceState != null) {
selectionTracker.onRestoreInstanceState(savedInstanceState)
if (selectionTracker.hasSelection()) {
selectionObserver.onSelectionChanged()
}
}
userAdapter.setOnClickListener { user ->
viewModel.onUserClick(user)
}
viewModel.userList.observe(viewLifecycleOwner) { userList ->
userAdapter.submitList(userList)
}
[...]
}
private val selectionObserver = object : SelectionTracker.SelectionObserver<String>() {
override fun onSelectionChanged() {
if (selectionTracker.selection.size() > 0) {
if (actionMode == null) {
actionMode =
(requireActivity() as AppCompatActivity).startSupportActionMode(
actionModeCallback
)
}
actionMode?.title = selectionTracker.selection.size().toString()
} else {
actionMode?.finish()
}
selectionTracker.copySelection(viewModel.selection)
}
}
private val actionModeCallback = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
mode?.menuInflater?.inflate(R.menu.menu_action_mode_user_list_fragment, menu)
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
override fun onActionItemClicked(
mode: ActionMode?,
item: MenuItem?
): Boolean {
return when (item?.itemId) {
R.id.action_new_group -> {
viewModel.onNewGroupClick()
actionMode?.finish()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
actionMode = null
selectionTracker.clearSelection()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
if (::selectionTracker.isInitialized) {
selectionTracker.onSaveInstanceState(outState)
}
}
override fun onDestroyView() {
super.onDestroyView()
actionMode?.finish()
}
}