1.适配器PagerAdapter
ViewPager使用适配器类将数据和view的处理分离,ViewPager的适配器叫PagerAdapter,这是一个抽象类,不能实例化,所以它有两个子类:FragmentPagerAdapter 和 FragmentStatePagerAdapter,这两个都是处理页面为Fragment的情况。而PagerAdapter则是处理通用View的适配器,但是使用它的时候一定要自定义一个类继承这个基类。
private class Adapter extends PagerAdapter {
public Object instantiateItem(ViewGroup container, int position) {
//container其实就是ViewPager
View itemView = LayoutInflater.from( context).inflate(R.layout.item_pager, null);
container.addView(itemView);
return itemView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// object为instantiateItem返回的object对象
container.removeView((View) object);
}
@Override
public int getCount() {
return 10; //获取viewpager页数
}
@Override
public boolean isViewFromObject(View view, Object o) {
//判断view和o是否存在对应关系,内部是通过view找到对应的object的关联关系(instantiateItem中返回的object)。如果在instantiateItem方法中返回的是view,那这里就返回view == o
return view == o;
}
}
PageAdapter主要有以下几个方法:
①Object instantiateItem(ViewGroup container, int position)
这个方法在ViewPager需要加载某个页面时调用,container就是ViewPager自己,position是页面索引;
这个方法需要实现的是添加一个view到container中,然后返回一个跟这个view能够关联起来的对象,这个对象可以是view自身,也可以是其他对象(比如FragmentPagerAdapter返回的就是一个Fragment),关键是在isViewFromObject能够将view和这个object关联起来。
②void destroyItem(ViewGroup container, int position, Object object)
当ViewPager需要销毁一个页面时调用,在这个方法里需要将position对应的view从container中移除。
这个方法有三个参数,container就是ViewPager自己,position是页面索引,第三个参数object就是instantiateItem方法返回的对象。
在这个方法里需要通过object找到对应的View,然后将其移除掉,如果instantiateItem方法返回的就是View,就直接将object强转成View移除即可:container.removeView((View) object);如果不是,一般会自己创建一个List缓存view列表,然后根据position从List中找到对应的view移除(如果不移除,会产生内存泄漏)。FragmentPagerAdapter的实现是:mCurTransaction.detach((Fragment)object),其实也就是将fragemnt的view从container中移除
③isViewFromObject(View view, Object object)
这个方法用来说明instantiateItem方法中向container中添加的view和方法返回的对象两者之间一对一的关系。因为在ViewPager内部有个方法叫infoForChild,这个方法是通过view去找到对应页面信息缓存类ItemInfo(内部调用了isViewFromObject),如果找不到,ViewPager会认为不是Adapter提供的View,所以这个View不会显示出来。所以说,isViewFromObject方法是让view和object(内部为ItemInfo)一一对应起来。
④int getItemPosition(Object object)
该方法是判断当前object对应的View是否需要更新,在调用notifyDataSetChanged时会间接触发该方法。如果返回POSITION_UNCHANGED,表示该页面不需要更新;如果返回POSITION_NONE则表示该页面无效了,需要销毁并触发destroyItem方法(并且有可能调用instantiateItem重新初始化这个页面)。
从这几个方法就可以看出ViewPager的工作流程:在注册Adapter和draw绘制等时机,会调用 getCount()确定需要显示的子View的数量,然后在真正addView()加载子View的时候会调用instantiateItem()获取具体要显示的View,然后在装载显示的时候又会调用isViewFromObject()方法再次确认类型正确,然后在ViewPager不需要某个View的时候,会调用destroyItem()方法请求销毁。
2.FragmentPagerAdapter
子View是Fragment的情景比较常见,而Fragment的管理是个麻烦事,意味着Adapter中更多的代码量,针对这种情况,谷歌推荐开发者直接继承PagerAdapter的两个直接子类FragmentPagerAdapter和FragmentStatePagerAdapter,这样就不用关注Fragment的管理,只需要提供两个方法就行了。
FragmentPagerAdapter是一个不管理Fragment状态的适配器。这就意味着,许多碎片的Fragment会占用大量的内存(在少数情况下Fragment还是有可能会丢失的),如果在Fragment页面较少,或者每个Fragment页面中持有的数据较少的情况下,可以选择使用较为简单的适配器,不用关注并管理 Fragment 的状态,加快业务开发。
另一种情况是当大量碎片的Fragment占用的内存是不能被容忍,这时FragmentStatePagerAdapter就派上用场了,这个适配器保存并绑定了状态,在状态为销毁的状态下,可以去重新获取。但是频繁创建又会导致切换页面的性能消耗。所以在选择适配器的时候,要根据具体的业务逻辑进行判断,选择合适的适配器。
这两个子类的区别主要是Fragment内存管理状态的不同。为了验证,在Fragment的生命周期中添加Log。
采用了FragmentPagerAdapter 的Log记录:
//滑到第一页
MessageFragment: ---onAttach----
MessageFragment: ---onCreate----
FriendFragment: ---onAttach----
FriendFragment: ---onCreate----
MessageFragment: ---onCreateView---
FriendFragment: ---onCreateView---
//滑到第二页
CircleFragment: ---onAttach----
CircleFragment: ---onCreate----
CircleFragment: ---onCreateView---
//滑到第三页
AccountFragment: ---onAttach----
AccountFragment: ---onCreate----
MessageFragment: ----onDestroyView---
AccountFragment: ---onCreateView---
//滑到第四页
FriendFragment: ----onDestroyView---
采用了FragmentStatePagerAdapter的Log记录:
//滑到第一页
MessageFragment: ---onAttach----
MessageFragment: ---onCreate----
FriendFragment: ---onAttach----
FriendFragment: ---onCreate----
MessageFragment: ---onCreateView---
FriendFragment: ---onCreateView---
//滑到第二页
CircleFragment: ---onAttach----
CircleFragment: ---onCreate----
CircleFragment: ---onCreateView---
//滑到第三页
AccountFragment: ---onAttach----
AccountFragment: ---onCreate----
MessageFragment: ----onDestroyView---
MessageFragment: ----onDestroy--- (不同的地方)
MessageFragment: ----ondetach--- (不同的地方)
AccountFragment: ---onCreateView---
//滑到第四页
FriendFragment: ----onDestroyView---
FriendFragment: ----ondestroy--- (不同的地方)
FriendFragment: ----ondetach--- (不同的地方)
不更改ViewPager的预加载状态的情况下,翻到第一页(MessageFragment)和翻到第二页(FriendFragment)的时候,两个Adapter完全相同,这是因为这个时候根据ViewPager的缓存策略,会缓存3个子View,提高加载速度和显示流畅性。看日志可以证明:翻到第二页的时候第三个Fragment(CircleFragment)已经完成了加载操作,而且这个时候还没有回调Fragment卸载的相关方法。
翻到第三页(CircleFragment)和第四页(AccountFragment)的时候,出现了不同:当翻到第三页的时候,因为要提前加载第四页(AccountFragment),又由于缓存的数量是3,所以第一页(MessageFragment)开始回调卸载方法。使用FragmentPagerAdapter的回调了 onDestroyView卸载方法;使用FragmentStatePagerAdapter的回调了onDestroyView、onDestroy、onDetach卸载方法。当翻到第四页(AccountFrag