ViewPager 简单分析

来源:互联网 发布:psp游戏数据已损坏 编辑:程序博客网 时间:2024/06/05 16:43
工作中用到了FragmentPagerAdapter ,所以先看看FragmentPagerAdapter 的实现。
本文写得比较乱,仅仅是个人笔记,很多地方本人也没弄清楚
public abstract class FragmentPagerAdapter extends PagerAdapter

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
实际上 只是在instantiateItem 中 调用了getItem(position); 这个抽象方法。  其他方法这个adapter 已经基本实现,所以user使用fpadapter的时候 实现getItem方法 就 是给 instantiateItem返回  fragment了。
 
然而 返回的是一个fragment,并不是view,且PagerAdapter中instantiateItem 要求的只是一个object。 那么ViewPager如何处理这个object?
    ItemInfo addNewItem(int position, int index) {
        ItemInfo ii = new ItemInfo();
        ii.position = position;
        ii.object = mAdapter.instantiateItem(this, position);
        ii.widthFactor = mAdapter.getPageWidth(position);
        if (index < 0 || index >= mItems.size()) {
            mItems.add(ii);
        } else {
            mItems.add(index, ii);
        }
        return ii;
    }

可见会在addNewItem中 将 这个 object封装 成 一个 iteminfo。
private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
同时将这个info 放在了一个arraylist中、

正在使用 MyPagerAdapter extends PagerAdapter 的情况下,instantiateItem方法
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = View.inflate(MainActivity.this, R.layout.adapter_ad, null);
ImageView imageView = (ImageView) view.findViewById(R.id.image);
imageView.setBackgroundResource(list.get(position%list.size()).getIconResId());
//将view对象添加到viewpager,交给它管理
container.addView(view);
return view;
}
实际上每次返回的view 都要user手动 添加到viewpager中, destory的时候 要手动remove。 保证 viewpager 缓存pager 数目固定 ,与viewpager内部的items数组 数目应该也保持一致。
猜测fragmentpageradapter应该是在下面某句话中 将fragment的视图addview到了viewpager中? 望指正。
if (fragment != null) {    if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);    mCurTransaction.attach(fragment);} else {    fragment = getItem(position);    if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);    mCurTransaction.add(container.getId(), fragment,            makeFragmentName(container.getId(), itemId));}
addNewItem 会被populate 调用

populate 这个方法200 行,逻辑比较复杂。 大致是 处理 populate(mCurItem)
负责处理当前位置 int currentItem 的 item的显示,以及 这个位置前后 item的 进出,显示,index位置,在 mItems中是否移除 加入 等。

populate在整个viewpager中 有5处? 地方被调用。
1    private final Runnable mEndScrollRunnable = new Runnable() {
        public void run() {
            setScrollState(SCROLL_STATE_IDLE);
            populate();
        }
    };
    用处不明
2 setAdapter时。line447
     else if (!wasFirstLayout) {
                populate();
            } else {
                requestLayout();
            }
  看出来应该是第一次setAdapter会造成 第一次layout,所以调用了 populate来 additem,获得 view展示。
3.public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer)  line 656  
    用户调用此方法改变page的 位置时?
4.public void setOffscreenPageLimit(int limit)
            if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    这是一个比较常用的方法。 可以看到 当user 设置的 offsreen页面 与当前值不符合的时候,  会调用 populate,来 增加或者减少 当前缓存页面。

5.void smoothScrollTo(int x, int y, int velocity)  line838
    平滑移动时
6.onMeasure()
            // Make sure we have created all fragments that we need to have shown.
        mInLayout = true;
        populate();
        mInLayout = false;
        可见,populate 完后,就把 layout标记设置为false。 确实 是用来 在滑动,layout情况下页面改变后加载页面的。
7.public boolean onInterceptTouchEvent(MotionEvent ev)
                    // Let the user 'catch' the pager as it animates.
                    mScroller.abortAnimation();
                    mPopulatePending = false;
                    populate();
                    mIsBeingDragged = true;
  在ACTION_DOWN分支中。  
8. public boolean onTouchEvent(MotionEvent ev)  line 2043
    依然是在ACTION_DOWN分支中。
                 mScroller.abortAnimation();
                mPopulatePending = false;
                populate();

                // Remember where the motion event started

    
    一共8处。


总结一下:
1.viewpager 并没有像listview一样,将user(使用这个控件的人) 返回的view 内部自己addview 到 viewgroip的成员mChildren[] 数组中。需要user手动在instantiateItem方法中手动调用一次addview。
 同时viewpager也没有像listview一样 有从mChildren[]中移除子view的操作,需要用户在destory方法中手动remove。
为何viewpager要这么设计?  
2.viewpager内部自己定义了一个ArrayList的 mItems成员 来保存 user返回的object(view),猜想 这个mItems的数量应该和mChildren[]数量应该是一致的?
3.viewpager如果有上百个页面,开始只加载3个,那么在快速滑动的时候 肯定是不断的在创建新的view销毁旧的view,并没有复用机制?viewpager没有复用机制 是因为考虑到每个page条目可能差别较大无法复用?不像listview一样 都属于同一类型?

0 0
原创粉丝点击