关于PagerAdapter的instantiateItem方法

在为ViewPager设置Adapter时肯定会用到PagerAdapter,Google Android文档对该类的定义如下:

Base class providing the adapter to populate pages inside of a ViewPager. You will most likely want to use a more specific implementation of this, such as FragmentPagerAdapter or FragmentStatePagerAdapter.

When you implement a PagerAdapter, you must override the following methods at minimum:

  • instantiateItem(ViewGroup, int)
  • destroyItem(ViewGroup, int, Object)
  • getCount()
  • isViewFromObject(View, Object)

上述四个方法是必须得到重载的,其他的不管,我们今天只看instantiateItem (View container, int position),对于该方法的说明如下:

Parameters
containerThe containing View in which the page will be shown.
positionThe page position to be instantiated.
Returns
  • Returns an Object representing the new page. This does not need to be a View, but can be some other container of the page.

我的理解是一个Page在切换完成后会调用该方法去加载下一个即将展示的Page,至于是哪个Page取决于切换动作,比如Page1切换到Page2,切换完成后会调用该方法去加载Page3。空说无凭,试着重写该方法打个log出来看看,下面是我重载的instantiateItem()方法:

复制代码
     @Override
        public Object instantiateItem(View arg0, int arg1) {
            ViewGroup v = (ViewGroup) mListViews.get(arg1).getParent();
            if (v != null) {
                v.removeView(mListViews.get(arg1));
            } else {
                Log.i("Allen", String.valueOf(arg1 + 1));
            }
            ((ViewPager) arg0).addView(mListViews.get(arg1), 0);
            return mListViews.get(arg1);
        }
复制代码

问题一:是否是真的为即将展示页做操作?

第一句获取父组件后面会说到用处,mListViews定义如下:

mViews = new ArrayList<View>();
mViews.add(viewOne);
mViews.add(viewTwo);
mViews.add(viewThree);
mViews.add(viewFour);

当我从page1->page2->page3->page4->page3->page2->page1,日志如下:

1
2
3
4
5
6
11 - 07  00 : 14 : 38.803 : I/Allen( 31124 ):  1
11 - 07  00 : 14 : 38.803 : I/Allen( 31124 ):  2
11 - 07  00 : 14 : 56.853 : I/Allen( 31124 ):  3
11 - 07  00 : 15 : 00.998 : I/Allen( 31124 ):  4
11 - 07  00 : 15 : 05.498 : I/Allen( 31124 ):  2
11 - 07  00 : 15 : 07.208 : I/Allen( 31124 ):  1

除了第一次进入page1会多打出“1”,其余的切换page操作都会打出"下一次即将显示的页",从page3到page4不会有日志记录,因为没有即将出现在page4之后的页。

因此问题一的答案是肯定的。

问题二:为什么要获取父组件?

这个操作后来检查发现是不必要的,出现这个操作的原因是我在设置Adapter之前做了如下操作:

mPager = (ViewPager) findViewById(R.id.guide_pager);
mPager.addView(viewOne);
mPager.addView(viewTwo);
mPager.addView(viewThree);
mPager.addView(viewFour);

如果不获取父组件并对子view进行remove操作,会报如下错误

java.lang.IllegalStateException The specified child already has a parent. You must call removeView() on the child's parent first.  

这个操作打出的日志也是不同的,同样是page1->page2->page3->page4->page3->page2->page1,日志如下:

1
2
11 - 07  00 : 28 : 11.158 : I/Allen( 1688 ):  2
11 - 07  00 : 28 : 13.963 : I/Allen( 1688 ):  1

这是因为在第一次page1到page4的切换过程中,4个page早已被加到pager中,所以第一次进入page1打印的“1”和“2”,page2的“3”以及page3的“4”是不会打印出来的,只有page3的“2”和page2的“1”。由此我猜想pager对page做了窗口处理,窗口长度大小为3,即始终保存当前page,切换前page和即将切换page。

读者完全不必采用笔者提前为Pager添加addView的处理,如果这样处理就要及时从父组件中移除view

另外一篇文章:

之前以为ViewPager每次滑动都会调用instantiateItem,后来做项目时发现有时滑动时并不执行写在instantiateItem中的一些更新操作。于是写了个demo测试一下,代码如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.     ViewPager pager;  
  3.     List<View> views;  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_main);  
  9.           
  10.         pager = (ViewPager)findViewById(R.id.view_pager);  
  11.         views = new ArrayList<View>();  
  12.           
  13.         LayoutInflater mLi = LayoutInflater.from(this);    
  14.         View view1 = mLi.inflate(R.layout.view1, null);    
  15.         View view2 = mLi.inflate(R.layout.view2, null);    
  16.         View view3 = mLi.inflate(R.layout.view3, null);    
  17.         views.add(view3);    
  18.         views.add(view2);     
  19.         views.add(view1);  
  20.           
  21.         MyPagerAdapter adapter = new MyPagerAdapter();  
  22.         pager.setAdapter(adapter);  
  23.     }  
  24.       
  25.       
  26.     class MyPagerAdapter extends PagerAdapter{  
  27.   
  28.         @Override  
  29.         public int getCount() {  
  30.             // TODO Auto-generated method stub  
  31.             return views.size();  
  32.         }  
  33.   
  34.         @Override  
  35.         public boolean isViewFromObject(View arg0, Object arg1) {  
  36.             // TODO Auto-generated method stub  
  37.             return arg0 == arg1;    //这行代码很重要,它用于判断你当前要显示的页面  
  38.         }  
  39.           
  40.         @Override    
  41.         public Object instantiateItem(ViewGroup container, int position) {    
  42.             container.addView(views.get(position));    
  43.             Log.d("tag",String.valueOf(position));  
  44.             return views.get(position);    
  45.         }    
  46.         @Override    
  47.         public void destroyItem(ViewGroup container, int position, Object object) {    
  48.             container.removeView(views.get(position));    
  49.         }    
  50.           
  51.   
  52.           
  53.     }  
  54. }  

运行时,当我进入第一页时,log打印 0和1,当我滑到第二页时,log打印2,当我滑到第三页时,log没有输出信息。

所以我觉得是这样的。因为pagerAdapter是默认预加载前后一张的,所以当加载第一页时。调用了两次instantiateItem方法,第一次是加载本来的第一页,第二次是预加载第二页,所以log打印了0和1。可是显示的本来加载的第一页。当滑动到第二页时,只调用了一次instantiateItem方法,是因为本页已经在之前预加载过了,没有再调用instantiateItem方法也就是log不会再打印1,而是直接从ViewGroup中直接取出第二页用于显示。然后进行预加载第三页,所以这里会调用instantiateItem方法并打印2.接着滑动到第三页,由于第三节页也已经预加载过了,所以只是从ViewGroup取出第三页显示而不调用instantiateItem。但是由于预加载默认是前后一张,所以这时会从ViewGroup中取出第一页销毁。直到从第三页滑到第二页时才会再预加载第一页。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ViewGroup  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值