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即可。

相关推荐
darryrzhong10 分钟前
FluxImageLoader : 基于Coil3封装的 Android 图片加载库,旨在提供简单、高效且功能丰富的图片加载解决方案
android·github·android jetpack
我命由我123451 小时前
Android 开发问题:在无法直接获取或者通过传递获取 Context 的地方如何获取 Context
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
儿歌八万首6 小时前
Jetpack Compose 实战:实现手势缩放图片 (Zoomable Image) 组件
kotlin·android jetpack
QING61813 小时前
Kotlin Flow 去重 (distinctUntilChanged) 详解
kotlin·android studio·android jetpack
QING61813 小时前
Kotlin Flow 节流 (Throttle) 详解
android·kotlin·android jetpack
Nerve1 天前
FluxImageLoader : 基于Coil3封装的 Android 图片加载库,旨在提供简单、高效且功能丰富的图片加载解决方案
android·android jetpack
QING6181 天前
Kotlin Flow 防抖(Debounce)详解
android·kotlin·android jetpack
QING6181 天前
Kotlin Flow 防抖(Debounce)、节流(Throttle)、去重(distinctUntilChanged) —— 新手指南
android·kotlin·android jetpack
TeleostNaCl2 天前
使用 Android Jetpack 中的 Startup 组件快速实现组件初始化逻辑与主模块解耦
android·经验分享·android jetpack·androidx·android runtime·jetpack android
未来猫咪花3 天前
LiveData "数据倒灌":一个流行的错误概念
android·android jetpack