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

相关推荐
IAM四十二2 天前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
Wgllss3 天前
那些大厂架构师是怎样封装网络请求的?
android·架构·android jetpack
x02420 天前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton23 天前
深入理解观察者模式
android·kotlin·android jetpack
Wgllss23 天前
花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
android·性能优化·android jetpack
上官阳阳1 个月前
使用Compose创造有趣的动画:使用Compose共享元素
android·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-15.属性变化控制Service
android·android studio·android jetpack
IAM四十二1 个月前
Android Jetpack Core
android·android studio·android jetpack
王能1 个月前
Kotlin真·全平台——Kotlin Compose Multiplatform Mobile(kotlin跨平台方案、KMP、KMM)
android·ios·kotlin·web·android jetpack·kmp·kmm
alexhilton1 个月前
让Activity更加优雅地跳转
android·kotlin·android jetpack