您的位置:首页 > 其它

ViewPager 简单分析

2016-04-05 16:13 393 查看
工作中用到了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一样 都属于同一类型?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: