1.FragmentStatePageAdapter和FragmentPageAdapter的异同点
同:
(1)对于超出缓存范围的Fragment:
FragmentStateAdapter执行Fragment的onDetach,即超出缓存的范围,Fragment将从Activity中脱离(detach),当然此时Fragment的视图也会被销毁。
FragmentPageAdapter执行onDestroyView ,超出缓存的范围,Fragment将销毁视图,但是不会从Activity中脱离(detach)
超出缓存范围的 Fragment 都会销毁 成员变量
(2)使用FragmentStatePageAdapter,当Fragment销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。
FragmentPageAdapter不会调用onSaveInstanceState(Bundle outState)来保存数据。
(3)注意:
1)onDestroyView可以理解为切断了Fragment到View的引用,如果此时View没有被其他东西引用,那么这个View将等待GC回收,表现在你下次回来这个Fragment的时候,将执行onCreateView来重建View(如EditText,ImageView都是要重新创建的)。
反之这个View中的某些子View将不会被回收。比如下面的代码:
private ImageView imageView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
super.onCreateView( inflater, container,
savedInstanceState);
View view= inflater.inflate(R.layout.fragment_fragment_one, container, false);
imageView= (ImageView) view.findViewById(R.id.image_one);
return view;
}
如上面代码所示,因为成员变量imageView还保持对View中ImageView的引用,所以ImageView占用的内存并不会被回收。要是的ImageView被回收,采用如下代码
@Override
public void onDestroyView() {
super.onDestroyView();
imageView=null;
}
2)onDetach意思是Fragment从Activity脱离,此时若在Fragment中调用getActivity()将得到null。
3)关于FragmentAdapter的写法
//写法1:容易引起内存泄露
//不管使用哪种adapter,都只是销毁了视图而已,Fragment并不会被回收,因为adapter中的mFragmentList
//引用着Fragment。
public class FragmentAdapter4 extends FragmentStatePagerAdapter {
private List<Fragment> mFragmentList;
public FragmentAdapter4(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
mFragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
if (mFragmentList == null) {
return 0;
}
return mFragmentList.size();
}
}
//写法2:正确写法
//在getItem中去构造Fragment
public class FragmentAdapter2 extends FragmentStatePagerAdapter {
private int[] mArrayRes;
public FragmentAdapter2(FragmentManager fm, int[] arrayRes) {
super(fm);
mArrayRes = arrayRes;
}
@Override
public Fragment getItem(int position) {
return StateFragment.newInstance(mArrayRes[position]);
}
@Override
public int getCount() {
if (mArrayRes == null) {
return 0;
}
return mArrayRes.length;
}
}
(2)都是默认缓存3个,setOffscreenPageLimit(int num)设置缓存的个数。
(3)各个重要方法的作用和执行顺序。(从上到下按顺序)
左右滑动时:
onPageScrollStateChanged DRAGGING
onPageScrollStateChanged SETTLING
onPageSelected
onPageScrollStateChanged IDLE
setPrimaryItem (会执行多次)
初始化时:
getCount
getItem
setPrimaryItem(会先setPrimaryItem,再预加载,所以当第一次加载的时候,会先setPrimaryItem,再加载当前的Fragment)
Fragment的生命周期一定在getItem之后,但是多个Fragment的生命周期有可能是交替执行的。
2.给ViewPager设置切换动画:
public class BasePagerTransFarmer implements ViewPager.PageTransformer {
private static float MIN_SCALE = 0.75f;
@SuppressLint("NewApi")
@Override
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when
// moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 );
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE)
* (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
//设置动画
viewPager.setPageTransformer(true, new BasePagerTransFarmer());
更多切换动画内容请点
http://blog.youkuaiyun.com/angcyo/article/details/49796759
3.使用PagerAdapter需实现一下四个方法:
getCount
isViewFromObject
instantiateItem
destroyItem
而FragmentStatePageAdapter和FragmentPageAdapter只需实现
getCount和getItem,其他方法已经帮我们实现好了。
4.
设置页卡间距: viewPager.setPageMargin(30);(或者用 viewPager.setPageMargin(-30),这样可以在一个pager里看到到两个pager放在一起的效果。)
5.
ViewPager无限轮播
方法1:
特殊说明:只有两条(a,b),一条数据(a)时,往list中添加相同的数据即可(a,b,a,b),(a,a,a)
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(viewList.get(position%viewList.size()));
return viewList.get(position%viewList.size());
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(viewList.get(position%viewList.size()));
}
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
方法2:
特殊说明:只有一条数据时(a),往list中再添加两条相同数据即可(a,a,a)
boolean mIsChanged;//标识是否需要在onPageScrollStateChanged中重新setCurrentItem
int pageIndex;
@Override
public void onPageScrollStateChanged(int pState) {
if (ViewPager.SCROLL_STATE_IDLE == pState) {//viewpager已经停止滑动
if (mIsChanged) {
mIsChanged = false;
mViewPager.setCurrentItem(pageIndex, false);
}
}
}
@Override
public void onPageSelected(int position) {
if (position == 0) {
// 当视图在第一个时,将页面号设置为图片的最后一张。
mIsChanged=true;
pageIndex = mViewPagerList.size() - 2;
} else if (position == mViewPagerList.size() - 1) {
// 当视图在最后一个时,将页面号设置为图片的第一张。
mIsChanged=true;
pageIndex = 1;
}
/**
* 不要在此处 mViewPager.setCurrentItem(pageIndex, false); 因为此时viewpager还没有停止滑动,会造成界面闪跳
*/
}
6.自定义ViewPager的滑动速度。
(1)自定义一个Scroller类
public class FixedScroller extends Scroller {
private int mDuration = 500;
public FixedScroller(Context context) {
super(context);
}
public FixedScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
//用mDuration去代替系统默认的Duration
super.startScroll(startX, startY, dx, dy, mDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
//用mDuration去代替系统默认的Duration
super.startScroll(startX, startY, dx, dy, mDuration);
}
}
try { //使用反射重新设置ViewPager的Scroller
Field mScroller;
mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
Interpolator sInterpolator = new LinearInterpolator();
FixedScroller scroller = new FixedScroller(viewPager.getContext(),
sInterpolator);
mScroller.set(viewPager, scroller);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
}
7.关于adapter的 notifyDataSetChanged()
(1)当adapter中的数据源发生变化的时候(list的size变化了,list里面的内容变化了),就需要调用notifyDataSetChanged()去更新数,否则应用会崩溃。要注意两点:
a.如果只是list的size变化了,就不用重写adapter的getItemPosition()。
b.如果是list里面的内容变化了,就需要重写adapter的getItemPosition()
public int getItemPosition(Object object) {
// 对应position的pager不需要更新,这是默认值
//return POSITION_UNCHANGED;
//需要更新
return POSITION_NONE;
}
8.destroyItem和instantiateItem 源码解析
如果destroyItem没有删除viewpager,那么instantiateItem就不会去调用getItem来获取新的fragment,而是复用之前的fragment。
如果如果destroyItem删除了viewpager,那么instantiateItem就会去调用getItem来获取新的fragment。