[/b], содержащий 4 карточки, ведущие к другим фрагментам (FavouritesFragment, НедавноPlayedFragment, MostPlayedFragment, НедавноДобавленныйФрагмент). Когда я перехожу к любому из этих фрагментов и нажимаю кнопку «Назад», PlaylistsFragment отображается как пустой/пустой экран. Однако, если я переключусь на другую вкладку и вернусь, фрагмент отобразится правильно
@AndroidEntryPoint
class PlaylistsFragment : Fragment(R.layout.fragment_playlists), MenuProvider,
PlaylistsAdapter.PlaylistAdapterListener {
companion object {
private const val TAG = "PlaylistsFragment"
const val NEW_PLAYLIST_ID_MARKER = -100L
}
private var _binding: FragmentPlaylistsBinding? = null
private val binding get() = _binding!!
private val playlistsViewModel by viewModels
()
private lateinit var playlistsAdapter: PlaylistsAdapter
private val selectedPositions: MutableSet = mutableSetOf()
private var actionMode: ActionMode? = null
private var originalPlaylists = arrayListOf()
private var mMainActivityUiController: MainActivityUiController? = null
private var menu: Menu? = null
private var sortType: PlaylistSortType = PlaylistSortType.NAME_ASC
private lateinit var requestPermissionLauncher: ActivityResultLauncher
private var storagePermissionGranted = false
private var menuHost: MenuHost? = null
private val recentlyPlayedViewModel by viewModels()
private val mostPlayedViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupPermissionLauncher()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentPlaylistsBinding.inflate(inflater, container, false)
playlistsAdapter = PlaylistsAdapter(this)
menuHost = requireActivity()
menuHost?.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG, "onViewCreated")
binding.playlistsFragment.visibility = View.VISIBLE
setupRecyclerView()
observePlaylistList()
checkPermissionsAndLoadDataIfNeeded()
setupMainActivityUiController()
observeRecentlyPlayedCount()
observeMostPlayedCount()
observeFavouriteClipsCount()
binding.addNewPlaylistButton.setOnClickListener {
CreatePlaylistDialog.show(requireContext()) { playlistName ->
Log.d(
TAG,
"Playlist name entered: $playlistName. Navigating to AddClipsToPlaylistFragment for new playlist."
)
val fragment =
AddClipsToPlaylistFragment.newInstance(NEW_PLAYLIST_ID_MARKER, playlistName)
parentFragmentManager.beginTransaction()
.replace(R.id.main, fragment)
.addToBackStack(null)
.commit()
}
}
binding.cardRecentlyPlayed.setOnClickListener {
val recentlyPlayedFragment = RecentlyPlayedFragment()
val fragmentManager = requireActivity().supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.apply {
addToBackStack(null)
setReorderingAllowed(true)
hide(this@PlaylistsFragment)
add(R.id.main, recentlyPlayedFragment)
commit()
}
binding.playlistsFragment.visibility = GONE
mMainActivityUiController?.hideTabLayout()
}
binding.cardMostPlayed.setOnClickListener {
val mostPlayedFragment = MostPlayedFragment()
val fragmentManager = requireActivity().supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.apply {
addToBackStack(null)
setReorderingAllowed(true)
hide(this@PlaylistsFragment)
add(R.id.main, mostPlayedFragment)
commit()
}
binding.playlistsFragment.visibility = GONE
mMainActivityUiController?.hideTabLayout()
}
binding.cardFavorites.setOnClickListener {
val favouritesClipsFragment = FavouritesClipsFragment()
val fragmentManager = requireActivity().supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.apply {
addToBackStack(null)
setReorderingAllowed(true)
hide(this@PlaylistsFragment)
add(R.id.main, favouritesClipsFragment)
commit()
}
binding.playlistsFragment.visibility = GONE
mMainActivityUiController?.hideTabLayout()
}
}
override fun onResume() {
super.onResume()
Log.d(TAG, "onResume: Fragment resumed.")
(activity as? MainActivity)?.getToolbar()?.visibility = View.VISIBLE
mMainActivityUiController?.showTabLayout()
binding.playlistsFragment.visibility = View.VISIBLE
}
override fun onDestroyView() {
super.onDestroyView()
Log.d(TAG, "onDestroyView: Cleaning up.")
menuHost?.removeMenuProvider(this)
binding.recyclerPlaylists.adapter = null
actionMode?.finish()
actionMode = null
_binding = null
}
private fun setupMainActivityUiController() {
if (activity is MainActivityUiController) {
mMainActivityUiController = activity as MainActivityUiController
}
}
private fun observePlaylistList() {
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
Log.d(TAG, "Observer: STARTED collecting playlistList StateFlow.")
playlistsViewModel.playlists.collect { resource ->
if (!isAdded || _binding == null) {
Log.w(TAG, "Observer: Fragment view null or not added. Skipping UI update.")
return@collect
}
Log.d(
TAG,
"Observer: Collected playlistList resource: ${resource::class.java.simpleName}"
)
binding.progressBar.visibility =
if (resource is Resource.Loading) View.VISIBLE else GONE
val showList = resource is Resource.Success && resource.data.isNotEmpty()
val showEmpty =
(resource is Resource.Success && resource.data.isEmpty()) || resource is Resource.Empty
binding.recyclerPlaylists.visibility =
if (showList) View.VISIBLE else View.INVISIBLE
binding.emptyView.visibility = if (showEmpty) View.VISIBLE else GONE
when (resource) {
is Resource.Success -> {
val playlists = resource.data
originalPlaylists.clear()
originalPlaylists.addAll(playlists)
playlistsAdapter.submitList(ArrayList(playlists)) {
val itemCount = playlistsAdapter.itemCount
binding.recyclerPlaylists.visibility =
if (itemCount > 0) View.VISIBLE else View.INVISIBLE
binding.emptyView.visibility =
if (itemCount == 0) View.VISIBLE else GONE
binding.emptyView.text = getString(R.string.no_playlists_found)
}
Log.d(
TAG,
"Successfully submitted ${playlists.size} playlists to adapter."
)
}
is Resource.Error -> {
binding.emptyView.text = resource.message
Log.e(TAG, "Error loading playlists: ${resource.message}")
}
is Resource.Empty -> {
playlistsAdapter.submitList(emptyList())
binding.emptyView.text = getString(R.string.no_playlists_found)
}
else -> {}
}
}
playlistsViewModel.counts.collect { resource ->
if (resource is Resource.Success) {
binding.countRecentlyAdded.text =
"${resource.data.recentlyAddedCount} الأغاني"
binding.countFavorites.text = "${resource.data.favoriteCount} الأغاني"
binding.countMostPlayed.text = "${resource.data.mostPlayedCount} الأغاني"
} else if (resource is Resource.Empty) {
binding.countRecentlyAdded.text = "0 الأغاني"
binding.countFavorites.text = "0 الأغاني"
binding.countMostPlayed.text = "0 الأغاني"
binding.countRecentlyPlayed.text = "0 الأغاني"
}
}
}
}
}
private fun customizeMenuItem(menu: Menu, itemId: Int, title: String) {
val menuItem = menu.findItem(itemId)
val spannableString = SpannableString(" $title")
val drawable =
ContextCompat.getDrawable(requireContext(), R.drawable.baseline_arrow_left_24)
drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
val imageSpan =
android.text.style.ImageSpan(drawable!!, android.text.style.ImageSpan.ALIGN_BOTTOM)
spannableString.setSpan(
imageSpan,
0,
1,
android.text.SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
)
menuItem.title = spannableString
}
override fun onPlaylistClicked(playlist: Playlist) {
Log.d(TAG, "Playlist clicked: ${playlist.name}")
=
val playlistDetailsFragment = PlaylistDetailsFragment().apply {
arguments = Bundle().apply {
putParcelable("playlist", playlist)
}
}
val fragmentManager = requireActivity().supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.apply {
addToBackStack(null)
setReorderingAllowed(true)
hide(this@PlaylistsFragment)
add(R.id.main, playlistDetailsFragment, "playlistFragment")
commit()
}
binding.playlistsFragment.visibility = GONE
mMainActivityUiController?.hideTabLayout()
}
override fun onPlaylistMoreOptionsClicked(playlist: Playlist, anchorView: View) {
showBottomSheet(playlist, anchorView)
}
override fun onItemSelectionChanged(position: Int, isSelected: Boolean) {
Log.d(TAG, "Playlist selection changed: Pos $position, isSelected=$isSelected")
if (isSelected) selectedPositions.add(position) else selectedPositions.remove(position)
updateActionModeTitle()
}
override fun onStartSelectionMode(position: Int) {
TODO("Not yet implemented")
}
@SuppressLint("MissingInflatedId")
private fun showBottomSheet(playlist: Playlist, anchorView: View) {
val bottomSheetDialog = android.app.AlertDialog.Builder(requireContext())
.setTitle(playlist.name)
.setItems(arrayOf("Play", "Play Next", "Add to Queue", "Delete")) { _, which ->
when (which) {
0 -> Toast.makeText(
requireContext(),
"Play ${playlist.name}",
Toast.LENGTH_SHORT
).show()
1 -> Toast.makeText(
requireContext(),
"Play Next ${playlist.name}",
Toast.LENGTH_SHORT
).show()
2 -> Toast.makeText(
requireContext(),
"Add to Queue ${playlist.name}",
Toast.LENGTH_SHORT
).show()
3 -> showDeletePlaylistConfirmationDialog(playlist)
}
}
.create()
bottomSheetDialog.show()
}
private fun showDeletePlaylistConfirmationDialog(playlist: Playlist) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.delete_playlist_title))
.setMessage(getString(R.string.delete_playlist_confirmation_message, playlist.name))
.setPositiveButton(R.string.delete) { _, _ ->
playlistsViewModel.deletePlaylist(playlist.id)
Toast.makeText(requireContext(), "Deleted ${playlist.name}", Toast.LENGTH_SHORT)
.show()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
class CreatePlaylistDialog {
companion object {
@JvmStatic
fun show(context: Context, onPlaylistCreated: (String) -> Unit) {
val dialog = Dialog(context)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(R.layout.dialog_create_playlist)
// Set dialog properties
dialog.window?.setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
dialog.window?.setLayout(
(context.resources.displayMetrics.widthPixels * 0.9).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT
)
val editTextPlaylistName = dialog.findViewById(R.id.editTextPlaylistName)
val btnCreatePlaylist = dialog.findViewById(R.id.btnCreatePlaylist)
val btnCancel = dialog.findViewById(R.id.btnCancel)
// Set focus and show keyboard
editTextPlaylistName.requestFocus()
val imm =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
btnCreatePlaylist.setOnClickListener {
val playlistName = editTextPlaylistName.text.toString().trim()
if (playlistName.isNotEmpty()) {
onPlaylistCreated(playlistName)
dialog.dismiss()
} else {
editTextPlaylistName.error = "يرجى إدخال اسم قائمة التشغيل"
}
}
btnCancel.setOnClickListener {
dialog.dismiss()
}
dialog.setOnDismissListener {
imm.hideSoftInputFromWindow(editTextPlaylistName.windowToken, 0)
}
dialog.show()
}
}
}
}
Что я пробовал до сих пор:
Использование replace() вместо скрыть()/add(): это приводило к наложению фрагментов друг на друга, отображая контент из нескольких фрагментов одновременно
Итак, каков правильный подход, обеспечивающий видимость фрагмента при переходе обратно из стека, не вызывая перекрытия с содержимым дочерней навигации при переходе к ним?
У меня есть приложение аудиоплеера, оно содержит [b][code]PlaylistsFragment[/code][/b], содержащий 4 карточки, ведущие к другим фрагментам (FavouritesFragment, НедавноPlayedFragment, MostPlayedFragment, НедавноДобавленныйФрагмент). Когда я перехожу к любому из этих фрагментов и нажимаю кнопку «Назад», PlaylistsFragment отображается как пустой/пустой экран. Однако, если я переключусь на другую вкладку и вернусь, фрагмент отобразится правильно [img]https://i.sstatic.net/AbbuNe8J.png[/img]
[img]https://i.sstatic.net/8QM7PbTK.png[/img]
[img]https://i.sstatic.net/8M13idhT.png[/img]
Это моя текущая реализация: [code]@AndroidEntryPoint class PlaylistsFragment : Fragment(R.layout.fragment_playlists), MenuProvider, PlaylistsAdapter.PlaylistAdapterListener {
companion object { private const val TAG = "PlaylistsFragment" const val NEW_PLAYLIST_ID_MARKER = -100L }
private var _binding: FragmentPlaylistsBinding? = null private val binding get() = _binding!!
private val playlistsViewModel by viewModels () private lateinit var playlistsAdapter: PlaylistsAdapter
private val selectedPositions: MutableSet = mutableSetOf() private var actionMode: ActionMode? = null
private var originalPlaylists = arrayListOf() private var mMainActivityUiController: MainActivityUiController? = null private var menu: Menu? = null private var sortType: PlaylistSortType = PlaylistSortType.NAME_ASC private lateinit var requestPermissionLauncher: ActivityResultLauncher private var storagePermissionGranted = false private var menuHost: MenuHost? = null
private val recentlyPlayedViewModel by viewModels() private val mostPlayedViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setupPermissionLauncher() }
Log.d( TAG, "Playlist name entered: $playlistName. Navigating to AddClipsToPlaylistFragment for new playlist." ) val fragment = AddClipsToPlaylistFragment.newInstance(NEW_PLAYLIST_ID_MARKER, playlistName) parentFragmentManager.beginTransaction() .replace(R.id.main, fragment) .addToBackStack(null) .commit() } }
binding.cardRecentlyPlayed.setOnClickListener {
val recentlyPlayedFragment = RecentlyPlayedFragment()
val fragmentManager = requireActivity().supportFragmentManager val transaction = fragmentManager.beginTransaction()
val favouritesClipsFragment = FavouritesClipsFragment() val fragmentManager = requireActivity().supportFragmentManager val transaction = fragmentManager.beginTransaction()
private fun setupMainActivityUiController() { if (activity is MainActivityUiController) { mMainActivityUiController = activity as MainActivityUiController } }
private fun observePlaylistList() { viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { Log.d(TAG, "Observer: STARTED collecting playlistList StateFlow.") playlistsViewModel.playlists.collect { resource -> if (!isAdded || _binding == null) { Log.w(TAG, "Observer: Fragment view null or not added. Skipping UI update.") return@collect } Log.d( TAG, "Observer: Collected playlistList resource: ${resource::class.java.simpleName}" )
binding.progressBar.visibility = if (resource is Resource.Loading) View.VISIBLE else GONE val showList = resource is Resource.Success && resource.data.isNotEmpty() val showEmpty = (resource is Resource.Success && resource.data.isEmpty()) || resource is Resource.Empty binding.recyclerPlaylists.visibility = if (showList) View.VISIBLE else View.INVISIBLE binding.emptyView.visibility = if (showEmpty) View.VISIBLE else GONE
when (resource) { is Resource.Success -> { val playlists = resource.data originalPlaylists.clear() originalPlaylists.addAll(playlists) playlistsAdapter.submitList(ArrayList(playlists)) { val itemCount = playlistsAdapter.itemCount binding.recyclerPlaylists.visibility = if (itemCount > 0) View.VISIBLE else View.INVISIBLE binding.emptyView.visibility = if (itemCount == 0) View.VISIBLE else GONE binding.emptyView.text = getString(R.string.no_playlists_found) } Log.d( TAG, "Successfully submitted ${playlists.size} playlists to adapter." ) }
val editTextPlaylistName = dialog.findViewById(R.id.editTextPlaylistName) val btnCreatePlaylist = dialog.findViewById(R.id.btnCreatePlaylist) val btnCancel = dialog.findViewById(R.id.btnCancel)
// Set focus and show keyboard editTextPlaylistName.requestFocus() val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
btnCreatePlaylist.setOnClickListener { val playlistName = editTextPlaylistName.text.toString().trim() if (playlistName.isNotEmpty()) { onPlaylistCreated(playlistName) dialog.dismiss() } else { editTextPlaylistName.error = "يرجى إدخال اسم قائمة التشغيل" } }
dialog.show() } } } } [/code] Что я пробовал до сих пор: [list] [*][b]Использование replace() вместо скрыть()/add()[/b]: это приводило к наложению фрагментов друг на друга, отображая контент из нескольких фрагментов одновременно
mMainActivityUiController?.hideTabLayout() } [/code] [list] [*][b]Использование BringToFront() в onResume и onHiddenChanged: представление присутствует в иерархии, но не отображается на экране[/b] [/list] [b]Фрагмент макета плейлистов[/b] [code]
[/code] Итак, каков правильный подход, обеспечивающий видимость фрагмента при переходе обратно из стека, не вызывая перекрытия с содержимым дочерней навигации при переходе к ним?