ViewPager简单实现分析

来源:互联网 发布:如何创建域名 编辑:程序博客网 时间:2024/05/29 20:00
 public void setAdapter(PagerAdapter adapter) {                final PagerAdapter oldAdapter = mAdapter;        mAdapter = adapter;        mExpectedAdapterCount = 0;        if (mAdapter != null) {            if (mObserver == null) {                mObserver = new PagerObserver();            }            mAdapter.registerDataSetObserver(mObserver);                       populate();                }    }void populate() {        populate(mCurItem);    }void populate(int newCurrentItem) {        ItemInfo oldCurInfo = null;        int focusDirection = View.FOCUS_FORWARD;        mAdapter.startUpdate(this);// 默认空实现        final int pageLimit = mOffscreenPageLimit;        final int startPos = Math.max(0, mCurItem - pageLimit);        final int N = mAdapter.getCount();        final int endPos = Math.min(N-1, mCurItem + pageLimit);              // Locate the currently focused item or add it if needed.        int curIndex = -1;        ItemInfo curItem = null;        if (curItem == null && N > 0) {            curItem = addNewItem(mCurItem, curIndex);// curIndex==0        }        // Fill 3x the available width or up to the number of offscreen        // pages requested to either side, whichever is larger.        // If we have no current item we have no work to do.        if (curItem != null) {            float extraWidthLeft = 0.f;            int itemIndex = curIndex - 1;            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;            final int clientWidth = getClientWidth();            final float leftWidthNeeded = clientWidth <= 0 ? 0 :                    2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;                 float extraWidthRight = curItem.widthFactor;// extraWidthRight==1            itemIndex = curIndex + 1;            if (extraWidthRight < 2.f) {                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                final float rightWidthNeeded = clientWidth <= 0 ? 0 :                        (float) getPaddingRight() / (float) clientWidth + 2.f;                for (int pos = mCurItem + 1; pos < N; pos++) {                    if (extraWidthRight >= rightWidthNeeded && pos > endPos) {                        if (ii == null) {                            break;                        }                        if (pos == ii.position && !ii.scrolling) {                            mItems.remove(itemIndex);                            mAdapter.destroyItem(this, pos, ii.object);                            if (DEBUG) {                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos +                                        " view: " + ((View) ii.object));                            }                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                        }                    } else if (ii != null && pos == ii.position) {                        extraWidthRight += ii.widthFactor;                        itemIndex++;                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                    } else {                        ii = addNewItem(pos, itemIndex);// 猜测应该走这一步,添加所有item到数组中                        itemIndex++;                        extraWidthRight += ii.widthFactor;                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                    }                }            }            calculatePageOffsets(curItem, curIndex, oldCurInfo);        }            // Check width measurement of current pages and drawing sort order.        // Update LayoutParams as needed.        final int childCount = getChildCount();// 之前调用adapter的instantiateItem(ViewGroup, int)添加了view        for (int i = 0; i < childCount; i++) {            final View child = getChildAt(i);            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            lp.childIndex = i;            if (!lp.isDecor && lp.widthFactor == 0.f) {                // 0 means requery the adapter for this, it doesn't have a valid width.                final ItemInfo ii = infoForChild(child);// 获取view对应的ItemInfo                if (ii != null) {                    lp.widthFactor = ii.widthFactor;                    lp.position = ii.position;                }            }        }        sortChildDrawingOrder();        if (hasFocus()) {            View currentFocused = findFocus();            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;            if (ii == null || ii.position != mCurItem) {                for (int i=0; i<getChildCount(); i++) {                    View child = getChildAt(i);                    ii = infoForChild(child);                    if (ii != null && ii.position == mCurItem) {                        if (child.requestFocus(focusDirection)) {                            break;                        }                    }                }            }        }    }    ItemInfo addNewItem(int position, int index) {ItemInfo ii = new ItemInfo();ii.position = position;ii.object = mAdapter.instantiateItem(this, position);ii.widthFactor = mAdapter.getPageWidth(position);mItems.add(index, ii);// index==0return ii;}// 思路:和《艺术探索》中介绍的测量每个孩子的步骤类似@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // For simple implementation, our internal size is always 0.        // We depend on the container to specify the layout size of        // our view.  We can't really know what it is since we will be        // adding and removing different arbitrary views and do not        // want the layout to change as this happens.        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),                getDefaultSize(0, heightMeasureSpec));        final int measuredWidth = getMeasuredWidth();        final int maxGutterSize = measuredWidth / 10;        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);        // Children are just made to fill our space.        int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();        int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();        /*         * Make sure all children have been properly measured. Decor views first.         * Right now we cheat and make this less complicated by assuming decor         * views won't intersect. We will pin to edges based on gravity.         */        int size = getChildCount();        for (int i = 0; i < size; ++i) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (lp != null && lp.isDecor) {                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;                    int widthMode = MeasureSpec.AT_MOST;                    int heightMode = MeasureSpec.AT_MOST;                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;                    if (consumeVertical) {                        widthMode = MeasureSpec.EXACTLY;                    } else if (consumeHorizontal) {                        heightMode = MeasureSpec.EXACTLY;                    }                    int widthSize = childWidthSize;                    int heightSize = childHeightSize;                    if (lp.width != LayoutParams.WRAP_CONTENT) {                        widthMode = MeasureSpec.EXACTLY;                        if (lp.width != LayoutParams.FILL_PARENT) {                            widthSize = lp.width;                        }                    }                    if (lp.height != LayoutParams.WRAP_CONTENT) {                        heightMode = MeasureSpec.EXACTLY;                        if (lp.height != LayoutParams.FILL_PARENT) {                            heightSize = lp.height;                        }                    }                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);                    child.measure(widthSpec, heightSpec);                    if (consumeVertical) {                        childHeightSize -= child.getMeasuredHeight();                    } else if (consumeHorizontal) {                        childWidthSize -= child.getMeasuredWidth();                    }                }            }        }        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);        // Make sure we have created all fragments that we need to have shown.        mInLayout = true;        populate();        mInLayout = false;        // Page views next.        size = getChildCount();        for (int i = 0; i < size; ++i) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child                        + ": " + mChildWidthMeasureSpec);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (lp == null || !lp.isDecor) {                    final int widthSpec = MeasureSpec.makeMeasureSpec(                            (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);                    child.measure(widthSpec, mChildHeightMeasureSpec);                }            }        }    }// 思路:把每个已经测量过的view进行布局@Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int count = getChildCount();        int width = r - l;// 此width等于屏幕宽度(默认)        int height = b - t;        int paddingLeft = getPaddingLeft();        int paddingTop = getPaddingTop();        int paddingRight = getPaddingRight();        int paddingBottom = getPaddingBottom();        final int scrollX = getScrollX();         final int childWidth = width - paddingLeft - paddingRight;        // Page views. Do this once we have the right padding offsets from above.        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                ItemInfo ii;                if (!lp.isDecor && (ii = infoForChild(child)) != null) {                    int loff = (int) (childWidth * ii.offset);// 计算偏移。childWidth等于屏幕宽度                    int childLeft = paddingLeft + loff;                    int childTop = paddingTop;                    if (lp.needsMeasure) {                        // This was added during layout and needs measurement.                        // Do it now that we know what we're working with.                        lp.needsMeasure = false;                        final int widthSpec = MeasureSpec.makeMeasureSpec(                                (int) (childWidth * lp.widthFactor),                                MeasureSpec.EXACTLY);                        final int heightSpec = MeasureSpec.makeMeasureSpec(                                (int) (height - paddingTop - paddingBottom),                                MeasureSpec.EXACTLY);                        child.measure(widthSpec, heightSpec);                    }                              child.layout(childLeft, childTop,                            childLeft + child.getMeasuredWidth(),                            childTop + child.getMeasuredHeight());                }            }        }    }总结一:ViewPager和普通的PagerAdapter如何结合/** * 默认实现--默认List中成员是View类型 */@Overridepublic Object instantiateItem(ViewGroup viewGroup, int position) {// 向ViewPager中添加一个ImageViewImageView view = (ImageView) (getList().get(position));if (!(view instanceof View)) {throw new RuntimeException("List的成员view不是真正的View类型,请检查List数据!");}viewGroup.addView(view);// 把添加的ImageView返回回去return view;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}分析:如上述代码所述,当instantiateItem的时候,viewGroup.addView(view);添加子view到ViewPager中,这样ViewPager构造出List<ItemInfo>来维护所有view的相关信息。当measure和layout的时候,把所有view添加到ViewPager这个ViewGroup中。这样完成了界面的绘制工作。事件:响应各种滑动事件总结二:ViewPager和FragmentPagerAdapter如何结合@Override    public Object instantiateItem(ViewGroup container, int position) {        if (mCurTransaction == null) {            mCurTransaction = mFragmentManager.beginTransaction();        }        final long itemId = getItemId(position);        // 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));// 把当前Fragment添加到ViewGroup(也就是ViewPager)        }        return fragment;    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {        if (mCurTransaction == null) {            mCurTransaction = mFragmentManager.beginTransaction();        }        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object                + " v=" + ((Fragment)object).getView());        mCurTransaction.detach((Fragment)object);    }分析:instantiateItem的时候Fragment利用ViewGroup(ViewPager),把Fragment添加到ViewGroup中(核心步骤)改变pager的时候,也销毁旧的fragment,并创建新的fragment,并添加到viewgroup来实现切换

0 0
原创粉丝点击