Problem mit der Fragmentsichtbarkeit: PlaylistsFragment zeigt nach dem Zurücknavigieren von untergeordneten Fragmenten eAndroid

Forum für diejenigen, die für Android programmieren
Anonymous
 Problem mit der Fragmentsichtbarkeit: PlaylistsFragment zeigt nach dem Zurücknavigieren von untergeordneten Fragmenten e

Post by Anonymous »

Ich habe eine Audio-Player-App, sie enthält ein

Code: Select all

PlaylistsFragment
[/b], der 4 Karten enthält, die zu anderen Fragmenten navigieren (FavouritesFragment, LatestPlayedFragment, MostPlayedFragment, LatestAddedFragment). Wenn ich zu einem dieser Fragmente navigiere und die Zurück-Taste drücke, erscheint das PlaylistsFragment als leerer/leerer Bildschirm. Wenn ich jedoch zu einer anderen Registerkarte wechsle und zurückkomme, wird das Fragment korrekt angezeigt
Image

Image

Image

Das ist meine aktuelle Implementierung:

Code: Select all

@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()
}
}
}
}
Was ich bisher versucht habe:
  • Verwenden von replace() anstelle von hide()/add(): Dadurch überlappten sich Fragmente und zeigten Inhalte aus mehreren Fragmenten gleichzeitig an
  • Hinzufügen eines Rückrufs von onHiddenChanged():

Code: Select all

override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (!hidden) {
_binding?.let {
it.playlistsFragment.visibility = View.VISIBLE
it.root.bringToFront()
}
(activity as? MainActivity)?.getToolbar()?.visibility = View.VISIBLE
mMainActivityUiController?.showTabLayout()
}
}
  • Alte Fragmente vor der Navigation manuell entfernen:

Code: Select all

private fun navigateToFragment(fragment: Fragment) {
val fragmentManager = requireActivity().supportFragmentManager

fragmentManager.fragments.forEach { existingFragment ->
if (existingFragment != this &&
existingFragment !is PlaylistsFragment &&
existingFragment.isAdded) {
fragmentManager.beginTransaction()
.remove(existingFragment)
.commitNow()
}
}

fragmentManager.beginTransaction().apply {
setReorderingAllowed(true)
hide(this@PlaylistsFragment)
add(R.id.main, fragment)
addToBackStack(null)
commit()
}

mMainActivityUiController?.hideTabLayout()
}
  • Verwendung von bringToFront() in onResume und onHiddenChanged: Die Ansicht scheint in der Hierarchie vorhanden zu sein, aber auf dem Bildschirm nicht sichtbar
Das Layout des Playlist-Fragments

Code: Select all






android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />













































































Was ist also der richtige Ansatz, um sicherzustellen, dass das Fragment beim Zurücknavigieren vom Backstack sichtbar ist, ohne dass es beim Navigieren zu ihnen zu Überschneidungen mit dem Inhalt der untergeordneten Navigation kommt?

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post