代码之家  ›  专栏  ›  技术社区  ›  Alaa AbuZarifa

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

  •  1
  • Alaa AbuZarifa  · 技术社区  · 6 年前

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

    我知道适配器的行为是这样的,以预呈现片段,以确保选项卡和诸如此类的对象之间更好地进行事务处理。。!但我真的负担不起用每个select调用多个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);
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   Agnaldo Rodrigues    5 年前

    我在一个项目中遇到了同样的问题

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

    查看 fragment lifecycle

        2
  •  1
  •   Kushan    6 年前

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

    要点如下:

    1) 创建接口

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

    2) 修改FragmentPagerAdapter以覆盖OnInstallateItem方法

    这里,每个片段将在适配器类中声明一个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中,添加以下方法以获取图片中片段的弱引用

        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中的每个片段中,实现我们在步骤1中创建的接口并重写这些方法。

    在每个片段amIVisible中创建一个布尔变量。。。这将有助于确定片段何时可见以及何时可以调用网络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);
    
                }
            }
        }
    

    其他片段的接口方法相同。

    这很复杂,但确实有效。适配器内部的弱引用甚至允许垃圾收集并避免上下文泄漏。