ViewPager

作者: 涛涛123759 | 来源:发表于2021-11-02 10:44 被阅读0次

    Android知识总结

    一、测量

    1.1、onMeasure

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //设置父容器测量高度,没有根据子视图决定,
        //导致子view无法决定父容器的大小,这时需要我们重写  onMeasure 方法。
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
                getDefaultSize(0, heightMeasureSpec));
     
        final int measuredWidth = getMeasuredWidth();
        final int maxGutterSize = measuredWidth / 10;
        //设置mGutterSize的值,后面再讲mGutterSize
        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);
     
        // ViewPager的显示区域只能显示对于一个View
        //childWidthSize和childHeightSize为一个View的可用宽高大小
        //即去除了ViewPager内边距后的宽高
        int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();
        int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
     
        //1.先对Decor View进行测量
        //下面这个循环是只针对Decor View的,即用于装饰ViewPager的View
        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();
                //1.1 如果该View是Decor View,即用于装饰ViewPager的View
                if (lp != null && lp.isDecor) {
                    //1.2 获取Decor View的在水平方向和竖直方向上的Gravity
                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                    //1.3 默认Dedor View模式对应的宽高是wrap_content
                    int widthMode = MeasureSpec.AT_MOST;
                    int heightMode = MeasureSpec.AT_MOST;
     
                    //1.4 记录Decor View是在垂直方向上还是在水平方向上占用空间
                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;
     
                    //1.5 consumeHorizontal:如果是在垂直方向上占用空间,
                    // 那么水平方向就是match_parent,即EXACTLY
                    //而垂直方向上具体占用多少空间,还得由Decor View决定
                    //consumeHorizontal也是同理
                    if (consumeVertical) {
                        widthMode = MeasureSpec.EXACTLY;
                    } else if (consumeHorizontal) {
                        heightMode = MeasureSpec.EXACTLY;
                    }
                    //1.6 宽高大小,初始化为ViewPager可视区域中子View可用空间
                    int widthSize = childWidthSize;
                    int heightSize = childHeightSize;
                    //1.7 如果宽度不是wrap_content,那么width的测量模式就是EXACTLY
                    //如果宽度既不是wrap_content又不是match_parent,那么说明是用户
                    //在布局文件写的具体的尺寸,直接将widthSize设置为这个具体尺寸
                    if (lp.width != LayoutParams.WRAP_CONTENT) {
                        widthMode = MeasureSpec.EXACTLY;
                        if (lp.width != LayoutParams.FILL_PARENT) {
                            widthSize = lp.width;
                        }
                    }
                    //1.8 同1.7
                    if (lp.height != LayoutParams.WRAP_CONTENT) {
                        heightMode = MeasureSpec.EXACTLY;
                        if (lp.height != LayoutParams.FILL_PARENT) {
                            heightSize = lp.height;
                        }
                    }
                    //1.9 合成Decor View的宽高specification(包含尺寸和模式的整数)
                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
                    //1.10 对DecorView进行测量
                    child.measure(widthSpec, heightSpec);
                    //1.11 如果Decor View占用了ViewPager的垂直方向的空间
                    //需要将子View的竖直方向可用的空间减去DecorView的高度,
                    //同理,水平方向上也做同样的处理
                    if (consumeVertical) {
                        childHeightSize -= child.getMeasuredHeight();
                    } else if (consumeHorizontal) {
                        childWidthSize -= child.getMeasuredWidth();
                    }
                }
            }
        }
     
        //2.子View默认宽高的specification(包含尺寸和模式的整数)
        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);
     
        //3.确保我们需要显示的fragment已经被我们创建好了
        //populate()比较复杂,后面再详细介绍
        mInLayout = true;
        populate();
        mInLayout = false;
     
        // 4.再对子View进行测量
        size = getChildCount();
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            //4.1 visibility为GONE的无需测量
            if (child.getVisibility() != GONE) {
                if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child
                        + ": " + mChildWidthMeasureSpec);
                //4.2 获取子View的LayoutParams
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                //4.3 只针对子View而不对Decor View测量
                if (lp == null || !lp.isDecor) {
                    //4.4 LayoutParams的widthFactor是取值为[0,1]的浮点数,
                    // 用于表示子View占ViewPager显示区域中子View可用宽度的比例,
                    // 即(childWidthSize * lp.widthFactor)表示当前子View的实际宽度
                    final int widthSpec = MeasureSpec.makeMeasureSpec(
                            (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);
                    //4.5 对当前子View进行测量
                    child.measure(widthSpec, mChildHeightMeasureSpec);
                }
            }
        }
    }
    

    1.2、onLayout

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //1.以下局部变量很简单,不再解释
        final int count = getChildCount();
        int width = r - l;
        int height = b - t;
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();
        final int scrollX = getScrollX();
        //2.Decor View 数量
        int decorCount = 0;
     
        //3.首先对Decor View进行layout,再对普通子View进行layout,
        // 之所以先对Decor View布局,是为了让普通子View能有合适的偏移
        //下面循环主要是针对Decor View
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            //3.1 visibility不为GONE才layout
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                //3.2 左边和顶部的边距初始化为0
                int childLeft = 0;
                int childTop = 0;
                if (lp.isDecor) {//3.3 只针对Decor View
                    //3.4 获取水平或垂直方向上的Gravity
                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                    //3.5 根据水平方向上的Gravity,确定childLeft以及paddingRight
                    switch (hgrav) {
                        default://没有设置水平方向Gravity时(左中右),childLeft就取paddingLeft
                            childLeft = paddingLeft;
                            break;
                        case Gravity.LEFT://水平方向Gravity为left,Decor View往最左边靠
                            childLeft = paddingLeft;
                            paddingLeft += child.getMeasuredWidth();
                            break;
                        case Gravity.CENTER_HORIZONTAL://将Decor View居中摆放
                            childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
                                    paddingLeft);
                            break;
                        case Gravity.RIGHT://将Decor View往最右边靠
                            childLeft = width - paddingRight - child.getMeasuredWidth();
                            paddingRight += child.getMeasuredWidth();
                            break;
                    }
                    //3.6 与3.5同理
                    switch (vgrav) {
                        default:
                            childTop = paddingTop;
                            break;
                        case Gravity.TOP:
                            childTop = paddingTop;
                            paddingTop += child.getMeasuredHeight();
                            break;
                        case Gravity.CENTER_VERTICAL:
                            childTop = Math.max((height - child.getMeasuredHeight()) / 2,
                                    paddingTop);
                            break;
                        case Gravity.BOTTOM:
                            childTop = height - paddingBottom - child.getMeasuredHeight();
                            paddingBottom += child.getMeasuredHeight();
                            break;
                    }
                    //3.7 上面计算的childLeft是相对ViewPager的左边计算的,
                    //还需要加上x方向已经滑动的距离scrollX
                    childLeft += scrollX;
                    //3.8 对Decor View布局
                    child.layout(childLeft, childTop,
                            childLeft + child.getMeasuredWidth(),
                            childTop + child.getMeasuredHeight());
                    //3.9 将Decor View数量+1
                    decorCount++;
                }
            }
        }
        //4.普通子View的宽度
        final int childWidth = width - paddingLeft - paddingRight;
        // Page views. Do this once we have the right padding offsets from above.
        //5.下面针对普通子View布局,在此之前我们已经得到正确的偏移量了
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                //5.1 ItemInfo 是ViewPager静态内部类,
                // 它保存了普通子View的position、offset等信息,是对普通子View的一个抽象描述
                ItemInfo ii;
                //5.2 infoForChild通过传入View查询对应的ItemInfo对象
                if (!lp.isDecor && (ii = infoForChild(child)) != null) {
                //计算当前子View的左边偏移量
                    int loff = (int) (childWidth * ii.offset);
                    //将左边距+左边偏移量得到最终子View左边位置
                    int childLeft = paddingLeft + loff;
                    int childTop = paddingTop;
                    //5.3 如果当前子View需要进行测量(measure),当这个子View是在Layout期间新添加新的,
                    // 那么这个子View需要进行测量,即needsMeasure为true
                    if (lp.needsMeasure) {
                        //5.4 标记已经测量过了
                        lp.needsMeasure = false;
                        //5.5 下面过程跟onMeasure类似
                        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);
                    }
                    if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
                            + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
                            + "x" + child.getMeasuredHeight());
                    //5.6 对普通子View进行layout
                    child.layout(childLeft, childTop,
                            childLeft + child.getMeasuredWidth(),
                            childTop + child.getMeasuredHeight());
                }
            }
        }
        //6. 将部分局部变量保存到实例变量中
        mTopPageBounds = paddingTop;
        mBottomPageBounds = height - paddingBottom;
        mDecorChildCount = decorCount;
        //7. 如果是第一次layout,则将ViewPager滑动到第一个子View的位置
        if (mFirstLayout) {
            scrollToItem(mCurItem, false, 0, false);
        }
        //8. 标记已经布局过了,即不再是第一次布局了
        mFirstLayout = false;
    }
    

    二、执行流程

        public boolean onTouchEvent(MotionEvent ev) {
            switch (action & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    mScroller.abortAnimation();
                    mPopulatePending = false;
                    populate();
    
                    // Remember where the motion event started
                    mLastMotionX = mInitialMotionX = ev.getX();
                    mLastMotionY = mInitialMotionY = ev.getY();
                    mActivePointerId = ev.getPointerId(0);
                    break;
                }
            }
        }
    
        void populate() {
            populate(mCurItem);
        }
    
        void populate(int newCurrentItem) {
            ItemInfo oldCurInfo = null;
            if (mCurItem != newCurrentItem) {
                oldCurInfo = infoForPosition(mCurItem);
                mCurItem = newCurrentItem;
            }
    
            if (mAdapter == null) {
                sortChildDrawingOrder();
                return;
            }
    
            if (mPopulatePending) {
                if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
                sortChildDrawingOrder();
                return;
            }
            if (getWindowToken() == null) {
                return;
            }
            // 开始适配
            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);
    
            if (N != mExpectedAdapterCount) {
                String resName;
                try {
                    resName = getResources().getResourceName(getId());
                } catch (Resources.NotFoundException e) {
                    resName = Integer.toHexString(getId());
                }
                throw new IllegalStateException("The application's PagerAdapter changed the adapter's"
                        + " contents without calling PagerAdapter#notifyDataSetChanged!"
                        + " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N
                        + " Pager id: " + resName
                        + " Pager class: " + getClass()
                        + " Problematic adapter: " + mAdapter.getClass());
            }
    
            // Locate the currently focused item or add it if needed.
            int curIndex = -1;
            ItemInfo curItem = null;
            for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
                final ItemInfo ii = mItems.get(curIndex);
                if (ii.position >= mCurItem) {
                    if (ii.position == mCurItem) curItem = ii;
                    break;
                }
            }
    
            if (curItem == null && N > 0) {
                // 创建适配的Item数据
                curItem = addNewItem(mCurItem, curIndex);
            }
    
            // 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;
                for (int pos = mCurItem - 1; pos >= 0; pos--) {
                    if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                        if (ii == null) {
                            break;
                        }
                        if (pos == ii.position && !ii.scrolling) {
                            //移出当前位置 item
                            mItems.remove(itemIndex);
                            // 销毁适配的Item数据
                            mAdapter.destroyItem(this, pos, ii.object);
                            if (DEBUG) {
                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos
                                        + " view: " + ((View) ii.object));
                            }
                            itemIndex--;
                            curIndex--;
                            ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                        }
                    } else if (ii != null && pos == ii.position) {
                        extraWidthLeft += ii.widthFactor;
                        itemIndex--;
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    } else {
                        ii = addNewItem(pos, itemIndex + 1);
                        extraWidthLeft += ii.widthFactor;
                        curIndex++;
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    }
                }
    
                float extraWidthRight = curItem.widthFactor;
                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);
                            itemIndex++;
                            extraWidthRight += ii.widthFactor;
                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                        }
                    }
                }
    
                calculatePageOffsets(curItem, curIndex, oldCurInfo);
                // 设置当前显示的Item
                mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
            }
    
            if (DEBUG) {
                Log.i(TAG, "Current page list:");
                for (int i = 0; i < mItems.size(); i++) {
                    Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
                }
            }
            // 通过事务的方式,执行fragment生命周期函数
            mAdapter.finishUpdate(this);
    
            //获取 item 的个数
            final int childCount = getChildCount();
            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);
                    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(View.FOCUS_FORWARD)) {
                                break;
                            }
                        }
                    }
                }
            }
        }
    

    1.1、开始适配

    调用PagerAdapter中的startUpdate方法

        public void startUpdate(@NonNull ViewGroup container) {
            startUpdate((View) container);
        }
    
    • 1)、执行FragmentPagerAdapter
        public void startUpdate(@NonNull ViewGroup container) {
            if (container.getId() == View.NO_ID) {
                throw new IllegalStateException("ViewPager with adapter " + this
                        + " requires a view id");
            }
        }
    
    • 2)、执行FragmentStatePagerAdapter
        public void startUpdate(@NonNull ViewGroup container) {
            if (container.getId() == View.NO_ID) {
                throw new IllegalStateException("ViewPager with adapter " + this
                        + " requires a view id");
            }
        }
    

    1.2、创建 item

    调用PagerAdapter中的addNewItem方法

        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;
        }
    
    • 1)、执行FragmentPagerAdapter
        public Object instantiateItem(@NonNull 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 不是当前的fragment
            if (fragment != mCurrentPrimaryItem) {
                fragment.setMenuVisibility(false);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
                } else {
                     //创建的fragment 不显示
                    fragment.setUserVisibleHint(false);
                }
            }
    
            return fragment;
        }
    
    • 2)、执行FragmentStatePagerAdapter
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            if (mFragments.size() > position) {
                Fragment f = mFragments.get(position);
                if (f != null) {
                    return f;
                }
            }
    
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
    
            Fragment fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
            if (mSavedState.size() > position) {
                Fragment.SavedState fss = mSavedState.get(position);
                if (fss != null) {
                    fragment.setInitialSavedState(fss);
                }
            }
            while (mFragments.size() <= position) {
                mFragments.add(null);
            }
            fragment.setMenuVisibility(false);
            if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
                fragment.setUserVisibleHint(false);
            }
    
            mFragments.set(position, fragment);
            mCurTransaction.add(container.getId(), fragment);
    
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
            }
    
            return fragment;
        }
    

    1.3、销毁适配的Item

    调用PagerAdapter中的destroyItem方法

        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            destroyItem((View) container, position, object);
        }
    
    
    • 1)、执行FragmentPagerAdapter
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment) object;
    
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
            if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                    + " v=" + (fragment.getView()));
            //解绑视图,fragment实例还保留在FragmentManager 中
            mCurTransaction.detach(fragment);
            if (fragment == mCurrentPrimaryItem) {
                mCurrentPrimaryItem = null;
            }
        }
    
    • 2)、执行FragmentStatePagerAdapter
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment) object;
    
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
            if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment)object).getView());
            while (mSavedState.size() <= position) {
                mSavedState.add(null);
            }
            //保存State 信息,可在 onSaveInstanceState(Bundle) 方法中保存fragment的 Bundle 信息
            mSavedState.set(position, fragment.isAdded()
                    ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
            mFragments.set(position, null);
            //移出视图, FragmentManager 中的fragment会被彻底移除
            mCurTransaction.remove(fragment);
            if (fragment == mCurrentPrimaryItem) {
                mCurrentPrimaryItem = null;
            }
        }
    

    1.4、设置当前显示的Item

    调用PagerAdapter中的addNewItem方法

        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            setPrimaryItem((View) container, position, object);
        }
    
    • 1)、执行FragmentPagerAdapter
        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment)object;
            if (fragment != mCurrentPrimaryItem) {
                if (mCurrentPrimaryItem != null) {
                    mCurrentPrimaryItem.setMenuVisibility(false);
                    if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                        if (mCurTransaction == null) {
                            mCurTransaction = mFragmentManager.beginTransaction();
                        }
                        mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                    } else {
                        //当前fragment不显示
                        mCurrentPrimaryItem.setUserVisibleHint(false);
                    }
                }
                fragment.setMenuVisibility(true);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
                } else {
                    //创建的 fragment 显示
                    fragment.setUserVisibleHint(true);
                }
                //更新 mCurrentPrimaryItem 
                mCurrentPrimaryItem = fragment;
            }
        }
    
    • 2)、执行FragmentStatePagerAdapter
        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment)object;
            if (fragment != mCurrentPrimaryItem) {
                if (mCurrentPrimaryItem != null) {
                    mCurrentPrimaryItem.setMenuVisibility(false);
                    if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                        if (mCurTransaction == null) {
                            mCurTransaction = mFragmentManager.beginTransaction();
                        }
                        mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                    } else {
                        //setUserVisibleHint 不是生命周期方法,在 fragment 提交事务之前执行
                        //导致在生命周期之前执行
                        mCurrentPrimaryItem.setUserVisibleHint(false);
                    }
                }
                fragment.setMenuVisibility(true);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
                } else {
                    fragment.setUserVisibleHint(true);
                }
    
                mCurrentPrimaryItem = fragment;
            }
        }
    

    1.5、结束适配

    调用PagerAdapter中的finishUpdate方法

        public void finishUpdate(@NonNull ViewGroup container) {
            finishUpdate((View) container);
        }
    
    • 1)、执行FragmentPagerAdapter
        public void finishUpdate(@NonNull ViewGroup container) {
            if (mCurTransaction != null) {
                //提交事务执行生命周期函数,由此决定setUserVisibleHint在生命周期之前执行
                mCurTransaction.commitNowAllowingStateLoss();
                mCurTransaction = null;
            }
        }
    
    • 2)、执行FragmentStatePagerAdapter
        public void finishUpdate(@NonNull ViewGroup container) {
            if (mCurTransaction != null) {
                mCurTransaction.commitNowAllowingStateLoss();
                mCurTransaction = null;
            }
        }
    

    三、Adapter

    2.1、FragmentStatePagerAdapter 不可以缓存

    FragmentStatePagerAdapter 会销毁不需要的fragment,事务提交后,FragmentManager 中的fragment会被彻底移除。

    public class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
    
        private static final String TAG = MyFragmentPagerAdapter.class.getSimpleName();
    
        private List<Fragment> fragmentList;
    
        public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> flist) {
            super(fm,FragmentStatePagerAdapter.BEHAVIOR_SET_USER_VISIBLE_HINT);
            fragmentList = flist;
        }
    
        @Override
        public Fragment getItem(int i) {
            Log.v(TAG, "getItem: " + i);
            return fragmentList.get(i);
        }
    
        @Override
        public int getCount() {
            return fragmentList.size();
        }
    
        // 早期 Android   监听 ViewPager 的 Item  什么时候移除  记录下标
        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            super.destroyItem(container, position, object);
        }
    }
    

    2.2、FragmentPagerAdapter 可以缓存

    对于不再需要的fragment, FragmentPagerAdapter 会选择调用事务的 detach(Fragment) 方法来处理它,而非 remove(Fragment) 方法。
    FragmentPagerAdapter 只是销毁了fragment的视图,fragment实例还保留在FragmentManager 中。

    public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
        private static final String TAG = "MyFragmentPagerAdapter";
    
        private List<Fragment> fragmentList;
        public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> flist) {
            super(fm);
            fragmentList = flist;
        }
        @Override
        public Fragment getItem(int i) {
            return fragmentList.get(i);
        }
    
        @Override
        public int getCount() {
            return fragmentList.size();
        }
    }
    

    四、重写 onMeasure 方法

    public class MyViewPager extends ViewPager {
        public MyViewPager(@NonNull Context context) {
            super(context);
        }
    
        public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        //主要解决父View大小没有有子View决定的问题
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int hSpec, wSpec;
            int finalH = 0, finalW = 0;
            final int count = getChildCount();
            View child;
            //找出子View中最大的宽和高
            for (int i = 0; i < count; i++) {
                child = getChildAt(i);
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                int childHeight = child.getMeasuredHeight();
                int childWidth = child.getMeasuredWidth();
                finalH = Math.max(finalH, childHeight);
                finalW = Math.max(finalW, childWidth);
            }
            //设置模式为 MeasureSpec.EXACTLY
            wSpec = MeasureSpec.makeMeasureSpec(finalW, MeasureSpec.EXACTLY);
            hSpec = MeasureSpec.makeMeasureSpec(finalH, MeasureSpec.EXACTLY);
            setMeasuredDimension(wSpec, hSpec);
        }
    }
    

    相关文章

      网友评论

          本文标题:ViewPager

          本文链接:https://www.haomeiwen.com/subject/blotaltx.html