How to close current fragment in Android while using Navigation? - android

after setting listener on button I create navigationOnClickListener, which set startDestination to current fragment, after .navigate() it changes the destination to another fragment, but the view is not changed
onClickListener
navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
view.findViewById<Button>(R.id.fragment_button).setOnClickListener {
navController.navigate(R.id.Camera2VideoFragment_To_FirstFragment)
}
closeFragment()
private fun closeFragment()
{
closePreviewSession()
closeCamera()
parentFragmentManager.popBackStack()
updatePreview()
}
main.xml (navigation 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/main.xml"
app:startDestination="#id/cameraFragment"
>
<fragment
android:id="#+id/cameraFragment"
android:name="com.example.camera2videotextureview.Camera2VideoFragment"
android:label="Camera2VideoFragment"
tools:layout="#layout/fragment_camera2_video">
<action
android:id="#+id/navigateToSecondFragment"
app:destination="#id/firstFragment" />
</fragment>
<fragment
android:id="#+id/firstFragment"
android:name="com.example.camera2videotextureview.FirstFragment"
android:label="FirstFragment"
tools:layout="#layout/fragment_first" />
</navigation>
Main fragment is the camera fragment using Camera2 API. FirstFragment is just blank kotlin class where layout contains just one TextView.
EDIT: Now I have edited onClickListener, I am not able to change the current view, I need to use popBackStack on current fragment, but donot know how.

Navigation.findNavController(mActivity, R.id.nav_host_fragment).popBackStack();

I resolved my issue, which was that I have tried to implemented 3 different styles of Navigation. The whole navigation has changed in the past 4 years and I also found out that I have mixed up different versions from lower APIs with higher APIs.
I am not using closeFragment() method anymore.
Following declaration in code and the code itself has to be implemented in MainActivity:
navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
view.findViewById<Button>(R.id.fragment_button).setOnClickListener {
navController.navigate(R.id.Camera2VideoFragment_To_FirstFragment)
}
Also you have to setup correctly nav_host_fragment in xml file for MainActivity:
<androidx.fragment.app.FragmentContainerView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/main"
/>

Related

Back button always goes to home fragment, popping up dialog-fragment also [duplicate]

I am using navigation component and BottomNavigationView, i am facing a problem, that is when i go to from fragment 1>2>5>4>3 and when i press back button i get fragment 1. I know this is the default behavior but i don't want this, i want to save them in backstack so when i press back button it should go to fragment 4 not 1. I have been trying and searching but i couldn't find any solution. Can i put fragment manually into backstack in kotlin?
My code:
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
.......................
.......................>
<androidx.appcompat.widget.Toolbar
................
................/>
<fragment
android:id="#+id/app_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:navGraph="#navigation/app_nav"
..............
............../>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/app_bottom_nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#drawable/white_grey_border_bottom"
app:menu="#menu/bottom_nav_menu"
app:labelVisibilityMode="unlabeled"
...........
.........../>
</androidx.constraintlayout.widget.ConstraintLayout>
app_nav.xml
<navigation
...........
...........
android:id="#+id/app_nav"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/homeFragment"
android:name="com.example.instagram_clone.ui.HomeFragment"
android:label="HomeFragment"
tools:layout="#layout/fragment_home"/>
.............
.............
.............
.............
</navigation>
MainActivity.kt
class MainActivity : AppCompatActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val bottomNavView = binding.appBottomNavView
val navController = findNavController(R.id.app_nav_host_fragment)
bottomNavView.setupWithNavController(navController)
}
}
As per the documentation on onNavDestinationSelected() (the method that setupWithNavController() uses when selecting a MenuItem):
By default, the back stack will be popped back to the navigation graph's start destination. Menu items that have android:menuCategory="secondary" will not pop the back stack.
So just add android:menuCategory="secondary" to each of the menu items used with your BottomNavigationView.

Pop enter animation not working with the Jetpack Navigation Component

I have been using the Jetpack Navigation component (2.3.5, although I have tested older versions too) and trying to add transitions between two simple destinations. However, the transition specified with the popEnterAnim attribute doesn't run at all. Instead the re-entering fragment just immediately appears.
I have managed to recreate it with a really simple example. The fragment layouts are just a single TextView.
Main activity
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
class ActivityMain : AppCompatActivity(R.layout.main)
Fragment 1
class Fragment1 : Fragment(R.layout.fragment_1) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.setOnClickListener {
findNavController().navigate(R.id.action_fragment1_to_fragment2)
}
}
}
Fragment 2
class Fragment2: Fragment(R.layout.fragment_2)
Navigation graph
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="#+id/nav_graph.xml"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment1"
android:name="uk.henrytwist.projectsource.Fragment1"
android:label="MainFragment" >
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/transition_rise_up_enter"
app:exitAnim="#anim/transition_fade_exit"
app:popEnterAnim="#anim/transition_fade_pop_enter"
app:popExitAnim="#anim/transition_rise_up_pop_exit" />
</fragment>
<fragment
android:id="#+id/fragment2"
android:name="uk.henrytwist.projectsource.Fragment2"
android:label="Fragment2" />
</navigation>
It turns out that this was actually due to a bug in the AndroidX fragment library (androidx.fragment:fragment:1.3.2) which has now been rectified in version 1.3.3. The changelog confirms the fix:
Fixed a regression introduced in Fragment 1.3.2 which would cause popEnter animations to not run when popping a FragmentTransaction that included a setPrimaryNavFragment operation, such as those used by NavHostFragment. (I38c87, b/183877426)

Android Custom Navigation Drawer has weird backstack

I have an app similar to Android Studios Navigation Drawer Activity:
My activity uses Android Architecture Navigation Components & a navigation drawer to navigate between different fragments.
As the navigation drawer is pretty custom i can't use the usual navigation-view, but use a custom fragment hosting a LinearLayout.
Each item in that LinearLayout has an onClickListener which boils down to
navController.navigate(R.id.myCorrespondingFragment)
So far, so everything works fine.
The problem begins when navigating back:
Let's imagine i navigate from "Home"-Fragment A -> B -> C and then go back.
Android Studios example behaves correctly: C -> A -> close
My implementation doesn't: it just pops the backstack C -> B -> A -> close
How do i fix that?
Minified Main-Layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout android:id="#+id/drawerLayout" >
<LinearLayout android:orientation="vertical" >
<androidx.appcompat.widget.Toolbar android:id="#+id/toolbar" />
<fragment
android:id="#+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph"
/>
</LinearLayout>
<fragment
android:name="com.company.drawer.DrawerFragment"
android:layout_gravity="start"
/>
</androidx.drawerlayout.widget.DrawerLayout>
Navigation 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"
android:id="#+id/navigation_graph"
app:startDestination="#id/doorFragment"
>
<fragment
android:id="#+id/doorFragment"
android:name="com.company.door.DoorFragment"
android:label="#string/shared_empty"
/>
<fragment
android:id="#+id/historyFragment"
android:name="com.company.history.HistoryFragment"
android:label="#string/history"
/>
<fragment
android:id="#+id/settingsFragment"
android:name="com.company.settings.SettingsFragment"
android:label="#string/settings"
/>
</navigation>
Everything navigation related from my Main Activity's onCreate:
setSupportActionBar(toolbar)
val navController = (supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment).navController
toolbar.setupWithNavController(navController, AppBarConfiguration(
listOf(R.id.doorFragment, R.id.historyFragment, R.id.settingsFragment)
, drawerLayout
))
You can try like this
<fragment
android:id="#+id/fragmentA"
android:name="com.package.FragmentA"
android:label="Fragment A">
<action
android:id="#+id/action_fragmentA_to_fragmentB"
app:destination="#id/fragmentB"
app:popUpTo="#id/fragmentB"
app:popUpToInclusive="true / false" />
</fragment>
You can use global actions, so that you don't have to add an action for every fragment to fragment navigation but only for every fragment destination. This would look like this in XML:
<action android:id="#+id/action_global_doorFragment"
app:destination="#id/doorFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
<action android:id="#+id/action_global_historyFragment"
app:destination="#id/historyFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
<action android:id="#+id/action_global_settingsFragment"
app:destination="#id/historyFragment"
app:popUpTo="#id/#id/doorFragment"
app:restoreState="true"
app:popUpToSaveState="true" />
Note that I always used your start destination for popUpTo and also save the backstack state. You don't necessarily need the latter (restoreState and popUpToSaveState) for your use case but maybe want it later down the road when you have multiple fragments for a single drawer entry. This will get you the same behaviour as the current standard drawer integration.
Here's an easy example on how you can use the global action to go to your settings fragment. (The docs also explain how you can use global actions with the SafeArgs Gradle Plugin which is probably the better method.)
navController.navigate(R.id.action_global_settingsFragment)
If you don't want an extra action for every destination and your drawer menu items have the same id as its destination, you can also do it programmatically like this:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
navController.navigate(item.itemId, null, navOptions {
restoreState = true
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
})
return true
}

