Флажок не появляется в режиме выбора с Recyclerview и ListDapter в Android [закрыто]Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Флажок не появляется в режиме выбора с Recyclerview и ListDapter в Android [закрыто]

Сообщение Anonymous »

Я разрабатываю приложение, которое отображает список аудио -клипов. Я использую Recyclerview с списком. Я реализовал функцию режима выбора, где пользователи могут выбрать несколько элементов. Тем не менее, я сталкиваюсь с основной проблемой в режиме выбора, каждый элемент в переработке должен отображать флажок (определенный в clip_item.xml), чтобы разрешить выбор. Тем не менее, флажок остается невидимым, несмотря на то, что он устанавливает его видимость для просмотра. Видимо в ClipAdapter, когда IsseLectionMode является TRUE.
Я попробовал обновить Recyclerview с помощью notifyDatasetchAnted () и uppertist () , но проблема сохраняется. Функция устанавливает isselectionmode true и попытки отобразить inding.customsearchView, установив его видимость для просмотра. Visible < /code>.
Несмотря на это, CustomSearchView (edittext) не появляется на экране. Проблема: флажок не появляется
in clipadapter, когда IsseLectionMode True, я скрываю ImageView MotionOptions и показываю SelectionCheckbox. < /P>
Я попытался придумать обновление, позвонив по спискам () < /код>, когда IsseLectionMode изменился, но это не помогло. clip_item.xml clipadapter.kt

Код: Выделить всё

class ClipAdapter(
private val selectionListener: OnItemSelectionListener) : ListAdapter(ClipDiffCallback) {

var isSelectionMode: Boolean = false
set(value) {
field = value

val newList = currentList.map { it.copy() }
submitList(newList)
}

private val selectionState: MutableMap  = mutableMapOf()

fun updateSelectionState(clipId: Long, isSelected: Boolean) {
selectionState[clipId] = isSelected
notifyItemChanged(currentList.indexOfFirst { it.id == clipId })
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ClipViewHolder {
val binding = ClipItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ClipViewHolder(binding)
}

override fun onBindViewHolder(holder: ClipViewHolder, position: Int) {
val clip = getItem(position)
Log.d("ClipAdapter", "Binding item at position $position: ${clip.title}")
holder.bind(clip)
}

inner class ClipViewHolder(
private val binding: ClipItemBinding
): RecyclerView.ViewHolder(binding.root) {

fun bind(clip: Clip) {
binding.apply {
if (clip.artUri != null) {
Glide.with(binding.root)
.load(clip.artUri)
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(albumImage)
} else {
albumImage.setImageResource(R.mipmap.ic_launcher)
}

clipTitle.text = clip.title
clipArtist.text = clip.artist ?: ""

if (isSelectionMode) {

moreOptions.visibility = View.GONE
selectionCheckBox.visibility = View.VISIBLE
selectionCheckBox.isChecked = clip.isSelected

selectionCheckBox.setOnCheckedChangeListener { _, isChecked ->
clip.isSelected = isChecked
selectionListener.onItemSelectionChanged(adapterPosition, isChecked)
}
} else {
moreOptions.visibility = View.VISIBLE
selectionCheckBox.visibility = View.GONE
selectionCheckBox.setOnCheckedChangeListener(null)
}
}
}
}

companion object {
val ClipDiffCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: Clip, newItem: Clip): Boolean {
val result = oldItem.id == newItem.id
Log.d(
"ClipDiffCallback",
"areItemsTheSame: oldItem.id=${oldItem.id}, newItem.id=${newItem.id}, result=$result"
)
return result
}

override fun areContentsTheSame(oldItem: Clip, newItem: Clip): Boolean {
val result = oldItem.title == newItem.title &&
oldItem.artist == newItem.artist &&
oldItem.artUri == newItem.artUri &&
oldItem.isSelected == newItem.isSelected
Log.d(
"ClipDiffCallback",
"areContentsTheSame: oldItem=$oldItem, newItem=$newItem, result=$result"
)
return result
}
}
}

}

HomeFragment Этот фрагмент управляет режимом переработки и выбора

Код: Выделить всё

interface OnItemSelectionListener {
fun onItemSelectionChanged(position: Int, isSelected: Boolean)
}

