简体   繁体   English

从 LiveData 观察者调用时,导航组件的默认后台堆栈不起作用

[英]Navigation Component default backstack not working when called from a LiveData observer

I'm using Android Navigation component with a navigation drawer (as in Android Studio template).我正在使用带有导航抽屉的 Android 导航组件(如在 Android Studio 模板中)。 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.我将片段 A、B、C 作为顶级片段,用于导航抽屉中,片段 Z 与导航图中的片段 A 相连。 Now I have a button in fragment A. Clicking the button will open the fragment Z using Safe args.现在我在片段 A 中有一个按钮。单击该按钮将使用安全参数打开片段 Z。

    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.打开片段 Z 时,应用栏图标会自动变为后退按钮,这将允许我返回片段 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.这些工作正常,但这个问题是,当我在实时数据 obsedrver 中使用相同的安全参数代码时,后退按钮不起作用。

    viewModel.actionNewsDetails.observe(viewLifecycleOwner, {
        val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
        findNavController().navigate(action)
    })

Here are some additional details这里有一些额外的细节

  • Once we are in fragment Z, it will show back navigation as usual, but just clicking it will not do any action一旦我们在片段 Z 中,它会像往常一样显示返回导航,但只是单击它不会执行任何操作
  • When I clicked the back button fast for several time, I noticed the app bar title flickering (changing between fragment A and Z)当我快速点击后退按钮几次时,我注意到应用栏标题闪烁(在片段 A 和 Z 之间变化)
  • I'm able to open Nav drawer by swiping when I'm in fragment Z当我在片段 Z 中时,我可以通过滑动打开导航抽屉
  • My live data code is written in fragment A's onCreateView()我的实时数据代码写在片段 A 的 onCreateView()
  • The live data is triggered from a function in ViewModel实时数据由 ViewModel 中的函数触发

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)当我快速点击后退按钮几次时,我注意到应用栏标题闪烁(在片段 A 和 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.我很确定会发生什么,片段 Z 中的后退按钮工作正常,片段 A 显示并且它的 liveData 再次被触发并再次导航到片段 Z。这发生得非常快,但正如你所指出的,当你这样做时非常快,您可以看到延迟。

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.解决方案:在您的 LiveData 观察器中导航到片段 Z 之前,更改 liveData 的值,以便当您返回片段 A 时,它不会再次触发。

This problem made me lose one hour some weeks ago.几周前,这个问题让我损失了一个小时。

Edit 26/10/2020 : 26/10/2020 编辑

To solve that problem, implement the SingleLiveEvent class and use it instead the MutableLiveData .要解决该问题,请实现SingleLiveEvent类并使用它代替MutableLiveData

SingleLiveEvent.class 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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM