BottomNavigationView和Navigation点击闪烁问题

Navigation配合BottomNavigationView作Home页面,当点击底部同一导航按钮,界面会发生闪烁,问题原因在于:

在Navigation框架中,每次Fragment的navigate跳转,都是采用replace方法,这将导致界面闪烁。

源代码如下:FragmentNavigator.class

我们期望的是,如果目标页面跟当前页面一致,就不加载新的页面实例。这也是我们今天所要解决的问题。

这种解决办法有很多:

一、从BottomNavigationView入手

包括以下2种方案: 1.1、设置OnNavigationItemReselectedListener监听 从源代码入手分析:

java 复制代码
//代码在BottomNavigationView的构造方法中
this.menu.setCallback(
new MenuBuilder.Callback() {
    @Override
    public boolean onMenuItemSelected(MenuBuilder menu, @NonNull MenuItem item) {
        if (reselectedListener != null && item.getItemId() == getSelectedItemId()) {
            reselectedListener.onNavigationItemReselected(item);
            return true; // item is already selected
        }
        return selectedListener != null && !selectedListener.onNavigationItemSelected(item);
    }

    @Override
    public void onMenuModeChange(MenuBuilder menu) {}
});

这里可以发现,只要让reselectedListener不等于空,重复点击BottomNavigationView时,事件将不会传递给selectedListeneronNavigationItemSelected方法,也就不会让Navigation发生跳转。 这是我最推荐的方案,代码入侵最小:

kotlin 复制代码
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    mNavTab = findViewById(R.id.nav_tab)
    controller = findNavController(R.id.nav_host)

    mNavTab.setupWithNavController(controller)
    //加这一句
    mNavTab.setOnNavigationItemReselectedListener { }
}

1.2、重写setOnNavigationItemSelectedListener方法 仿造NavigationUI.setupWithNavController(bottomNavigationView, navController)方法,在OnNavigationItemSelectedListener监听事件中,增加一个判断逻辑。

二、配合ViewModel使用

这种方式是Google所推崇的,它能保证Fragment被销毁后,数据依然有效,只要将数据展示在新的Fragment上,就能让用户觉得一切都未改变。虽然这种方式,能够解决数据重复加载的问题,但界面闪烁,列表当前位置丢失等问题,也没有实质性的改变。

三、修改FragmentNavigator

这种方式又分两种:

3.1、重写navigate方法

将replace修改成hide、show方法,实现过程较为复杂。这里就不详细展开了。

3.2、重写instantiateFragment方法

如果当前Fragment跟目标Fragment类型一致,就不创建新的实例,而是直接返回当前Fragment。

实现过程如下: 1、主布局中去掉graphId,因为我们后续会手动添加

2、创建FixedFragmentNavigator类

java 复制代码
@Navigator.Name("fragment")
public class FixedFragmentNavigator extends FragmentNavigator {
    private final int mContainerId;

    public FixedFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager, int containerId) {
        super(context, manager, containerId);
        mContainerId = containerId;
    }

    @NonNull
    @Override
    public Fragment instantiateFragment(@NonNull Context context, @NonNull FragmentManager fragmentManager, @NonNull String className, @Nullable Bundle args) {
        Fragment fragment = fragmentManager.findFragmentById(mContainerId);
        if (fragment != null) {
            String name = fragment.getClass().getName();
            if (className.equals(name)) {
                return fragment;
            }
        }
        return super.instantiateFragment(context, fragmentManager, className, args);
    }
}

3、将FixedFragmentNavigator替换掉默认的FragmentNavigator,再设置navGraph。

四、修改FragmentFactory

这种方法一般是用来配合Dagger2或Hilt实现依赖注解,其原理是将Fragment创建交到自己手里。 修改代码如下:

java 复制代码
public class FixNavHostFragment extends NavHostFragment {

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        FragmentManager childFragmentManager = getChildFragmentManager();
        childFragmentManager.setFragmentFactory(new ReuseFragmentFactory(childFragmentManager));
    }


    static class ReuseFragmentFactory extends FragmentFactory {
        private final FragmentManager mFragmentManager;

        ReuseFragmentFactory(FragmentManager fragmentManager) {
            this.mFragmentManager = fragmentManager;
        }

        @NonNull
        @Override
        public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
            Fragment primaryFragment = mFragmentManager.getPrimaryNavigationFragment();
            if (primaryFragment != null && className.equals(primaryFragment.getClass().getName())) {
                return primaryFragment;
            }
            return super.instantiate(classLoader, className);
        }
    }
}

然后在布局文件中,将NavHostFragment替换为FixNavHostFragment即可。

相关推荐
我命由我1234512 小时前
MQTT - Android MQTT 编码实战(MQTT 客户端创建、MQTT 客户端事件、MQTT 客户端连接配置、MQTT 客户端主题)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
前行的小黑炭20 小时前
Android LiveData源码分析:为什么他刷新数据比Handler好,能更节省资源,解决内存泄漏的隐患;
android·kotlin·android jetpack
_一条咸鱼_21 小时前
深度剖析:Java PriorityQueue 使用原理大揭秘
android·面试·android jetpack
_一条咸鱼_1 天前
揭秘 Java PriorityBlockingQueue:从源码洞悉其使用原理
android·面试·android jetpack
_一条咸鱼_1 天前
深度揭秘:Java LinkedList 源码级使用原理剖析
android·面试·android jetpack
_一条咸鱼_1 天前
深入剖析 Java LinkedBlockingQueue:源码级别的全面解读
android·面试·android jetpack
_一条咸鱼_1 天前
探秘 Java DelayQueue:源码级剖析其使用原理
android·面试·android jetpack
_一条咸鱼_1 天前
揭秘 Java ArrayDeque:从源码到原理的深度剖析
android·面试·android jetpack
_一条咸鱼_1 天前
深入剖析!Android WebView使用原理全解析:从源码底层到实战应用
android·面试·android jetpack
_一条咸鱼_1 天前
深度剖析 Java ArrayBlockingQueue:源码级的原理探秘
android·面试·android jetpack