@AndroidEntryPoint
class HomeFragment : Fragment(), MenuProvider, OnItemSelectionListener {

private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!

private lateinit var requestPermissionLauncher: ActivityResultLauncher
private val clipViewModel by viewModels()
private val selectedPositions: MutableSet = mutableSetOf()
private var clipAdapter: ClipAdapter? = null
private var mMainActivityUiController: MainActivityUiController? = null
private var actionMode: ActionMode? = null
private var originalClips: List  = emptyList()
private var clipPosition: Int = 0
private var menu: Menu? = null

private var isSelectionMode: Boolean = false
set(value) {
field = value
clipAdapter?.isSelectionMode = value
if (!value) {
selectedPositions.clear()
val newList = clipAdapter?.currentList?.map { it.copy(isSelected = false) }
binding.clipsRV.post { clipAdapter?.submitList(newList) }
}
}

private var menuHost: MenuHost? = null

fun hideMenu() {
isSelectionMode = true
menu?.setGroupVisible(0, false)

}

fun showMenu() {
isSelectionMode = false

menu?.setGroupVisible(0, true)
(requireActivity() as MainActivity).getToolbar().visibility = View.VISIBLE

}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
Log.d(TAG, "Adding MenuProvider for HomeFragment")
menuHost = requireActivity()
menuHost?.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.STARTED)
this.clipAdapter = ClipAdapter(this)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupPermissionLauncher()
checkAndRequestPermissions()
observeClips()
setupRecyclerView()

binding.clipsRV.visibility = View.VISIBLE

binding.customSearchView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Log.d(TAG, "Filtering with query: $s")
filterSelectedClips(s.toString())
}

override fun afterTextChanged(s: Editable?) {}
})

if (activity is MainActivityUiController) {
mMainActivityUiController = activity as MainActivityUiController
}
}

override fun onResume() {
super.onResume()
(requireActivity() as MainActivity).getToolbar().visibility = View.VISIBLE
mMainActivityUiController?.showTabLayout()
requireActivity().invalidateOptionsMenu()
}

override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {

this.menu = menu

menu.clear()
menuInflater.inflate(R.menu.clips_home_menu, menu)

customizeMenuItem(menu, R.id.sortBy, "Sort by")
customizeMenuItem(menu, R.id.change_layout, "Change layout")

if (isSelectionMode) {
menu.setGroupVisible(0, false)
}

setupSearchView(menu)
}

override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.select -> {
isSelectionMode = !isSelectionMode
if (isSelectionMode) {
startSelectionMode()
} else {
actionMode?.finish()
}
true
}
R.id.sortBy -> {
true
}
else -> false
}
}

true
}
R.id.change_layout -> {

true
}
else ->  false
}
}

private fun setupSearchView(menu: Menu) {
val searchManager = requireActivity().getSystemService(SEARCH_SERVICE) as SearchManager
val searchView = menu.findItem(R.id.app_bar_search).actionView as SearchView
searchView.setSearchableInfo(searchManager.getSearchableInfo(requireActivity().componentName))
searchView.queryHint = getString(R.string.library_search)

searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(keyword: String): Boolean {
if (keyword.isEmpty()) {
Snackbar.make(
binding.root,
"please enter keyword to search",
Snackbar.LENGTH_SHORT
).show()
return false
}
filterClips(keyword)
return false
}

override fun onQueryTextChange(newText: String): Boolean {

return true
}
})
}

private fun filterClips(keyword: String) {
val filteredList = if (keyword.isEmpty()) {
originalClips
} else {
originalClips.filter {
it.title.contains(keyword, ignoreCase = true)
}
}
clipAdapter?.submitList(filteredList)
}

private fun filterSelectedClips(keyword: String) {
val filteredList = if (keyword.isEmpty()) {
originalClips
} else {
originalClips.filter {
it.title.contains(keyword, ignoreCase = true)
}
}
clipAdapter?.submitList(filteredList)
}

private fun startSelectionMode() {
isSelectionMode = true

hideMenu()

binding.customSearchView.visibility = View.VISIBLE

mMainActivityUiController?.hideTabLayout()

actionMode = (requireActivity() as MainActivity).startSupportActionMode(object :
ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
(requireActivity()as MainActivity).getToolbar().visibility = View.GONE
mode.menuInflater.inflate(R.menu.select_all_menu, menu)
mode.title = "${selectedPositions.size} clips selected"
updateActionModeTitle()
return true
}

override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = false

override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_select_all -> {
selectAll()
true
}

else ->  false
}
}

override fun onDestroyActionMode(mode: ActionMode) {
isSelectionMode = false
clipAdapter?.isSelectionMode = false

showMenu()

mMainActivityUiController?.showTabLayout()

binding.customSearchView.visibility = View.GONE
binding.customSearchView.text.clear()
clipAdapter?.submitList(originalClips)
actionMode = null
binding.clipsRV.scrollToPosition(0)
requireActivity().invalidateOptionsMenu()
}
})
}

