【问题标题】:Prevent fragment from loading data every time on bottomnavigation tab click防止片段每次在底部导航选项卡单击时加载数据
【发布时间】:2021-09-13 17:22:41
【问题描述】:

我正在使用带有 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)。

【问题讨论】:

    标签: java android android-fragments android-viewpager2


    【解决方案1】:

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

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

    • 要开始使用 ViewModel,请将其添加到 build.grade (Module:app) 中的依赖项部分:
    dependencies {
    ...
        implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.1"
        implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
    
    ...
    }
    
    • 您现在应该能够创建一个扩展 ViewModel 的类,例如我将其称为 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,但这是一个转折点,只要您通过 ViewModelProvider 提供对它的引用的 Object 还活着,ViewModel 就会保持活动状态在我们的例子中,这是主要活动。

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

    • 您可以稍后在 ViewModel 中添加一个 getter,以便随时在片段中获取数据。

    NB :您在 ViewModel 中带来和存储的数据会从您的 RAM 中消耗,因此请避免拥有大量数据并仅存储必要的数据,例如,不要存储位图,而是存储它的 uri/ 不是存储具有属于数据库的 key/id 的对象,而是仅存储 key/id 并且在需要时将其从数据库中带入内存。

    前面的通知对于在你的应用程序使用过程中避免OutOfMemoryError Exception很重要。

    如果您对 ViewModel 有兴趣,可以通过link 开始阅读它们。

    【讨论】:

    • 非常感谢您的详细解答
    【解决方案2】:

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

    另一个更简单/更快的解决方案是只使用存储库并将加载的数据保存在该类中,然后片段可以检查存储库是否已加载数据,然后相应地显示内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 2019-09-02
      相关资源
      最近更新 更多