Проблема с видимостью фрагмента: PlaylistsFragment показывает пустой экран после перехода обратно от дочерних фрагментовAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Проблема с видимостью фрагмента: PlaylistsFragment показывает пустой экран после перехода обратно от дочерних фрагментов

Сообщение Anonymous »

У меня есть приложение аудиоплеера, оно содержит

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

PlaylistsFragment
[/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(): это приводило к наложению фрагментов друг на друга, отображая контент из нескольких фрагментов одновременно
  • Добавление обратного вызова onHiddenChanged():

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

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()
}
}
  • Удаление старых фрагментов перед навигацией вручную:

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

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()
}
  • Использование BringToFront() в onResume и onHiddenChanged: представление присутствует в иерархии, но не отображается на экране
Фрагмент макета плейлистов

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






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













































































Итак, каков правильный подход, обеспечивающий видимость фрагмента при переходе обратно из стека, не вызывая перекрытия с содержимым дочерней навигации при переходе к ним?


Подробнее здесь: https://stackoverflow.com/questions/798 ... navigating
Ответить

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

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

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

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

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