private fun updateActionModeTitle() {
val count = selectedPositions.size
actionMode?.title = "$count selected clips"
}

override fun onItemSelectionChanged(position: Int, isSelected: Boolean) {
if (isSelected) {
selectedPositions.add(position)
} else {
selectedPositions.remove(position)
}

val newList = clipAdapter?.currentList!!.toMutableList()
newList[position] = newList[position].copy(isSelected = isSelected)
binding.clipsRV.post { clipAdapter?.submitList(newList) }

updateActionModeTitle()
}

private fun selectAll() {
if (clipAdapter?.currentList?.size == selectedPositions.size) {

selectedPositions.clear()
val newList = clipAdapter?.currentList!!.map { it.copy(isSelected = false) }
binding.clipsRV.post { clipAdapter?.submitList(newList) }
} else {

selectedPositions.clear()
selectedPositions.addAll(clipAdapter?.currentList!!.indices)
val newList = clipAdapter?.currentList?.map { it.copy(isSelected = true) }
binding.clipsRV.post { clipAdapter?.submitList(newList) }
}
updateActionModeTitle()
}

private fun setupPermissionLauncher() {
requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
clipViewModel.syncAndFetchClips()
} else {
val permissionToRequest =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_AUDIO
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (!shouldShowRequestPermissionRationale(permissionToRequest)) {
AlertDialog.Builder(requireContext())
.setTitle("Permission Denied")
.setMessage("This app needs access to your music files to display and play them. Please enable the permission in app settings.")
.setPositiveButton("Go to Settings") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data =
Uri.fromParts("package", requireContext().packageName, null)
startActivity(intent)
}
.setNegativeButton("Cancel") { _, _ ->
Toast.makeText(
requireContext(),
"Permission denied. Cannot access music files.",
Toast.LENGTH_SHORT
).show()
}
.show()
} else {
Toast.makeText(
requireContext(),
"Permission denied.  Cannot access music files.",
Toast.LENGTH_SHORT
).show()
}
}
}
}

private fun checkAndRequestPermissions() {
val permissionToRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_AUDIO
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}

if (ContextCompat.checkSelfPermission(
requireContext(),
permissionToRequest
) != PackageManager.PERMISSION_GRANTED
) {
if (shouldShowRequestPermissionRationale(permissionToRequest)) {
showPermissionRationaleDialog(permissionToRequest)
} else {
requestPermissionLauncher.launch(permissionToRequest)
}
} else {
clipViewModel.syncAndFetchClips()
}
}

private fun showPermissionRationaleDialog(permission: String) {
AlertDialog.Builder(requireContext())
.setTitle("Permission Required")
.setMessage("This app needs access to your music files to display and play them. Please grant the permission to continue.")
.setPositiveButton("Grant") { _, _ ->
requestPermissionLauncher.launch(permission)
}
.setNegativeButton("Deny") { _, _ ->
Toast.makeText(
requireContext(),
"Permission denied.  Cannot access music files.",
Toast.LENGTH_SHORT
).show()
}
.show()
}

private fun observeClips() {
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
clipViewModel.clipList.collect { resource ->
when (resource) {
is Resource.Loading -> {
binding.progressBar.visibility = View.VISIBLE
}

is Resource.Success -> {
binding.progressBar.visibility = View.GONE
originalClips = resource.data ?: emptyList()
Log.d(TAG, "Clips loaded, size: ${originalClips.size}")
clipAdapter?.submitList(originalClips) {
Log.d(TAG, "Submitted original clips to clipAdapter")
}
}

is Resource.Error -> {
binding.progressBar.visibility = View.GONE
Toast.makeText(
requireContext(),
"Error: ${resource.message}",
Toast.LENGTH_SHORT
).show()
}

is Resource.Empty -> {
binding.progressBar.visibility = View.GONE
}

else ->  {}
}
}
}
}
}