Android navigation component back Button doesn´t work [duplicate]

I use navigation components to navigate from one fragment to another. However, when the user press the back button, I want to navigate back to first fragment. But it keep showing the second fragment. This is my 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/fragment1">
<fragment
android:id="#+id/fragment2"
android:name="com.myapp.ui.fragments.Fragment2"
android:label="fragment_2" />
<fragment
android:id="#+id/fragment1"
android:name="com.myapp.ui.fragments.Fragment1"
android:label="fragment_1">
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/fragment_fade_enter"
app:exitAnim="#anim/fragment_fade_exit"
app:popUpTo="#id/fragment1" />
</fragment>
</navigation>
And this is how I trigger the navigation in the code of my Fragment1-Class:
viewModel.itemSelected.observe(viewLifecycleOwner) {
navigate(it)
}
....
fun navigate(id: Long){
val bundle = Bundle()
bundle.putLong("itemid", id)
getNavController().navigate(R.id.action_fragment1_to_fragment2, bundle)
}
Edit:
Corrected startDestination in XML.
Edit2:
Added more code.
You're using a LiveData for an event. LiveData always caches the set value, so when you return to your Fragment1, you observe the LiveData again and get the same value a second time, causing you to navigate() yet again.
See this blog post for more information and alternatives.

How to manage BottomNavigationView backstack in Android Navigation Component

I am using navigation component and BottomNavigationView, i am facing a problem, that is when i go to from fragment 1>2>5>4>3 and when i press back button i get fragment 1. I know this is the default behavior but i don't want this, i want to save them in backstack so when i press back button it should go to fragment 4 not 1. I have been trying and searching but i couldn't find any solution. Can i put fragment manually into backstack in kotlin?
My code:
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
.......................
.......................>
<androidx.appcompat.widget.Toolbar
................
................/>
<fragment
android:id="#+id/app_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:navGraph="#navigation/app_nav"
..............
............../>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/app_bottom_nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#drawable/white_grey_border_bottom"
app:menu="#menu/bottom_nav_menu"
app:labelVisibilityMode="unlabeled"
...........
.........../>
</androidx.constraintlayout.widget.ConstraintLayout>
app_nav.xml
<navigation
...........
...........
android:id="#+id/app_nav"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/homeFragment"
android:name="com.example.instagram_clone.ui.HomeFragment"
android:label="HomeFragment"
tools:layout="#layout/fragment_home"/>
.............
.............
.............
.............
</navigation>
MainActivity.kt
class MainActivity : AppCompatActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val bottomNavView = binding.appBottomNavView
val navController = findNavController(R.id.app_nav_host_fragment)
bottomNavView.setupWithNavController(navController)
}
}
As per the documentation on onNavDestinationSelected() (the method that setupWithNavController() uses when selecting a MenuItem):
By default, the back stack will be popped back to the navigation graph's start destination. Menu items that have android:menuCategory="secondary" will not pop the back stack.
So just add android:menuCategory="secondary" to each of the menu items used with your BottomNavigationView.

Categories

Resources