Как добраться до предыдущего фрагмента с помощью навигации по реактивному ранцу в котлине?

#android #android-studio #kotlin #android-fragments #android-recyclerview

Вопрос:

Я создаю приложение с двумя фрагментами, которые являются домашним фрагментом и фрагментом youtube. Когда я нажимаю кнопку «поиск», я могу правильно перейти на youtube-фрагмент. Однако, когда я нажимаю кнопку «Назад» («Назад» на моем телефоне вместо стрелки «Назад» на панели инструментов), я просто выхожу из приложения, не получая никаких ошибок или предупреждений. Я хотел бы вернуться к главному фрагменту после нажатия кнопки «Назад».

Вот мой код обоих фрагментов:

Фрагмент домашней страницы

 package com.example.signlanguage_new

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.GridLayout
import android.widget.Toast
import android.widget.Toast.LENGTH_SHORT
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.example.signlanguage_new.databinding.FragmentHomeBinding

class HomeFragment : Fragment() {
    private val viewModel: MyViewModel by activityViewModels {
        MyViewModelFactory(
            (activity?.application as SignLanguageApplication).database.VideoDao()
        )
    }
    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.result.observe(viewLifecycleOwner)
        {
            if(it==null) {
                Toast.makeText(requireContext(), "查無資料", LENGTH_SHORT).show()
            }
            else
            {
                val action=HomeFragmentDirections.actionHomeFragmentToYoutubeFragment()
                findNavController().navigate(action)
            }
        }
        super.onViewCreated(view, savedInstanceState)
        binding.apply {
            searchBtn.setOnClickListener {
                val search=binding.search.text.toString()
                if(search=="")
                {
                    Toast.makeText(requireContext(), "請輸入關鍵字", LENGTH_SHORT).show()
                }
                else
                {
                    viewModel.searchVideo(search)
                }
            }
        }
        val adapter=homeButtonAdapter{
            viewModel.category(it)
        }
        binding.recyclerView.adapter=adapter
        binding.recyclerView.layoutManager=GridLayoutManager(this.context,2)
    }
}
 

Фрагмент YouTube

 package com.example.signlanguage_new

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.signlanguage_new.data.Video
import com.example.signlanguage_new.databinding.FragmentYoutubeBinding

class youtubeFragment : Fragment() {
    private val viewModel: MyViewModel by activityViewModels {
        MyViewModelFactory(
            (activity?.application as SignLanguageApplication).database.VideoDao()
        )
    }
    private var _binding: FragmentYoutubeBinding? = null
    private val binding get() = _binding!!
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentYoutubeBinding.inflate(inflater, container, false)
        return binding.root
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val Dataset= viewModel.result.value
        binding.recyclerView.adapter= Dataset?.let {
            VideoAdapter(requireContext(),
                it,this.lifecycle)
        }
        binding.recyclerView.layoutManager = LinearLayoutManager(this.context)
    }
}
 

And here is the nav_graph and my mainActivity:

nav_graph

 <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@ id/nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@ id/homeFragment"
        android:name="com.example.signlanguage_new.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@ id/action_homeFragment_to_youtubeFragment"
            app:destination="@id/youtubeFragment"
            app:popUpTo="@id/homeFragment"
            app:popUpToInclusive="true" />
    </fragment>
    <fragment
        android:id="@ id/youtubeFragment"
        android:name="com.example.signlanguage_new.youtubeFragment"
        android:label="fragment_youtube"
        tools:layout="@layout/fragment_youtube" />
</navigation>
 

MainActivity

 package com.example.signlanguage_new

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.KeyEvent
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI.setupActionBarWithNavController

class MainActivity : AppCompatActivity(R.layout.activity_main) {

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Retrieve NavController from the NavHostFragment
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController
        // Set up the action bar for use with the NavController
        setupActionBarWithNavController(this, navController)
    }

    /**
     * Handle navigation when the user chooses Up from the action bar.
     */
    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}
 

Я предполагаю, что это может быть как-то связано с моим адаптером для recyclerview в youtube-фрагменте, потому что я предоставляю жизненный цикл в качестве параметра для него.

Вот он:

ВидеоАдаптер

 package com.example.signlanguage_new

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.signlanguage_new.data.Video
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView

class VideoAdapter (private val context: Context, private val dataset:List<Video>, private val lifecycle: Lifecycle) :RecyclerView.Adapter<VideoAdapter.ItemViewHolder>(){
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {

        val youTubePlayerView: YouTubePlayerView = LayoutInflater.from(parent.context).inflate(R.layout.videolistitem,parent,false) as YouTubePlayerView
        lifecycle.addObserver(youTubePlayerView)
        return ItemViewHolder(youTubePlayerView)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val current=dataset[position]
        holder.setIsRecyclable(false)
        holder.loadVideo(current.ytId)
    }
    
    override fun getItemCount(): Int {
        return dataset.size
    }
    class ItemViewHolder(playerView: YouTubePlayerView) :
        RecyclerView.ViewHolder(playerView) {
        val youTubePlayerView: YouTubePlayerView = playerView
        var youTubePlayer: YouTubePlayer? = null
        var currentVideoId: String? = null
        
        fun loadVideo(videoId: String?) {
            currentVideoId = videoId
            if (youTubePlayer == null) return
            if (videoId != null) {
                youTubePlayer!!.cueVideo(videoId, 0F)
            }
        }
        init {
            youTubePlayerView.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
                override fun onReady(initializedYouTubePlayer: YouTubePlayer) {
                    youTubePlayer = initializedYouTubePlayer
                    currentVideoId?.let {
                        youTubePlayer!!.cueVideo(it, 0F)
                    }
                }
            })
        }
    }
}
 

Кто-нибудь знает, как я могу добраться до домашнего фрагмента вместо того, чтобы выходить из приложения?

Комментарии:

1. Зачем вы используете app:popUpTo="@id/homeFragment" , если не хотите удалять (всплывать) homeFragment из заднего стека?

Ответ №1:

Когда вы добавляете app:popUpTo="@id/homeFragment", app:popUpToInclusive="true" в свое действие навигации, это означает, что вы хотите удалить homeFragment из заднего стека. по сути, это означает, что когда вы нажмете «Назад», homeFragment вас там больше не будет, если вы хотите, чтобы кнопка «Назад» привела вас туда homeFragment , вам нужно обновить действие как

 <action
      android:id="@ id/action_homeFragment_to_youtubeFragment"
      app:destination="@id/youtubeFragment" />