How to get to the previous fragment using jetpack navigation in kotlin?

Viewed 209

I'm making an app with two fragments, which are home-fragment and youtube-fragment. When I press the "search" button, I can get to youtube-fragment properly. However, when I press the back button(the back on my phone instead of the back arrow in the toolbar), I just exit the app without getting any error or warning. I would like to get back to the home-fragment after press the back button.

Here is my code of both fragments:

homeFragment

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)
    }
}

youtubeFragment

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()
    }
}

I guess it may have something to do with my adapter for the recyclerview in youtube-fragment because I provide the lifecycle as a parameter for it.

Here it is:

VideoAdapter

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)
                    }
                }
            })
        }
    }
}

Does anyone know how I can get to the home fragment instead of leaving the app?

1 Answers

When you add app:popUpTo="@id/homeFragment", app:popUpToInclusive="true" to your navigation action, this means you want to remove the homeFragment from back stack. essentially meaning when you press back, homeFragment will no longer be there, if you want back button to take you to homeFragment, you need to update the action as

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