阻止 FragmentPagerAdapter 立即创建其所有片段

问题描述 投票:0回答:3

所以我有底部导航栏,每个选项卡有 4 个片段,在每个选项卡内我调用 API 请求来获取一些数据,但问题是每次我按下导航栏的任何选项卡时,都会创建至少两个片段他们调用自己的方法,并通过扩展触发 API 请求..!我只想实例化我选择的片段。!

我知道适配器的行为就像这样预渲染片段以确保选项卡之间更好的事务等等......!但我真的无法承受每次选择时调用多个 api 调用..!

适配器

public class My_PagerAdapter extends FragmentPagerAdapter {
                       // I've tried FragmentStatePagerAdapter but same thing

  public My_PagerAdapter (FragmentManager fm) {
    super(fm);
  }

  @Override
  public Fragment getItem(int position) {

    switch (position) {
      case 0:
         new MyFragment_1();
      case 1:
         new MyFragment_2();
      case 2:
         new MyFragment_3();
      case 3:
         new MyFragment_4();
    }

  }

  @Override
  public int getCount() {
    return 4;
  }
}

编辑

我就是这样称呼适配器的..

ViewPager viewPager = main.findViewById(R.id.vp); 
viewPager.setOffscreenPageLimit(1); 
viewPager.setAdapter(new My_PagerAdapter (getChildFragmentManager()));
navigationTabBar.setModels(models); // just UI stuff for each tab offered by the bottom navigation bar library, 
navigationTabBar.setViewPager(viewPager);
java android android-fragments android-viewpager android-adapter
3个回答
2
投票

我在我正在从事的项目中遇到了同样的问题

我的解决方案是在每个片段的 OnResume 方法上添加 API 调用。 这样它们只会在片段完全可见时被触发。

查看fragment lifecycle


1
投票

好吧,这正是我面临的问题。我的解决方案不会阻止 viewpager 创建片段,但会停止对网络 api 的调用。

要点如下:

1)创建接口

public interface ViewPagerLifeCycleManagerInterface {
  void onResumeAndShowFragment();
  void onPauseAndHideFragment();
  //void refreshFragment();
}

2) 修改 FragmentPagerAdapter 以覆盖 onInstantiateItem 方法

这里每个 Fragment 将在 Adapter 类中声明一个weakReference,以便存储对创建的片段的引用

    @Override
public Object instantiateItem(ViewGroup container, int position){

    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);

    switch (position){
        case 0:
            xyzWeakReference=null;
            xyzFragmentWeakReference=new WeakReference<>((xyz)createdFragment);
            break;

        case 1:
            xyz1WeakReference=null;
            xyz1WeakReference=new WeakReference<>((xyz1WeakReference)createdFragment);
            break;

    }

    return createdFragment;
};

3)在FragmentPagerAdapter内部,添加以下方法,以获取图片中fragment的弱引用

    public Fragment getFragmentAtGivenPosition(int i){
    switch (i){
        case 0:
            if(xyzFragmentWeakReference == null){
                return null;
            }
            return xyzFragmentWeakReference.get();
        case 1:
            if(xyz1FragmentWeakReference == null){
                return null;
            }
            return xyz1FragmentWeakReference.get();

    }
}

