I'm using Android Navigation component with a navigation drawer (as in Android Studio template). I have fragment A, B, C as top level fragments which are used in navigation drawer and fragment Z which is connected with fragment A in navigation graph. Now I have a button in fragment A. Clicking the button will open the fragment Z using Safe args.
binding.button.setOnClickListener {
val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
it.findNavController().navigate(action)
}
When the fragment Z is opened, the app bar icon will automatically change to back button which will allow me to go back to fragment A.
These are working fine, but this issue is, when I use the same safe args code in a live data obsedrver, the back button is not working.
viewModel.actionNewsDetails.observe(viewLifecycleOwner, {
val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
findNavController().navigate(action)
})
Here are some additional details
I've been struggling with this issue for long. Sorry for my bad English.
The next information is very important :
When I clicked the back button fast for several time, I noticed the app bar title flickering (changing between fragment A and Z)
I am pretty sure what happens is that, your back button in fragment Z is working correctly, your fragment A displays and it's liveData get triggered again and navigate once again to fragment Z. This happens very fast but as you indicate, when you do it very fast you can see the delay.
Solution: before navigate to fragment Z in your LiveData observer, change the value of the liveData so when you go back to fragment A it doesn't trigger again.
This problem made me lose one hour some weeks ago.
Edit 26/10/2020:
To solve that problem, implement the SingleLiveEvent class and use it instead the MutableLiveData.
SingleLiveEvent.class
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private static final String TAG = "SingleLiveEvent";
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(LifecycleOwner owner, final Observer<? super T> observer) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
Kotlin Version
class SingleLiveEvent<T> : MutableLiveData<T>() {
val TAG: String = "SingleLiveEvent"
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(TAG,"Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, Observer<T> { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(@Nullable t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With