我们在什么情况下能使用到这个呢?
一般的情况,ViewPager只需要简单的实现PagerAdapter即可。可是当你遇到这种情况,ViewPager中的View本身有着大量的数据处理工作,要调很多接口什么的,你会发现你脑子会炸的...
我遇到的情况是,我的上篇文章点击打开链接中的情况,上面有一个TabLayout可以滚动,而且下面的内容也可以滚动来切换Tab,所以我用的是TabLayout+ViewPager,这样可以实现动态的添加Tab(取决于后台返回的多少来添加),这个easy,可是下面的ViewPager如果要动态的添加,而且每一项的数据又不一样的时候,我发现传统的只是实现PagerAdapter是不行的,每一个子项里面有太多不一样的数据,我绞尽脑汁实现了一下,能实现效果,但是当你滚动的很快的时候他就崩了(发生IndexOutOfBounds异常)。
想了想,最好是能在Fragment中单独去处理数据,可是Fragment也不是View,该怎么添加到ViewPager中呢,尝试着去寻找答案,原来还有PagerAdapter的子类FragmentPagerAdapter!这个是什么?好像能解决问题!查阅资料得知,好像这个就是能向ViewPager中添加Fragment的适配器!先试试是不是吧。
果然,就是他!
//切换Fragment的ViewPager private ViewPager mViewpager; //切换Fragment的ViewPager的适配器 private MyFragPagerAdapter mFragPagerAdapter; //盛放要切换的所有fragment(只需要定义一个Fragment类就可),按照getArgument()去获取不同的数据 private List看定义:mFragments;
private class MyFragPagerAdapter extends FragmentPagerAdapter { private List而且他的getItem只能返回v4兼容包下的Fragment,不能返回android.app.Fragment下的fragment,而且一路发现和fragment相关的都是v4包下的,即使是构造方法里要用的FragmentManager也得是v4包下的,而且在fragment中无法获取v4包下的FragmentManager(getSupportFragmentManager()方法来取),哪怕是getActivity().getSupportFragmentManager()也不行,所以要在他的上下文Activity中定义成static型的,而且不同的两个Activity下的v4包下的FragmentManager也不能公用,并且必须是AppCompatActivity环境下才能获取到。fragments; public MyFragPagerAdapter(FragmentManager fm, List mFragments) { super(fm); fragments = mFragments; } @Override public android.support.v4.app.Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return fragments.size(); } }
//v4包下的FragmentManager public static android.support.v4.app.FragmentManager sV4FragManager ;然后在fragment中取
mFragPagerAdapter = new MyFragPagerAdapter(Main.sV4FragManager, mFragments);这样就可以实现了。
注意,要先将ViewPager要显示的Fragment集合给初始化,然后再设置tab的内容,要不然提前设置会被清空(和ViewPager绑定之后会被remove掉然后添加了新的)。所以
/* 给ViewPager初始化要添加的View */ private void addAllViews() { for (int i = 0; i < mTitles.size(); i++) { android.support.v4.app.Fragment fragment = new Child_SingleTab(); Bundle bundle = new Bundle(); bundle.putString("categoryId", (String) mTitles.get(i).get("categoryId")); fragment.setArguments(bundle); mFragments.add(fragment); } mFragPagerAdapter.notifyDataSetChanged(); //此时Tab的标题被覆盖了,需要重新设置 for (int i = 0; i < mFragments.size(); i++) { mTabLayout.getTabAt(i).setText((String) mTitles.get(i).get("categoryName")); } }那么初始化的View数是多少呢,我们前面说过,接口去多少就添加多少,所以先调获取Tab的接口,然后得到他的数量。
final JSONObject jsobj = BaseDataService.getTabsData("1"); //先得到标题 mTitles = JsonUtils.parseJsonArray(jsobj.getJSONArray("results")); Main.sHandler.post(new Runnable() { @Override public void run() { //给ViewPager添加View addAllViews(); } });在取完后去调添加View的方法。
还有一个问题,我们说过,每一项的子View里面的数据和显示也不一样,我们的Fragment在这里只有一个,Fragment怎么知道当前是第几个Tab下的显示从而去获取不同的数据呢?想了一会,有了!
Bundle bundle = new Bundle(); bundle.putString("categoryId", (String) mTitles.get(i).get("categoryId")); fragment.setArguments(bundle);看上面的添加View的方法里,在添加每个fragment的时候加入arguments,在Fragment的onCreateView方法里去判断是第几个Tab,从而去获取不同的数据。哪怕如果像腾讯视频里的那样,有的布局都不一样都可以实现:只需要判断arguments,然后去inflate不同的布局即可!
bingo!!
还有一个FragmentStatePagerAdapter,据说它的意义在于:
之前我们知道
mViewpager.setOffscreenPageLimit(index);
这个方法可以设置显示之外的缓存页数,优点就是切换其他页的时候会不用现去初始化界面,只要显示过一次他会提前在内存中存在不会销毁,默认是显示页的左右两页缓存,当你的内存不够用时就可以用FragmentStatePagerAdapter了,只要是没在当前显示,立马销毁,不占用内存。
补充补充:
FragmentPagerAdapter中的数据集合要使用同一个指向,不能用中间指向,像上文中的fragments就不行。初始的mFragments改变并不会引起重绘(notifyDataSetChanged之后)。
如果想要启动另一个Activity,在fragment中调用startActivityForResult()之后会回调到此fragment的onActivityResult方法中,getActivity.startActivityForResult()之后会回调到Activity的onActivityResult方法中。
我想让回调时可以重新创建视图怎么办,replace试过了,不行,remove,add还是不行,标题是最新的了,但是下面的ViewPager的内容不显示或者显示混乱,跑了断点结果发现ViewPager里面的fragment根本没执行onCreateView方法!
也就是说Activity下的Fragment和其下的子Fragment都被移除了,但是ViewPager中的Fragment还没有被移除或者是冲突了!
解决方法是,每次onSaveInstanceState时都移除ViewPager的fragment集合里面的fragment,然后onResume的时候重新添加。
ps:
@Override public void onSaveInstanceState(Bundle outState) { android.support.v4.app.FragmentTransaction transactionV4 = Main.sV4FragManager.beginTransaction(); if (mFragments != null) { for (int i = 0; i < mFragments.size(); i++) { transactionV4.remove(mFragments.get(i)); } } transactionV4.commit(); mFragPagerAdapter.notifyDataSetChanged(); super.onSaveInstanceState(outState); } @Override public void onResume() { super.onResume(); //初始化 init(); }
注意了!纠正!纠正!(此问题导致我2016-12-3留宿公司一宿)
1.我的情况是Activity里嵌套了Fragment_0,然后Fragment_0里面又嵌套了两个Fragment:Fragment_1、Fragment_2,然后我在其中一个Fragment,Fragment_1里面有一个包含一系列Fragment的ViewPager,按照前面的步骤没什么问题,数据也是对的,可是!可是!当我在另一个fragment,也就是Fragment_2里添加的和Fragment_1里面的一模一样,然后就有问题了,Tab能正常显示,点击切换也没什么问题,但是当你活动下面的ViewPager时,居然指示条滑动和标题不对应,有时候滑动2、3次才能切换标题,但是指示条每次只移动一点,这是我已经把ViewPager里面的fragment都给去了,要不然直接就甭,现实的错误信息是我ViewPager里面的fragment的下拉刷新出问题了,这都是由于先前的Fragment_1里面的fragment有没有销毁的导致的!!
结局方法就:我们前面说到,FragmentPagerAdapter的构造方法需要一个v4包下的FragmentManager,之前我是在上溯到Activity里面去取的,其实FragmentPagerAdapter的构造方法里要传进去的是getChildFragmentManager()获取到的FragmentManager,这个是专门供当前Fragment用的FragmentManager,必须要这样,要不然可能你当时没发现问题,后来有问题了就准备哭吧,然后还需要注意的是,getChildFragmentManager()这个方法是属于Fragment里面的方法,如果你在app包下调用这个方法那么获取的就是app包下的FragmengManager,反之,如果你在v4包下调用这个方法那么获取的就是v4包下的FragmengManager!
2.还有,同一个Activity下,不能同时使用v4包下的Fragment和app包下的Fragment!
3.大神说,如果使用的不是v4包下的Fragment,需要导入v11支持库用v11的FragmentPagerAdapter!(我想这种适用于有太多地方,改不过来的情况吧)
切记切记!!!