4) 现在,在创建 TabLayout 并实例化视图分页器的活动中,将侦听器附加到 TabLayout 以侦听选项卡更改

        tabLayout_bookmarks.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(final TabLayout.Tab tab) {
            //let the instantiateItem have some time to be called by the adapter

            currentFragmentIndex = tab.getPosition();

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {

                    ViewPagerLifeCycleManagerInterface currentFragment = (ViewPagerLifeCycleManagerInterface)btca.getFragmentAtGivenPosition(tab.getPosition());

                    if(currentFragment!=null){
                        currentFragment.onResumeAndShowFragment();
                    }else{
                        //Log.d("FragmentCreate","Current fragment is null and fucked up in adapter");

                        //if it is null ... that means the adapter hasn't yet called instantiate item ... this internally calls get item any way
                        //.....

                        //This shouldn't really hit but in case it does ... keep a handler in order to ensure that everything is created
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                ViewPagerLifeCycleManagerInterface localFragment  = (ViewPagerLifeCycleManagerInterface)btca.getItem(tab.getPosition());
                                //getItem never returns a null fragment unless supplied a horrendous value for position
                                //by the time these 50 ms pass, the instantiate item should surely have been called
                                //else it will be an empty space ... no crash though
                                localFragment.onResumeAndShowFragment();
                            }
                        },50);
                    }

                }
            },100);

        }

        @Override
        public void onTabUnselected(final TabLayout.Tab tab) {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    ViewPagerLifeCycleManagerInterface currentFragment = (ViewPagerLifeCycleManagerInterface)btca.getFragmentAtGivenPosition(tab.getPosition());
                    if(currentFragment!=null){
                        currentFragment.onPauseAndHideFragment();
                    }else{
                        //Log.d("FragmentCreateTab","the fucking fragment was null");
                        //if it is null ... that means the adapter hasn't yet called instantiate item ... this internally calls get item any way
                        //.....

                        //This shouldn't really hit but in case it does ... keep a handler in order to ensure that everything is created
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                ViewPagerLifeCycleManagerInterface localFragment  = (ViewPagerLifeCycleManagerInterface)btca.getItem(tab.getPosition());
                                //getItem never returns a null fragment unless supplied a horrendous value for position
                                //by the time these 50 ms pass, the instantiate item should surely have been called
                                //else it will be an empty space ... no crash though
                                localFragment.onPauseAndHideFragment();
                            }
                        },50);

                    }

                }
            },100);
        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {
            //do nothing
        }
    });

5)在Viewpager内的每个Fragments中,实现我们在步骤1中创建的接口并重写方法。

在每个fragment amIVisible中创建一个布尔变量...这将有助于决定fragment何时可见以及何时可以调用网络api

a) 对于 viewpager 中的第一个片段,即在 0 索引处,网络 api 调用必须在视图创建后立即发生。默认情况下该片段显然是可见的。这是写在onCreateView方法里面的

        if(dataList!=null && dataList.size()==0) {
        if (savedInstanceState==null) {
            //your api call to load from net
        } else {
            if (savedInstanceState.getBoolean("savedState")) {
                //If you have saved data in state save, load it here
            } else {
                //only fire the async if the current fragment is the one visible, else the onResumeAndShowFragment will trigger the same async when it becomes visible
                if (savedInstanceState.getBoolean("amIVisible")) {
                    //Load data from net
                }
            }
        }
    }

第一个片段的其他方法如下

    @Override
public void onResumeAndShowFragment() {
    amIVisible=true;


    if(dataList!=null && dataList.size()==0){
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Load data from net if data was not found,
                //This basically means auto refresh when user scrolls back and the fragment had no data
            }
        },400);
    }
}

@Override
public void onPauseAndHideFragment() {
    amIVisible=false;
}

这里我重写了 onSaveInstanceState 方法并保存了 amIVisible 的值,savedState 是一个布尔值,指示列表是否至少有 1 个项目。

b) 对于其他片段,数据将通过以下过程加载

        if(savedInstance!=null){

        if (savedInstance.getBoolean("savedState")) {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                   //load data from saved State
                }
            },100);

        } else {
            //only fire the async if the current fragment is the one visible, else the onResumeAndShowFragment will trigger the same async when it becomes visible


            if (savedInstance.getBoolean("amIVisible")) {

                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //load data from net
                    }
                },100);

            }
        }
    }

其他fragment接口方法相同

这相当复杂,但可以完成工作。适配器内部的弱引用甚至允许垃圾收集并避免上下文泄漏。


0
投票

防止在视图寻呼机中加载所有片段。

在view pager的setAdapret之前添加这一行。 viewPager.setOffscreenPageLimit(number_of_fragment); // 示例:4

并检查登录 onResume。可以更新里面的数据

© www.soinside.com 2019 - 2024. All rights reserved.