private fun setupRecyclerView() {
binding.clipsRV.apply {
setHasFixedSize(true)
setItemViewCacheSize(10)
layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
adapter = clipAdapter

onItemClick { _, position, _ ->
if (!isSelectionMode) {

clipPosition = position

val navController = requireActivity()
.findNavController(R.id.nav_host_fragment_container)

(requireActivity() as MainActivity).showNavHostFragmentContainer()

if (navController.currentDestination?.id == R.id.homeFragment) {
navController.navigate(
HomeFragmentDirections.actionHomeFragmentToPlayerFragment(
clipPosition,
clipsArray = clipAdapter?.currentList!!.toTypedArray()
)
)
}
} else {

val clip = clipAdapter?.currentList[position]
val newCheckedState = !clip?.isSelected!!
clip.isSelected = newCheckedState
onItemSelectionChanged(position, newCheckedState)
}
}

onItemCheckChange { _, position, isChecked ->
if (isSelectionMode) {
onItemSelectionChanged(position, isChecked)
}
}
}
}

override fun onDestroyView() {
super.onDestroyView()
Log.d(TAG, "Removing MenuProvider for HomeFragment")
clipAdapter = null
menuHost?.removeMenuProvider(this)
binding.clipsRV.removeItemClickSupport()
_binding = null
}

}
и я обрабатываю щелчки и флажки с этим классом itemclicksupport.kt

Код: Выделить всё

class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {

private var onItemClickListener: OnRecyclerViewItemClickListener? = null
private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null
private var onItemCheckChangeListener: OnRecyclerViewItemCheckChangeListener? = null

private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
val holder = this@ItemClickSupport.recyclerView.getChildViewHolder(view)

val isClickable = (holder as? ItemClickSupportViewHolder)?.isClickable ?: true
val isLongClickable = (holder as? ItemClickSupportViewHolder)?.isLongClickable ?: true

if (onItemClickListener != null && isClickable) {
view.setOnClickListener(onClickListener)
}

if (onItemLongClickListener != null && isLongClickable) {
view.setOnLongClickListener(onLongClickListener)
}

val checkBox = view.findViewById(R.id.selectionCheckBox)
if (checkBox != null &&  onItemCheckChangeListener != null) {
checkBox.setOnCheckedChangeListener { _, isChecked ->
val position = holder.getAdapterPosition()
if (position != RecyclerView.NO_POSITION) {
onItemCheckChangeListener?.invoke(recyclerView, position, isChecked)
}
}
}
}

override fun onChildViewDetachedFromWindow(view: View) {
val checkBox = view.findViewById(R.id.selectionCheckBox)
checkBox?.setOnCheckedChangeListener(null)
}
}

init {
this.recyclerView.setTag(R.id.item_click_support, this)
this.recyclerView.addOnChildAttachStateChangeListener(attachListener)
}

companion object {
fun addTo(view: RecyclerView): ItemClickSupport {
var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport
if (support == null) {
support = ItemClickSupport(view)
}
return support
}

fun removeFrom(view: RecyclerView): ItemClickSupport? {
val support = view.getTag(R.id.item_click_support) as? ItemClickSupport
support?.detach(view)
return support
}
}

private val onClickListener = View.OnClickListener { v ->
val listener = onItemClickListener ?: return@OnClickListener
val holder = this.recyclerView.getChildViewHolder(v)
val position = holder.getAdapterPosition()
if (position != RecyclerView.NO_POSITION) {
listener.invoke(this.recyclerView, position, v)
}
}

private val onLongClickListener = View.OnLongClickListener { v ->
val listener = onItemLongClickListener ?: return@OnLongClickListener false
val holder = this.recyclerView.getChildViewHolder(v)
val position = holder.getAdapterPosition()
if (position != RecyclerView.NO_POSITION) {
return@OnLongClickListener listener.invoke(this.recyclerView, position, v)
}
return@OnLongClickListener false
}

private fun detach(view: RecyclerView) {
view.removeOnChildAttachStateChangeListener(attachListener)
view.setTag(R.id.item_click_support, null)
}

fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport {
onItemClickListener = listener
return this
}

fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport {
onItemLongClickListener = listener
return this
}

fun onItemCheckChange(listener: OnRecyclerViewItemCheckChangeListener?): ItemClickSupport {
onItemCheckChangeListener = listener
return this
}
}

interface ItemClickSupportViewHolder {
val isClickable: Boolean get() = true
val isLongClickable: Boolean get() = true
}

fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration)

fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this)

fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) {
addItemClickSupport { onItemClick(onClick) }
}

fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) {
addItemClickSupport { onItemLongClick(onLongClick) }
}

fun RecyclerView.onItemCheckChange(onCheckChange: OnRecyclerViewItemCheckChangeListener) {
addItemClickSupport { onItemCheckChange(onCheckChange) }
}
Это изображение показывает проблему, когда я ввожу режим выбора и изменяю все три точки с флажками


Подробнее здесь: https://stackoverflow.com/questions/795 ... pter-in-an
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»