The animation when I am going from a fragment to an activity is working fine but when I click back it returns without the custom animation I insert. The same if I make the navigation from a fragment to another with the same animations works fine. Here is the action code I am using:
<action
android:id="#+id/toTicker"
app:destination="#id/tickerActivity"
app:enterAnim="#anim/slide_bottom_up"
app:exitAnim="#anim/slide_up_bottom"
app:popEnterAnim="#anim/slide_bottom_up"
app:popExitAnim="#anim/slide_up_bottom"/>
As per this issue, you need to call the static ActivityNavigator.applyPopAnimationsToPendingTransition() method in your other activity to get the pop animations to apply - it should be called directly following when you call finish() or as part of callbacks to onBackPressed() (which internally will call finish()):
override fun onBackPressed() {
super.onBackPressed()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
Updating the documentation to specifically call this out is being tracked in this documentation issue.
The previously selected answer doesn't work anymore.
applyPopAnimationsToPendingTransition must be called by overriding Activity.finish().
override fun finish() {
super.finish()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
Related
I have three fragments A, B and C. And I'm using navHostFragment container in MainActivity. So the application goes from A -> B using kotlin extension function findNavController().navigate... and then go from B to C using same function. All works fine till here.
Now in Fragment C, I'm replacing different elements on fragment C using
activity?.supportFragmentManager
?.beginTransaction()
?.replace(R.id.list_container, someFragment)
?.addToBackStack("some_frag_id")
?.commit()
The list_container is replaced with someFragment. After this when I press physical back button Fragment C pops out and my app goes to Fragment B while what I expect it to restore replaced list_container i.e. whatever was there before replacement.
I'm also overiding this in my MainActivity
override fun onBackPressed() {
val count = supportFragmentManager.backStackEntryCount
if (count == 0) {
super.onBackPressed()
//additional code
}
else {
supportFragmentManager.popBackStack()
}
}
I'm not sure what is missing here. I have read a lot of solutions on stackoverflow but none worked to my satisfaction. Please guide.
If you are adding a Fragment to a View within a Fragment, you must always use the childFragmentManager - using activity?.supportFragmentManager is always the wrong FragmentManager to use in that case.
Besides fixing cases with restoring state (which would not work when using the wrong FragmentManager), this also ensures that the default behavior for dispatching onBackPressed() down the FragmentManager hierarchy will work out the box - you should not need any logic at all in onBackPressed() to have the pop work correctly.
If you need to intercept the back button in Fragment C, you should follow the providing custom back documentation to register an OnBackPressedDispatcher - you should not override onBackPressed() even in those cases.
i am using
navController.navigate(R.id.FragmentB)
to navigate from host fragment in MainActivity to navigate to different fragments and it works like expected except inside onActivityResult it won't respond ,i am returning an ID from other activity and want to navigate to Fragments depending on that ID but it is not responding ,
If you're not using coroutines, a simple Handler() will do the trick.
Handler().post {
navigate()
}
It turns out that i needed to create a Coroutine and run my navigation function on it , i tried with :
GlobalScope.launch(Dispatchers.Main) {
navigate()
}
and it works fine, obviously gotta need to optimise my coroutine ,but that was the main issue
I have 2 fragment call CreateRoomFragment and DisplayPhotoFragment,the navigation graph is look like this:
<navigation>
<fragment
android:id="#+id/createRoomFragment"
android:name="package.room.CreateRoomFragment"
android:label="Create a room"
tools:layout="#layout/fragment_create_room">
<action
android:id="#+id/action_createRoomFragment_to_roomFragment"
app:destination="#id/roomFragment" />
<action
android:id="#+id/action_createRoomFragment_to_displayPhotoFragment"
app:destination="#id/displayPhotoFragment" />
</fragment>
<fragment
android:id="#+id/displayPhotoFragment"
android:name="package.fragment.DisplayPhotoFragment"
android:label="fragment_display_photo"
tools:layout="#layout/fragment_display_photo" >
<argument android:name="bitmap"
app:argType="android.graphics.Bitmap"/>
</fragment>
So when I wanna to move from CreateRoomFragment to DisplayPhotoFragment,I use the do as below:
NavDirections action = CreateRoomFragmentDirections.actionCreateRoomFragmentToDisplayPhotoFragment(selectedPhoto);
Navigation.findNavController(view).navigate(action);
Doing this,I can navigate to DisplayPhotoFragment.
But when I press back button of the device and also the Back arrow from the toolbar,it cant go back to CreateRoomFragment.
I tried this,but still unable to back to previous fragment:
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(),
new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
navController.navigateUp(); //I tried this
navController.popBackStack(R.id.createRoomFragment,false); //and also this
}
});
Main Problem now:
By using the code above,the screen didnt go back to previous Fragment(CreateRoomFragment).It still stuck in DisplayPhotoFragment,but at the same time,an API method in CreateRoomFragment onViewCreated section is being called.
What causing this? and how can I solve this problem?
I had the same problem. For me the issue was that I was using a LiveData boolean to decide when to go to the next fragment. When I then navigated back/up the boolean was still true so it would automatically navigate forward again.
Android maintains a back stack that contains the destinations you've visited. The first destination of your app is placed on the stack when the user opens the app. Each call to the navigate() method puts another destination on top of the stack. Tapping Up or Back calls the NavController.navigateUp() and NavController.popBackStack() methods, respectively, to remove (or pop) the top destination off of the stack.
NavController.popBackStack() returns a boolean indicating whether it successfully popped back to another destination. The most common case when this returns false is when you manually pop the start destination of your graph.
When the method returns false, NavController.getCurrentDestination() returns null. You are responsible for either navigating to a new destination or handling the pop by calling finish() on your Activity.
When navigating using an action, you can optionally pop additional destinations off of the back stack by using popUpTo and popUpToInclusive parameter of the action.
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (true == conditionForCustomAction) {
CustomActionHere()
} else NavHostFragment.findNavController(this#MyFragment).navigateUp();
}
}
requireActivity().onBackPressedDispatcher.addCallback(
this, onBackPressedCallback
)
...
}
The best solution for handling navigation using live data is to use the SingleLiveEvent.
You can always use this class which is an extension of MutableLiveData.
https://gist.githubusercontent.com/cdmunoz/ebe5c4104dadc2a461f512ea1ca71495/raw/a17f76754f86a4c0b1a6b43f5c6e6d179535e627/SingleLiveEvent.kt
For a detail run down of this check:
https://proandroiddev.com/singleliveevent-to-help-you-work-with-livedata-and-events-5ac519989c70
Had a similar issue. We still have multiple activities with nav component.
So imagine activity A -> activity B, activity B has its own nav and fragments. When the initial fragment tries to pop the back stack there is nowhere to pop back to and the nav controller does not know to finish the activity. So one solution I found was to do
if (!findNavController().popBackStack()) activity?.finish()
If nav controller can not pop back it will finish activity.
You can use MutableSharedFlow instead on MutableLiveData if you want to observe the Event only once.
in your viewModel:
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow() // read-only public view
suspend fun postEvent() {
_events.emit(event) // suspends until subscribers receive it
}
In your Activity/Fragment class:
lifecycleScope.launchWhenStarted {
viewModel.events.collect {
}
}
viewModel.postEvent()
This will prevent observing data continuously when going back to fragment.
Current situation:
There is only one activity called MainActivity.
MainActivity maintains the NavHostFragment.
There is a WebViewFragment which embeded a WebView inside.
Thera are many other fragment controlled by NavHostFragment.
What troubles me:
I can forward web pages inside WebView, but when I press back to go back inside the WebView, the NavController handle's it, pop the fragment away.
Since the WebView is inside a fragment not an activity, which means there is not method called onBackPressed() to override.
Reference:
See NavigationBasicSample#github
See Getting start with Navigation#android developer home
As far as I understand, you need a way to handle onBackPressed on your fragment, right? With NavigationComponent you can do it like this, in your onViewCreated:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
findNavController().popBackStack(R.id.dest, false)
}
}
})
add a new fragment and inside that place your WebView ,using navigation editor navigate from WebViewFragment to new fragment ,in the right hand side of navigation editor you can see an option to backstack option to manage .
code lab has example https://codelabs.developers.google.com/handling-gesture-back-navigation#5 you need onBackPressedDispatcher which need to be enabled and disabled.
you can use mWeb.goBack(); to goBack to the previous page or for returning back.
I want to implement the onbackpressed() in android and my code is as follows
public void backpressed(){
NDListeningFragment fragment1=(NDListeningFragment)getSupportFragmentManager().findFragmentByTag(ConnectedDevicesFragment.TAG);
if(fragment1!=null && fragment1.isVisible())
{
super.onBackPressed();
}
else
{
fragment1=(NDListeningFragment)SimpleFragmentFactory.createFragment(ConnectedDevicesFragment.TAG);
getSupportFragmentManager().beginTransaction().replace(R.id.content,fragment1).commit();
fragment1.setUserVisibleHint(true);
}
}
The above code checks if the visible fragment is ConnectedDevicesFragment. If yes then super() is called and if not then I create ConnectedDevicesFragment and replace it in the framelayout.
But I am not able to implement in this way. When I press back button it reloads the Connected DevicesFragment again and again.
can you help with some workaround.
Cheers!
You creates fragment1 object every time in the onBackPressed function it means it will not null and it is on invisible state. You need to add NDListeningFragment in backstack when you open NDListeningFragment first and check Is the fragment available in back stack. If yes then call super.onBackpressed.