每次单击底部导航选项卡时,防止片段加载数据

Prevent fragment from loading data every time on bottomnavigation tab click

我正在使用带有 5 个选项卡的 BottomNavigationView。最初,我在应用程序启动后加载第一个选项卡。当我单击任何其他选项卡时,会进行 API 调用并加载数据。现在,当我再次单击上一个选项卡(在启动时加载)时,会调用 API 以再次加载相同的数据。

我不想再次加载数据,并且想在没有新 API 调用的情况下显示具有相同数据的片段。

我的代码:

public class MainActivity extends AppCompatActivity implements NavigationBarView.OnItemSelectedListener {

    private BottomNavigationView bottomNavigationView;
    public static ViewPager2 viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewPager = findViewById(R.id.viewpager);
        bottomNavigationView = findViewById(R.id.navigation);

        bottomNavigationView.setOnItemSelectedListener(this);

        getTabs();
    }

    public void getTabs() {

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentAdapterMain adapter = new FragmentAdapterMain(fragmentManager, getLifecycle());
        viewPager.setAdapter(adapter);

        viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                switch (position) {
                    case 0:
                        bottomNavigationView.getMenu().findItem(R.id.one).setChecked(true);
                        break;
                    case 1:
                        bottomNavigationView.getMenu().findItem(R.id.two).setChecked(true);
                        break;
                    case 2:
                        bottomNavigationView.getMenu().findItem(R.id.three).setChecked(true);
                        break;
                    case 3:
                        bottomNavigationView.getMenu().findItem(R.id.four).setChecked(true);
                        break;
                    case 4:
                        bottomNavigationView.getMenu().findItem(R.id.five).setChecked(true);
                        break;
                }
            }
        });

    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        switch (item.getItemId()) {
            case R.id.one:
                viewPager.setCurrentItem(0, false);
                break;
            case R.id.two:
                viewPager.setCurrentItem(1, false);
                break;
            case R.id.three:
                viewPager.setCurrentItem(2, false);
                break;
            case R.id.four:
                viewPager.setCurrentItem(3, false);
                break;
            case R.id.five:
                viewPager.setCurrentItem(4, false);
                break;
        }
        return true;
    }
}

我不想使用 viewPager.setOffscreenPageLimit(size) 因为它会在启动时同时加载所有 5 个片段中的数据(我不想在启动时调用所有 5 个 API)。

这可能是一个比您希望的更复杂的解决方案,但是将数据与 UI 分离是可行的方法。意思是使用 MVVM 之类的东西,然后只保留 ViewModel 数据(而不是人为地使片段保持活动状态)

另一种 easier/faster 解决方案是只使用存储库并将加载的数据保存在其中 class,然后片段可以只检查存储库是否已经加载数据,然后相应地显示内容.

我不知道这是否可行,因为无论何时使用导航组件,它都会弹出片段堆栈。

但是为了保存数据,我认为有一种方法是使用 ViewModel,即使片段被销毁也能保持活动状态。

  • 要开始使用 ViewModel,请将此添加到 build.grade(模块:应用程序)中的依赖项部分:
dependencies {
...
    implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.1"
    implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"

...
}
  • 您现在应该能够创建一个扩展 ViewModel 的 class,例如我将其称为 myViewModel,在 ViewModel 构造函数中您将调用为您加载数据的方法:
public class MyViewModel extends ViewModel {
    DataClass myData ;
    public MyViewModel () {
        super();
        getData();
    }
    void getData(){
    ...
    assign myData here 
    ...
    }

}
  • 然后在第一个片段的 onCreate 中,您将使用以下代码创建 ViewModel:
MyViewModel myViewMode= new ViewModelProvider(requireActivity()).get(MyViewModel .class);
  • 现在您会期望每次从零创建片段时也会从头创建 ViewModel,但这里有一个转折点,只要您提供的对象引用,ViewModel 就会保持活动状态通过 ViewModelProvider 是活的,在我们的例子中是主要的 activity.

  • 这样,ViewModel 将在应用程序的整个生命周期内仅调用一次构造函数,只要您的 activity 没有被销毁,之后每次您请求 ViewModel在您的 onCreate 片段中,它只会为您提供对先前创建的片段的引用,而不是创建一个新片段(如果您之前遇到过设计模式,则类似于单例设计模式)。

  • 您稍后可以将 getter 添加到您的 ViewModel 以在您的片段中随时获取数据。

N.B :您在 ViewModel 中带来和存储的数据会从您的 RAM 中消耗,因此请避免拥有大量数据并仅存储必要的数据,以便一个示例,而不是存储位图,存储它的 uri/ 而不是存储具有属于数据库的 key/id 的对象,只存储 key/id 并且只要你需要,只需将它从数据库中带到内存中.

之前的通知对于避免在您的应用程序使用期间出现 OutOfMemoryError 异常非常重要。

如果你对 ViewModels 有好奇心,你可以通过这个 link

开始阅读它们