阻止 FragmentPagerAdapter 一次创建它的所有片段
Stop FragmentPagerAdapter from creating all its fragments at once
所以我有底部导航栏,每个选项卡有 4 个片段,在每个片段中我调用 API 请求来获取一些数据,但问题是每次我按导航栏的任何选项卡时,至少创建了两个片段,它们调用了自己的方法,并且通过扩展,它们触发了 API 请求..!我只想实例化我 select 的片段。!
我知道适配器会像这样预渲染片段以确保选项卡之间更好的交易等等..!但我真的负担不起每个 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);
好的,这正是我遇到的问题。我的解决方案不会阻止 viewpager 创建片段,但会停止对网络 api.
的调用
这里是要点:
1) 创建接口
public interface ViewPagerLifeCycleManagerInterface {
void onResumeAndShowFragment();
void onPauseAndHideFragment();
//void refreshFragment();
}
2) 修改您的 FragmentPagerAdapter 以覆盖 onInstantiateItem 方法
这里每个 Fragment 都会在 Adapter 中声明一个 weakReference class 以存储对创建的 fragment 的引用
@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 并实例化视图寻呼机的 activity 中,将侦听器附加到 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);
}
}
}
其他片段的接口方法相同。
这很复杂,但可以完成工作。适配器内部的弱引用甚至允许垃圾收集并避免上下文泄漏。
我 运行 在我正在从事的项目中遇到了同样的问题
我的解决方案是在每个片段中添加对 OnResume 方法的 API 调用。
这样它们只会在片段完全可见时触发。
查看 fragment lifecycle
所以我有底部导航栏,每个选项卡有 4 个片段,在每个片段中我调用 API 请求来获取一些数据,但问题是每次我按导航栏的任何选项卡时,至少创建了两个片段,它们调用了自己的方法,并且通过扩展,它们触发了 API 请求..!我只想实例化我 select 的片段。!
我知道适配器会像这样预渲染片段以确保选项卡之间更好的交易等等..!但我真的负担不起每个 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);
好的,这正是我遇到的问题。我的解决方案不会阻止 viewpager 创建片段,但会停止对网络 api.
的调用这里是要点:
1) 创建接口
public interface ViewPagerLifeCycleManagerInterface {
void onResumeAndShowFragment();
void onPauseAndHideFragment();
//void refreshFragment();
}
2) 修改您的 FragmentPagerAdapter 以覆盖 onInstantiateItem 方法
这里每个 Fragment 都会在 Adapter 中声明一个 weakReference class 以存储对创建的 fragment 的引用
@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 并实例化视图寻呼机的 activity 中,将侦听器附加到 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);
}
}
}
其他片段的接口方法相同。
这很复杂,但可以完成工作。适配器内部的弱引用甚至允许垃圾收集并避免上下文泄漏。
我 运行 在我正在从事的项目中遇到了同样的问题
我的解决方案是在每个片段中添加对 OnResume 方法的 API 调用。 这样它们只会在片段完全可见时触发。
查看 fragment lifecycle