关于PagerAdapter的instantiateItem方法

来源:互联网 发布:淘宝店铺交易记录 编辑:程序博客网 时间:2024/06/05 18:22

在为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
原创粉丝点击