zoukankan      html  css  js  c++  java
  • 1-VIII--ViewPager的基本使用

    零、前言

    [1].ViewPager顾名思义是将若干视图一页一页的展现
    [2].ViewPager和Fragment郎才女貌,天造之合,在加个TabLayout简直和睦一家人
    [3].本文介绍ViewPager的基本使用


    一、ViewPager的基本使用

    1.activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <android.support.v4.view.ViewPager
            android:id="@+id/vp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
    
    </android.support.constraint.ConstraintLayout>
    
    2.用来填充的布局:item_viewpager.xml
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/id_tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
    
    </android.support.constraint.ConstraintLayout>
    
    3.MainActivity
    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        @BindView(R.id.vp)
        LazyViewPager mVp;
        private ArrayList<View> mViews;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            //[1].创建容器盛放数据
            mViews = new ArrayList<>();
            for (int i = 0; i < 5; i++) {
                View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_viewpager, null);
                view.setBackgroundColor(ColUtils.randomRGB());//自定义随机颜色方法
                TextView tv = view.findViewById(R.id.id_tv_content);
                tv.setText("Pager" + i);
                mViews.add(view);
            }
            //[3]设置适配器,为ViewPager填充数据
            mVp.setAdapter(new MyViewPagerAdapter());
        }
    
        /**
         * [2].适配器类
         */
        class MyViewPagerAdapter extends PagerAdapter {
            /**
             * [2-1]页面数量
             *
             * @return
             */
            @Override
            public int getCount() {
                return mViews.size();
            }
    
            /**
             * [2-2]初始化页面
             *
             * @param container 视图容器
             * @param position  当前位置
             * @return 视图
             */
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                Log.e(TAG, "instantiateItem-----: " + position);
    
                container.addView(mViews.get(position));
                return mViews.get(position);
            }
    
            /**
             * [2-3]视图页是否和对象相关联
             *
             * @param view   视图页
             * @param object 对象
             * @return 视图页是否和对象相关联
             */
            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }
    
    
            /**
             * [2-4]页面销毁时调用
             *
             * @param container 视图容器
             * @param position  位置
             * @param object 对象
             */
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView((View) object);
            }
        }
    
    随机颜色方法
        /**
         * 返回随机颜色
         *
         * @return 随机颜色
         */
        public static int randomRGB() {
            Random random = new Random();
            int r = 30 + random.nextInt(200);
            int g = 30 + random.nextInt(200);
            int b = 30 + random.nextInt(200);
            return Color.rgb( r, g, b);
        }
    
    ViewPager默认加载方式
    9414344-9c4dde40c50e88bc.png
    ViewPager默认加载方式.png

    屏蔽预加载

    预加载是为了让滑动流畅,预先或事后保证当前页的左右都有页面被缓存(首左,尾右除外)
    某些情况下我们不希望预加载,那就要找出罪魁祸首,是它,就是它:private static final int DEFAULT_OFFSCREEN_PAGES = 1;
    私有静态final的字段,呵呵,改不了了。。。。解决方法:自定义控件把某版的ViewPager全考出来,改个字段


    本文由张风捷特烈原创,转载请注明
    更多安卓技术欢迎访问:https://www.jianshu.com/c/004f3fe34c94
    张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com
    你的喜欢与支持将是我最大的动力

    附录、网上找的一个:LazyViewPager

    package com.toly1994.viii_viewpager;
    
    import android.content.Context;
    import android.database.DataSetObserver;
    import android.graphics.Canvas;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.os.SystemClock;
    import android.support.v4.os.ParcelableCompat;
    import android.support.v4.os.ParcelableCompatCreatorCallbacks;
    import android.support.v4.view.KeyEventCompat;
    import android.support.v4.view.MotionEventCompat;
    import android.support.v4.view.PagerAdapter;
    import android.support.v4.view.VelocityTrackerCompat;
    import android.support.v4.view.ViewCompat;
    import android.support.v4.view.ViewConfigurationCompat;
    import android.support.v4.widget.EdgeEffectCompat;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.FocusFinder;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.view.SoundEffectConstants;
    import android.view.VelocityTracker;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewGroup;
    import android.view.ViewParent;
    import android.view.accessibility.AccessibilityEvent;
    import android.view.animation.Interpolator;
    import android.widget.Scroller;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    
    /**
     * Layout manager that allows the user to flip left and right through pages of
     * data. You supply an implementation of a
     * {@link android.support.v4.view.PagerAdapter} to generate the pages that the
     * view shows.
     * <p/>
     * <p>
     * Note this class is currently under early design and development. The API will
     * likely change in later updates of the compatibility library, requiring
     * changes to the source code of apps when they are compiled against the newer
     * version.
     * </p>
     */
    public class LazyViewPager extends ViewGroup {
        private static final String TAG = "LazyViewPager";
        private static final boolean DEBUG = false;
    
        private static final boolean USE_CACHE = false;
    
        private static final int DEFAULT_OFFSCREEN_PAGES = 0;// 默认的加载页面,ViewPager是1个,所以会加载两个Fragment
        private static final int MAX_SETTLE_DURATION = 600; // ms
    
        static class ItemInfo {
            Object object;
            int position;
            boolean scrolling;
        }
    
        private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() {
            @Override
            public int compare(ItemInfo lhs, ItemInfo rhs) {
                return lhs.position - rhs.position;
            }
        };
    
        private static final Interpolator sInterpolator = new Interpolator() {
            public float getInterpolation(float t) {
                // _o(t) = t * t * ((tension + 1) * t + tension)
                // o(t) = _o(t - 1) + 1
                t -= 1.0f;
                return t * t * t + 1.0f;
            }
        };
    
        private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
    
        private PagerAdapter mAdapter;
        private int mCurItem; // Index of currently displayed page.
        private int mRestoredCurItem = -1;
        private Parcelable mRestoredAdapterState = null;
        private ClassLoader mRestoredClassLoader = null;
        private Scroller mScroller;
        private PagerObserver mObserver;
    
        private int mPageMargin;
        private Drawable mMarginDrawable;
    
        private int mChildWidthMeasureSpec;
        private int mChildHeightMeasureSpec;
        private boolean mInLayout;
    
        private boolean mScrollingCacheEnabled;
    
        private boolean mPopulatePending;
        private boolean mScrolling;
        private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
    
        private boolean mIsBeingDragged;
        private boolean mIsUnableToDrag;
        private int mTouchSlop;
        private float mInitialMotionX;
        /**
         * Position of the last motion event.
         */
        private float mLastMotionX;
        private float mLastMotionY;
        /**
         * ID of the active pointer. This is used to retain consistency during
         * drags/flings if multiple pointers are used.
         */
        private int mActivePointerId = INVALID_POINTER;
        /**
         * Sentinel value for no current active pointer. Used by
         * {@link #mActivePointerId}.
         */
        private static final int INVALID_POINTER = -1;
    
        /**
         * Determines speed during touch scrolling
         */
        private VelocityTracker mVelocityTracker;
        private int mMinimumVelocity;
        private int mMaximumVelocity;
        private float mBaseLineFlingVelocity;
        private float mFlingVelocityInfluence;
    
        private boolean mFakeDragging;
        private long mFakeDragBeginTime;
    
        private EdgeEffectCompat mLeftEdge;
        private EdgeEffectCompat mRightEdge;
    
        private boolean mFirstLayout = true;
    
        private OnPageChangeListener mOnPageChangeListener;
    
        /**
         * Indicates that the pager is in an idle, settled state. The current page
         * is fully in view and no animation is in progress.
         */
        public static final int SCROLL_STATE_IDLE = 0;
    
        /**
         * Indicates that the pager is currently being dragged by the user.
         */
        public static final int SCROLL_STATE_DRAGGING = 1;
    
        /**
         * Indicates that the pager is in the process of settling to a final
         * position.
         */
        public static final int SCROLL_STATE_SETTLING = 2;
    
        private int mScrollState = SCROLL_STATE_IDLE;
    
        /**
         * Callback interface for responding to changing state of the selected page.
         */
        public interface OnPageChangeListener {
    
            /**
             * This method will be invoked when the current page is scrolled, either
             * as part of a programmatically initiated smooth scroll or a user
             * initiated touch scroll.
             *
             * @param position             Position index of the first page currently being
             *                             displayed. Page position+1 will be visible if
             *                             positionOffset is nonzero.
             * @param positionOffset       Value from [0, 1) indicating the offset from the page at
             *                             position.
             * @param positionOffsetPixels Value in pixels indicating the offset from position.
             */
            public void onPageScrolled(int position, float positionOffset,
                                       int positionOffsetPixels);
    
            /**
             * This method will be invoked when a new page becomes selected.
             * Animation is not necessarily complete.
             *
             * @param position Position index of the new selected page.
             */
            public void onPageSelected(int position);
    
            /**
             * Called when the scroll state changes. Useful for discovering when the
             * user begins dragging, when the pager is automatically settling to the
             * current page, or when it is fully stopped/idle.
             *
             * @param state The new scroll state.
             * @see android.support.v4.view.ViewPager#SCROLL_STATE_IDLE
             * @see android.support.v4.view.ViewPager#SCROLL_STATE_DRAGGING
             * @see android.support.v4.view.ViewPager#SCROLL_STATE_SETTLING
             */
            public void onPageScrollStateChanged(int state);
        }
    
        /**
         * Simple implementation of the
         * interface with stub implementations of each method. Extend this if you do
         * not intend to override every method of
         */
        public static class SimpleOnPageChangeListener implements
                OnPageChangeListener {
            @Override
            public void onPageScrolled(int position, float positionOffset,
                                       int positionOffsetPixels) {
                // This space for rent
            }
    
            @Override
            public void onPageSelected(int position) {
                // This space for rent
            }
    
            @Override
            public void onPageScrollStateChanged(int state) {
                // This space for rent
            }
        }
    
        public LazyViewPager(Context context) {
            super(context);
            initViewPager();
        }
    
        public LazyViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
            initViewPager();
        }
    
        void initViewPager() {
            setWillNotDraw(false);
            setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
            setFocusable(true);
            final Context context = getContext();
            mScroller = new Scroller(context, sInterpolator);
            final ViewConfiguration configuration = ViewConfiguration.get(context);
            mTouchSlop = ViewConfigurationCompat
                    .getScaledPagingTouchSlop(configuration);
            mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
            mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
            mLeftEdge = new EdgeEffectCompat(context);
            mRightEdge = new EdgeEffectCompat(context);
    
            float density = context.getResources().getDisplayMetrics().density;
            mBaseLineFlingVelocity = 2500.0f * density;
            mFlingVelocityInfluence = 0.4f;
        }
    
        private void setScrollState(int newState) {
            if (mScrollState == newState) {
                return;
            }
    
            mScrollState = newState;
            if (mOnPageChangeListener != null) {
                mOnPageChangeListener.onPageScrollStateChanged(newState);
            }
        }
    
        public void setAdapter(PagerAdapter adapter) {
            if (mAdapter != null) {
                mAdapter.unregisterDataSetObserver(mObserver);
                mAdapter.startUpdate(this);
                for (int i = 0; i < mItems.size(); i++) {
                    final ItemInfo ii = mItems.get(i);
                    mAdapter.destroyItem(this, ii.position, ii.object);
                }
                mAdapter.finishUpdate(this);
                mItems.clear();
                removeAllViews();
                mCurItem = 0;
                scrollTo(0, 0);
            }
    
            mAdapter = adapter;
    
            if (mAdapter != null) {
                if (mObserver == null) {
                    mObserver = new PagerObserver();
                }
                mAdapter.registerDataSetObserver(mObserver);
                mPopulatePending = false;
                if (mRestoredCurItem >= 0) {
                    mAdapter.restoreState(mRestoredAdapterState,
                            mRestoredClassLoader);
                    setCurrentItemInternal(mRestoredCurItem, false, true);
                    mRestoredCurItem = -1;
                    mRestoredAdapterState = null;
                    mRestoredClassLoader = null;
                } else {
                    populate();
                }
            }
        }
    
        public PagerAdapter getAdapter() {
            return mAdapter;
        }
    
        /**
         * Set the currently selected page. If the ViewPager has already been
         * through its first layout there will be a smooth animated transition
         * between the current item and the specified item.
         *
         * @param item Item index to select
         */
        public void setCurrentItem(int item) {
            mPopulatePending = false;
            setCurrentItemInternal(item, !mFirstLayout, false);
        }
    
        /**
         * Set the currently selected page.
         *
         * @param item         Item index to select
         * @param smoothScroll True to smoothly scroll to the new item, false to transition
         *                     immediately
         */
        public void setCurrentItem(int item, boolean smoothScroll) {
            mPopulatePending = false;
            setCurrentItemInternal(item, smoothScroll, false);
        }
    
        public int getCurrentItem() {
            return mCurItem;
        }
    
        void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
            setCurrentItemInternal(item, smoothScroll, always, 0);
        }
    
        void setCurrentItemInternal(int item, boolean smoothScroll, boolean always,
                                    int velocity) {
            if (mAdapter == null || mAdapter.getCount() <= 0) {
                setScrollingCacheEnabled(false);
                return;
            }
            if (!always && mCurItem == item && mItems.size() != 0) {
                setScrollingCacheEnabled(false);
                return;
            }
            if (item < 0) {
                item = 0;
            } else if (item >= mAdapter.getCount()) {
                item = mAdapter.getCount() - 1;
            }
            final int pageLimit = mOffscreenPageLimit;
            if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
                // We are doing a jump by more than one page. To avoid
                // glitches, we want to keep all current pages in the view
                // until the scroll ends.
                for (int i = 0; i < mItems.size(); i++) {
                    mItems.get(i).scrolling = true;
                }
            }
    
            final boolean dispatchSelected = mCurItem != item;
            mCurItem = item;
            populate();
            final int destX = (getWidth() + mPageMargin) * item;
            if (smoothScroll) {
                smoothScrollTo(destX, 0, velocity);
                if (dispatchSelected && mOnPageChangeListener != null) {
                    mOnPageChangeListener.onPageSelected(item);
                }
            } else {
                if (dispatchSelected && mOnPageChangeListener != null) {
                    mOnPageChangeListener.onPageSelected(item);
                }
                completeScroll();
                scrollTo(destX, 0);
            }
        }
    
        public void setOnPageChangeListener(OnPageChangeListener listener) {
            mOnPageChangeListener = listener;
        }
    
        /**
         * Returns the number of pages that will be retained to either side of the
         * current page in the view hierarchy in an idle state. Defaults to 1.
         *
         * @return How many pages will be kept offscreen on either side
         * @see #setOffscreenPageLimit(int)
         */
        public int getOffscreenPageLimit() {
            return mOffscreenPageLimit;
        }
    
        /**
         * Set the number of pages that should be retained to either side of the
         * current page in the view hierarchy in an idle state. Pages beyond this
         * limit will be recreated from the adapter when needed.
         * <p/>
         * <p>
         * This is offered as an optimization. If you know in advance the number of
         * pages you will need to support or have lazy-loading mechanisms in place
         * on your pages, tweaking this setting can have benefits in perceived
         * smoothness of paging animations and interaction. If you have a small
         * number of pages (3-4) that you can keep active all at once, less time
         * will be spent in layout for newly created view subtrees as the user pages
         * back and forth.
         * </p>
         * <p/>
         * <p>
         * You should keep this limit low, especially if your pages have complex
         * layouts. This setting defaults to 1.
         * </p>
         *
         * @param limit How many pages will be kept offscreen in an idle state.
         */
        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();
            }
        }
    
        /**
         * Set the margin between pages.
         *
         * @param marginPixels Distance between adjacent pages in pixels
         * @see #getPageMargin()
         * @see #setPageMarginDrawable(Drawable)
         * @see #setPageMarginDrawable(int)
         */
        public void setPageMargin(int marginPixels) {
            final int oldMargin = mPageMargin;
            mPageMargin = marginPixels;
    
            final int width = getWidth();
            recomputeScrollPosition(width, width, marginPixels, oldMargin);
    
            requestLayout();
        }
    
        /**
         * Return the margin between pages.
         *
         * @return The size of the margin in pixels
         */
        public int getPageMargin() {
            return mPageMargin;
        }
    
        /**
         * Set a drawable that will be used to fill the margin between pages.
         *
         * @param d Drawable to display between pages
         */
        public void setPageMarginDrawable(Drawable d) {
            mMarginDrawable = d;
            if (d != null)
                refreshDrawableState();
            setWillNotDraw(d == null);
            invalidate();
        }
    
        /**
         * Set a drawable that will be used to fill the margin between pages.
         *
         * @param resId Resource ID of a drawable to display between pages
         */
        public void setPageMarginDrawable(int resId) {
            setPageMarginDrawable(getContext().getResources().getDrawable(resId));
        }
    
        @Override
        protected boolean verifyDrawable(Drawable who) {
            return super.verifyDrawable(who) || who == mMarginDrawable;
        }
    
        @Override
        protected void drawableStateChanged() {
            super.drawableStateChanged();
            final Drawable d = mMarginDrawable;
            if (d != null && d.isStateful()) {
                d.setState(getDrawableState());
            }
        }
    
        // We want the duration of the page snap animation to be influenced by the
        // distance that
        // the screen has to travel, however, we don't want this duration to be
        // effected in a
        // purely linear fashion. Instead, we use this method to moderate the effect
        // that the distance
        // of travel has on the overall snap duration.
        float distanceInfluenceForSnapDuration(float f) {
            f -= 0.5f; // center the values about 0.
            f *= 0.3f * Math.PI / 2.0f;
            return (float) Math.sin(f);
        }
    
        /**
         * Like {@link View#scrollBy}, but scroll smoothly instead of
         * immediately.
         *
         * @param x the number of pixels to scroll by on the X axis
         * @param y the number of pixels to scroll by on the Y axis
         */
        void smoothScrollTo(int x, int y) {
            smoothScrollTo(x, y, 0);
        }
    
        /**
         * Like {@link View#scrollBy}, but scroll smoothly instead of
         * immediately.
         *
         * @param x        the number of pixels to scroll by on the X axis
         * @param y        the number of pixels to scroll by on the Y axis
         * @param velocity the velocity associated with a fling, if applicable. (0
         *                 otherwise)
         */
        void smoothScrollTo(int x, int y, int velocity) {
            if (getChildCount() == 0) {
                // Nothing to do.
                setScrollingCacheEnabled(false);
                return;
            }
            int sx = getScrollX();
            int sy = getScrollY();
            int dx = x - sx;
            int dy = y - sy;
            if (dx == 0 && dy == 0) {
                completeScroll();
                setScrollState(SCROLL_STATE_IDLE);
                return;
            }
    
            setScrollingCacheEnabled(true);
            mScrolling = true;
            setScrollState(SCROLL_STATE_SETTLING);
    
            final float pageDelta = (float) Math.abs(dx)
                    / (getWidth() + mPageMargin);
            int duration = (int) (pageDelta * 100);
    
            velocity = Math.abs(velocity);
            if (velocity > 0) {
                duration += (duration / (velocity / mBaseLineFlingVelocity))
                        * mFlingVelocityInfluence;
            } else {
                duration += 100;
            }
            duration = Math.min(duration, MAX_SETTLE_DURATION);
    
            mScroller.startScroll(sx, sy, dx, dy, duration);
            invalidate();
        }
    
        void addNewItem(int position, int index) {
            ItemInfo ii = new ItemInfo();
            ii.position = position;
            ii.object = mAdapter.instantiateItem(this, position);
            if (index < 0) {
                mItems.add(ii);
            } else {
                mItems.add(index, ii);
            }
        }
    
        void dataSetChanged() {
            // This method only gets called if our observer is attached, so mAdapter
            // is non-null.
    
            boolean needPopulate = mItems.size() < 3
                    && mItems.size() < mAdapter.getCount();
            int newCurrItem = -1;
    
            for (int i = 0; i < mItems.size(); i++) {
                final ItemInfo ii = mItems.get(i);
                final int newPos = mAdapter.getItemPosition(ii.object);
    
                if (newPos == PagerAdapter.POSITION_UNCHANGED) {
                    continue;
                }
    
                if (newPos == PagerAdapter.POSITION_NONE) {
                    mItems.remove(i);
                    i--;
                    mAdapter.destroyItem(this, ii.position, ii.object);
                    needPopulate = true;
    
                    if (mCurItem == ii.position) {
                        // Keep the current item in the valid range
                        newCurrItem = Math.max(0,
                                Math.min(mCurItem, mAdapter.getCount() - 1));
                    }
                    continue;
                }
    
                if (ii.position != newPos) {
                    if (ii.position == mCurItem) {
                        // Our current item changed position. Follow it.
                        newCurrItem = newPos;
                    }
    
                    ii.position = newPos;
                    needPopulate = true;
                }
            }
    
            Collections.sort(mItems, COMPARATOR);
    
            if (newCurrItem >= 0) {
                // TODO This currently causes a jump.
                setCurrentItemInternal(newCurrItem, false, true);
                needPopulate = true;
            }
            if (needPopulate) {
                populate();
                requestLayout();
            }
        }
    
        void populate() {
            if (mAdapter == null) {
                return;
            }
    
            // Bail now if we are waiting to populate. This is to hold off
            // on creating views from the time the user releases their finger to
            // fling to a new position until we have finished the scroll to
            // that position, avoiding glitches from happening at that point.
            if (mPopulatePending) {
                if (DEBUG)
                    Log.i(TAG, "populate is pending, skipping for now...");
                return;
            }
    
            // Also, don't populate until we are attached to a window. This is to
            // avoid trying to populate before we have restored our view hierarchy
            // state and conflicting with what is restored.
            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 (DEBUG)
                Log.v(TAG, "populating: startPos=" + startPos + " endPos=" + endPos);
    
            // Add and remove pages in the existing list.
            int lastPos = -1;
            for (int i = 0; i < mItems.size(); i++) {
                ItemInfo ii = mItems.get(i);
                if ((ii.position < startPos || ii.position > endPos)
                        && !ii.scrolling) {
                    if (DEBUG)
                        Log.i(TAG, "removing: " + ii.position + " @ " + i);
                    mItems.remove(i);
                    i--;
                    mAdapter.destroyItem(this, ii.position, ii.object);
                } else if (lastPos < endPos && ii.position > startPos) {
                    // The next item is outside of our range, but we have a gap
                    // between it and the last item where we want to have a page
                    // shown. Fill in the gap.
                    lastPos++;
                    if (lastPos < startPos) {
                        lastPos = startPos;
                    }
                    while (lastPos <= endPos && lastPos < ii.position) {
                        if (DEBUG)
                            Log.i(TAG, "inserting: " + lastPos + " @ " + i);
                        addNewItem(lastPos, i);
                        lastPos++;
                        i++;
                    }
                }
                lastPos = ii.position;
            }
    
            // Add any new pages we need at the end.
            lastPos = mItems.size() > 0 ? mItems.get(mItems.size() - 1).position
                    : -1;
            if (lastPos < endPos) {
                lastPos++;
                lastPos = lastPos > startPos ? lastPos : startPos;
                while (lastPos <= endPos) {
                    if (DEBUG)
                        Log.i(TAG, "appending: " + lastPos);
                    addNewItem(lastPos, -1);
                    lastPos++;
                }
            }
    
            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);
                }
            }
    
            ItemInfo curItem = null;
            for (int i = 0; i < mItems.size(); i++) {
                if (mItems.get(i).position == mCurItem) {
                    curItem = mItems.get(i);
                    break;
                }
            }
            mAdapter.setPrimaryItem(this, mCurItem,
                    curItem != null ? curItem.object : null);
    
            mAdapter.finishUpdate(this);
    
            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(FOCUS_FORWARD)) {
                                break;
                            }
                        }
                    }
                }
            }
        }
    
        public static class SavedState extends BaseSavedState {
            int position;
            Parcelable adapterState;
            ClassLoader loader;
    
            public SavedState(Parcelable superState) {
                super(superState);
            }
    
            @Override
            public void writeToParcel(Parcel out, int flags) {
                super.writeToParcel(out, flags);
                out.writeInt(position);
                out.writeParcelable(adapterState, flags);
            }
    
            @Override
            public String toString() {
                return "FragmentPager.SavedState{"
                        + Integer.toHexString(System.identityHashCode(this))
                        + " position=" + position + "}";
            }
    
            public static final Creator<SavedState> CREATOR = ParcelableCompat
                    .newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
                        @Override
                        public SavedState createFromParcel(Parcel in,
                                                           ClassLoader loader) {
                            return new SavedState(in, loader);
                        }
    
                        @Override
                        public SavedState[] newArray(int size) {
                            return new SavedState[size];
                        }
                    });
    
            SavedState(Parcel in, ClassLoader loader) {
                super(in);
                if (loader == null) {
                    loader = getClass().getClassLoader();
                }
                position = in.readInt();
                adapterState = in.readParcelable(loader);
                this.loader = loader;
            }
        }
    
        @Override
        public Parcelable onSaveInstanceState() {
            Parcelable superState = super.onSaveInstanceState();
            SavedState ss = new SavedState(superState);
            ss.position = mCurItem;
            if (mAdapter != null) {
                ss.adapterState = mAdapter.saveState();
            }
            return ss;
        }
    
        @Override
        public void onRestoreInstanceState(Parcelable state) {
            if (!(state instanceof SavedState)) {
                super.onRestoreInstanceState(state);
                return;
            }
    
            SavedState ss = (SavedState) state;
            super.onRestoreInstanceState(ss.getSuperState());
    
            if (mAdapter != null) {
                mAdapter.restoreState(ss.adapterState, ss.loader);
                setCurrentItemInternal(ss.position, false, true);
            } else {
                mRestoredCurItem = ss.position;
                mRestoredAdapterState = ss.adapterState;
                mRestoredClassLoader = ss.loader;
            }
        }
    
        @Override
        public void addView(View child, int index, LayoutParams params) {
            if (mInLayout) {
                addViewInLayout(child, index, params);
                child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);
            } else {
                super.addView(child, index, params);
            }
    
            if (USE_CACHE) {
                if (child.getVisibility() != GONE) {
                    child.setDrawingCacheEnabled(mScrollingCacheEnabled);
                } else {
                    child.setDrawingCacheEnabled(false);
                }
            }
        }
    
        ItemInfo infoForChild(View child) {
            for (int i = 0; i < mItems.size(); i++) {
                ItemInfo ii = mItems.get(i);
                if (mAdapter.isViewFromObject(child, ii.object)) {
                    return ii;
                }
            }
            return null;
        }
    
        ItemInfo infoForAnyChild(View child) {
            ViewParent parent;
            while ((parent = child.getParent()) != this) {
                if (parent == null || !(parent instanceof View)) {
                    return null;
                }
                child = (View) parent;
            }
            return infoForChild(child);
        }
    
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            mFirstLayout = true;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // For simple implementation, or 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));
    
            // Children are just made to fill our space.
            mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth()
                    - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY);
            mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                    MeasureSpec.EXACTLY);
    
            // Make sure we have created all fragments that we need to have shown.
            mInLayout = true;
            populate();
            mInLayout = false;
    
            // Make sure all children have been properly measured.
            final int 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);
                    child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);
                }
            }
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            // Make sure scroll position is set correctly.
            if (w != oldw) {
                recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);
            }
        }
    
        private void recomputeScrollPosition(int width, int oldWidth, int margin,
                                             int oldMargin) {
            final int widthWithMargin = width + margin;
            if (oldWidth > 0) {
                final int oldScrollPos = getScrollX();
                final int oldwwm = oldWidth + oldMargin;
                final int oldScrollItem = oldScrollPos / oldwwm;
                final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm;
                final int scrollPos = (int) ((oldScrollItem + scrollOffset) * widthWithMargin);
                scrollTo(scrollPos, getScrollY());
                if (!mScroller.isFinished()) {
                    // We now return to your regularly scheduled scroll, already in
                    // progress.
                    final int newDuration = mScroller.getDuration()
                            - mScroller.timePassed();
                    mScroller.startScroll(scrollPos, 0, mCurItem * widthWithMargin,
                            0, newDuration);
                }
            } else {
                int scrollPos = mCurItem * widthWithMargin;
                if (scrollPos != getScrollX()) {
                    completeScroll();
                    scrollTo(scrollPos, getScrollY());
                }
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            mInLayout = true;
            populate();
            mInLayout = false;
    
            final int count = getChildCount();
            final int width = r - l;
    
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                ItemInfo ii;
                if (child.getVisibility() != GONE
                        && (ii = infoForChild(child)) != null) {
                    int loff = (width + mPageMargin) * ii.position;
                    int childLeft = getPaddingLeft() + loff;
                    int childTop = getPaddingTop();
                    if (DEBUG)
                        Log.v(TAG,
                                "Positioning #" + i + " " + child + " f="
                                        + ii.object + ":" + childLeft + ","
                                        + childTop + " " + child.getMeasuredWidth()
                                        + "x" + child.getMeasuredHeight());
                    child.layout(childLeft, childTop,
                            childLeft + child.getMeasuredWidth(),
                            childTop + child.getMeasuredHeight());
                }
            }
            mFirstLayout = false;
        }
    
        @Override
        public void computeScroll() {
            if (DEBUG)
                Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished());
            if (!mScroller.isFinished()) {
                if (mScroller.computeScrollOffset()) {
                    if (DEBUG)
                        Log.i(TAG, "computeScroll: still scrolling");
                    int oldX = getScrollX();
                    int oldY = getScrollY();
                    int x = mScroller.getCurrX();
                    int y = mScroller.getCurrY();
    
                    if (oldX != x || oldY != y) {
                        scrollTo(x, y);
                    }
    
                    if (mOnPageChangeListener != null) {
                        final int widthWithMargin = getWidth() + mPageMargin;
                        final int position = x / widthWithMargin;
                        final int offsetPixels = x % widthWithMargin;
                        final float offset = (float) offsetPixels / widthWithMargin;
                        mOnPageChangeListener.onPageScrolled(position, offset,
                                offsetPixels);
                    }
    
                    // Keep on drawing until the animation has finished.
                    invalidate();
                    return;
                }
            }
    
            // Done with scroll, clean up state.
            completeScroll();
        }
    
        private void completeScroll() {
            boolean needPopulate = mScrolling;
            if (needPopulate) {
                // Done with scroll, no longer want to cache view drawing.
                setScrollingCacheEnabled(false);
                mScroller.abortAnimation();
                int oldX = getScrollX();
                int oldY = getScrollY();
                int x = mScroller.getCurrX();
                int y = mScroller.getCurrY();
                if (oldX != x || oldY != y) {
                    scrollTo(x, y);
                }
                setScrollState(SCROLL_STATE_IDLE);
            }
            mPopulatePending = false;
            mScrolling = false;
            for (int i = 0; i < mItems.size(); i++) {
                ItemInfo ii = mItems.get(i);
                if (ii.scrolling) {
                    needPopulate = true;
                    ii.scrolling = false;
                }
            }
            if (needPopulate) {
                populate();
            }
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            /*
             * This method JUST determines whether we want to intercept the motion.
             * If we return true, onMotionEvent will be called and we do the actual
             * scrolling there.
             */
    
            final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
    
            // Always take care of the touch gesture being complete.
            if (action == MotionEvent.ACTION_CANCEL
                    || action == MotionEvent.ACTION_UP) {
                // Release the drag.
                if (DEBUG)
                    Log.v(TAG, "Intercept done!");
                mIsBeingDragged = false;
                mIsUnableToDrag = false;
                mActivePointerId = INVALID_POINTER;
                return false;
            }
    
            // Nothing more to do here if we have decided whether or not we
            // are dragging.
            if (action != MotionEvent.ACTION_DOWN) {
                if (mIsBeingDragged) {
                    if (DEBUG)
                        Log.v(TAG, "Intercept returning true!");
                    return true;
                }
                if (mIsUnableToDrag) {
                    if (DEBUG)
                        Log.v(TAG, "Intercept returning false!");
                    return false;
                }
            }
    
            switch (action) {
                case MotionEvent.ACTION_MOVE: {
                    /*
                     * mIsBeingDragged == false, otherwise the shortcut would have
                     * caught it. Check whether the user has moved far enough from his
                     * original down touch.
                     */
    
                    /*
                     * Locally do absolute value. mLastMotionY is set to the y value of
                     * the down event.
                     */
                    final int activePointerId = mActivePointerId;
                    if (activePointerId == INVALID_POINTER) {
                        // If we don't have a valid id, the touch down wasn't on
                        // content.
                        break;
                    }
    
                    final int pointerIndex = MotionEventCompat.findPointerIndex(ev,
                            activePointerId);
                    final float x = MotionEventCompat.getX(ev, pointerIndex);
                    final float dx = x - mLastMotionX;
                    final float xDiff = Math.abs(dx);
                    final float y = MotionEventCompat.getY(ev, pointerIndex);
                    final float yDiff = Math.abs(y - mLastMotionY);
                    final int scrollX = getScrollX();
                    final boolean atEdge = (dx > 0 && scrollX == 0)
                            || (dx < 0 && mAdapter != null && scrollX >= (mAdapter
                            .getCount() - 1) * getWidth() - 1);
                    if (DEBUG)
                        Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + ","
                                + yDiff);
    
                    if (canScroll(this, false, (int) dx, (int) x, (int) y)) {
                        // Nested view has scrollable area under this point. Let it be
                        // handled there.
                        mInitialMotionX = mLastMotionX = x;
                        mLastMotionY = y;
                        return false;
                    }
                    if (xDiff > mTouchSlop && xDiff > yDiff) {
                        if (DEBUG)
                            Log.v(TAG, "Starting drag!");
                        mIsBeingDragged = true;
                        setScrollState(SCROLL_STATE_DRAGGING);
                        mLastMotionX = x;
                        setScrollingCacheEnabled(true);
                    } else {
                        if (yDiff > mTouchSlop) {
                            // The finger has moved enough in the vertical
                            // direction to be counted as a drag... abort
                            // any attempt to drag horizontally, to work correctly
                            // with children that have scrolling containers.
                            if (DEBUG)
                                Log.v(TAG, "Starting unable to drag!");
                            mIsUnableToDrag = true;
                        }
                    }
                    break;
                }
    
                case MotionEvent.ACTION_DOWN: {
                    /*
                     * Remember location of down touch. ACTION_DOWN always refers to
                     * pointer index 0.
                     */
                    mLastMotionX = mInitialMotionX = ev.getX();
                    mLastMotionY = ev.getY();
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
    
                    if (mScrollState == SCROLL_STATE_SETTLING) {
                        // Let the user 'catch' the pager as it animates.
                        mIsBeingDragged = true;
                        mIsUnableToDrag = false;
                        setScrollState(SCROLL_STATE_DRAGGING);
                    } else {
                        completeScroll();
                        mIsBeingDragged = false;
                        mIsUnableToDrag = false;
                    }
    
                    if (DEBUG)
                        Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
                                + " mIsBeingDragged=" + mIsBeingDragged
                                + "mIsUnableToDrag=" + mIsUnableToDrag);
                    break;
                }
    
                case MotionEventCompat.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    break;
            }
    
            /*
             * The only time we want to intercept motion events is if we are in the
             * drag mode.
             */
            return mIsBeingDragged;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (mFakeDragging) {
                // A fake drag is in progress already, ignore this real one
                // but still eat the touch events.
                // (It is likely that the user is multi-touching the screen.)
                return true;
            }
    
            if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
                // Don't handle edge touches immediately -- they may actually belong
                // to one of our
                // descendants.
                return false;
            }
    
            if (mAdapter == null || mAdapter.getCount() == 0) {
                // Nothing to present or scroll; nothing to touch.
                return false;
            }
    
            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);
    
            final int action = ev.getAction();
            boolean needsInvalidate = false;
    
            switch (action & MotionEventCompat.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    /*
                     * If being flinged and user touches, stop the fling. isFinished
                     * will be false if being flinged.
                     */
                    completeScroll();
    
                    // Remember where the motion event started
                    mLastMotionX = mInitialMotionX = ev.getX();
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                    break;
                }
                case MotionEvent.ACTION_MOVE:
                    if (!mIsBeingDragged) {
                        final int pointerIndex = MotionEventCompat.findPointerIndex(ev,
                                mActivePointerId);
                        final float x = MotionEventCompat.getX(ev, pointerIndex);
                        final float xDiff = Math.abs(x - mLastMotionX);
                        final float y = MotionEventCompat.getY(ev, pointerIndex);
                        final float yDiff = Math.abs(y - mLastMotionY);
                        if (DEBUG)
                            Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff
                                    + "," + yDiff);
                        if (xDiff > mTouchSlop && xDiff > yDiff) {
                            if (DEBUG)
                                Log.v(TAG, "Starting drag!");
                            mIsBeingDragged = true;
                            mLastMotionX = x;
                            setScrollState(SCROLL_STATE_DRAGGING);
                            setScrollingCacheEnabled(true);
                        }
                    }
                    if (mIsBeingDragged) {
                        // Scroll to follow the motion event
                        final int activePointerIndex = MotionEventCompat
                                .findPointerIndex(ev, mActivePointerId);
                        final float x = MotionEventCompat.getX(ev, activePointerIndex);
                        final float deltaX = mLastMotionX - x;
                        mLastMotionX = x;
                        float oldScrollX = getScrollX();
                        float scrollX = oldScrollX + deltaX;
                        final int width = getWidth();
                        final int widthWithMargin = width + mPageMargin;
    
                        final int lastItemIndex = mAdapter.getCount() - 1;
                        final float leftBound = Math.max(0, (mCurItem - 1)
                                * widthWithMargin);
                        final float rightBound = Math.min(mCurItem + 1, lastItemIndex)
                                * widthWithMargin;
                        if (scrollX < leftBound) {
                            if (leftBound == 0) {
                                float over = -scrollX;
                                needsInvalidate = mLeftEdge.onPull(over / width);
                            }
                            scrollX = leftBound;
                        } else if (scrollX > rightBound) {
                            if (rightBound == lastItemIndex * widthWithMargin) {
                                float over = scrollX - rightBound;
                                needsInvalidate = mRightEdge.onPull(over / width);
                            }
                            scrollX = rightBound;
                        }
                        // Don't lose the rounded component
                        mLastMotionX += scrollX - (int) scrollX;
                        scrollTo((int) scrollX, getScrollY());
                        if (mOnPageChangeListener != null) {
                            final int position = (int) scrollX / widthWithMargin;
                            final int positionOffsetPixels = (int) scrollX
                                    % widthWithMargin;
                            final float positionOffset = (float) positionOffsetPixels
                                    / widthWithMargin;
                            mOnPageChangeListener.onPageScrolled(position,
                                    positionOffset, positionOffsetPixels);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mIsBeingDragged) {
                        final VelocityTracker velocityTracker = mVelocityTracker;
                        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                        int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                                velocityTracker, mActivePointerId);
                        mPopulatePending = true;
                        final int widthWithMargin = getWidth() + mPageMargin;
                        final int scrollX = getScrollX();
                        final int currentPage = scrollX / widthWithMargin;
                        int nextPage = initialVelocity > 0 ? currentPage
                                : currentPage + 1;
                        setCurrentItemInternal(nextPage, true, true, initialVelocity);
    
                        mActivePointerId = INVALID_POINTER;
                        endDrag();
                        needsInvalidate = mLeftEdge.onRelease()
                                | mRightEdge.onRelease();
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                    if (mIsBeingDragged) {
                        setCurrentItemInternal(mCurItem, true, true);
                        mActivePointerId = INVALID_POINTER;
                        endDrag();
                        needsInvalidate = mLeftEdge.onRelease()
                                | mRightEdge.onRelease();
                    }
                    break;
                case MotionEventCompat.ACTION_POINTER_DOWN: {
                    final int index = MotionEventCompat.getActionIndex(ev);
                    final float x = MotionEventCompat.getX(ev, index);
                    mLastMotionX = x;
                    mActivePointerId = MotionEventCompat.getPointerId(ev, index);
                    break;
                }
                case MotionEventCompat.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    mLastMotionX = MotionEventCompat.getX(ev,
                            MotionEventCompat.findPointerIndex(ev, mActivePointerId));
                    break;
            }
            if (needsInvalidate) {
                invalidate();
            }
            return true;
        }
    
        @Override
        public void draw(Canvas canvas) {
            super.draw(canvas);
            boolean needsInvalidate = false;
    
            final int overScrollMode = ViewCompat.getOverScrollMode(this);
            if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS
                    || (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS
                    && mAdapter != null && mAdapter.getCount() > 1)) {
                if (!mLeftEdge.isFinished()) {
                    final int restoreCount = canvas.save();
                    final int height = getHeight() - getPaddingTop()
                            - getPaddingBottom();
    
                    canvas.rotate(270);
                    canvas.translate(-height + getPaddingTop(), 0);
                    mLeftEdge.setSize(height, getWidth());
                    needsInvalidate |= mLeftEdge.draw(canvas);
                    canvas.restoreToCount(restoreCount);
                }
                if (!mRightEdge.isFinished()) {
                    final int restoreCount = canvas.save();
                    final int width = getWidth();
                    final int height = getHeight() - getPaddingTop()
                            - getPaddingBottom();
                    final int itemCount = mAdapter != null ? mAdapter.getCount()
                            : 1;
    
                    canvas.rotate(90);
                    canvas.translate(-getPaddingTop(), -itemCount
                            * (width + mPageMargin) + mPageMargin);
                    mRightEdge.setSize(height, width);
                    needsInvalidate |= mRightEdge.draw(canvas);
                    canvas.restoreToCount(restoreCount);
                }
            } else {
                mLeftEdge.finish();
                mRightEdge.finish();
            }
    
            if (needsInvalidate) {
                // Keep animating
                invalidate();
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // Draw the margin drawable if needed.
            if (mPageMargin > 0 && mMarginDrawable != null) {
                final int scrollX = getScrollX();
                final int width = getWidth();
                final int offset = scrollX % (width + mPageMargin);
                if (offset != 0) {
                    // Pages fit completely when settled; we only need to draw when
                    // in between
                    final int left = scrollX - offset + width;
                    mMarginDrawable.setBounds(left, 0, left + mPageMargin,
                            getHeight());
                    mMarginDrawable.draw(canvas);
                }
            }
        }
    
        /**
         * Start a fake drag of the pager.
         * <p/>
         * <p/>
         * A fake drag can be useful if you want to synchronize the motion of the
         * ViewPager with the touch scrolling of another view, while still letting
         * the ViewPager control the snapping motion and fling behavior. (e.g.
         * parallax-scrolling tabs.) Call {@link #fakeDragBy(float)} to simulate the
         * actual drag motion. Call {@link #endFakeDrag()} to complete the fake drag
         * and fling as necessary.
         * <p/>
         * <p/>
         * During a fake drag the ViewPager will ignore all touch events. If a real
         * drag is already in progress, this method will return false.
         *
         * @return true if the fake drag began successfully, false if it could not
         * be started.
         * @see #fakeDragBy(float)
         * @see #endFakeDrag()
         */
        public boolean beginFakeDrag() {
            if (mIsBeingDragged) {
                return false;
            }
            mFakeDragging = true;
            setScrollState(SCROLL_STATE_DRAGGING);
            mInitialMotionX = mLastMotionX = 0;
            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            } else {
                mVelocityTracker.clear();
            }
            final long time = SystemClock.uptimeMillis();
            final MotionEvent ev = MotionEvent.obtain(time, time,
                    MotionEvent.ACTION_DOWN, 0, 0, 0);
            mVelocityTracker.addMovement(ev);
            ev.recycle();
            mFakeDragBeginTime = time;
            return true;
        }
    
        /**
         * End a fake drag of the pager.
         *
         * @see #beginFakeDrag()
         * @see #fakeDragBy(float)
         */
        public void endFakeDrag() {
            if (!mFakeDragging) {
                throw new IllegalStateException(
                        "No fake drag in progress. Call beginFakeDrag first.");
            }
    
            final VelocityTracker velocityTracker = mVelocityTracker;
            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
            int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
                    velocityTracker, mActivePointerId);
            mPopulatePending = true;
            if ((Math.abs(initialVelocity) > mMinimumVelocity)
                    || Math.abs(mInitialMotionX - mLastMotionX) >= (getWidth() / 3)) {
                if (mLastMotionX > mInitialMotionX) {
                    setCurrentItemInternal(mCurItem - 1, true, true);
                } else {
                    setCurrentItemInternal(mCurItem + 1, true, true);
                }
            } else {
                setCurrentItemInternal(mCurItem, true, true);
            }
            endDrag();
    
            mFakeDragging = false;
        }
    
        /**
         * Fake drag by an offset in pixels. You must have called
         * {@link #beginFakeDrag()} first.
         *
         * @param xOffset Offset in pixels to drag by.
         * @see #beginFakeDrag()
         * @see #endFakeDrag()
         */
        public void fakeDragBy(float xOffset) {
            if (!mFakeDragging) {
                throw new IllegalStateException(
                        "No fake drag in progress. Call beginFakeDrag first.");
            }
    
            mLastMotionX += xOffset;
            float scrollX = getScrollX() - xOffset;
            final int width = getWidth();
            final int widthWithMargin = width + mPageMargin;
    
            final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);
            final float rightBound = Math
                    .min(mCurItem + 1, mAdapter.getCount() - 1) * widthWithMargin;
            if (scrollX < leftBound) {
                scrollX = leftBound;
            } else if (scrollX > rightBound) {
                scrollX = rightBound;
            }
            // Don't lose the rounded component
            mLastMotionX += scrollX - (int) scrollX;
            scrollTo((int) scrollX, getScrollY());
            if (mOnPageChangeListener != null) {
                final int position = (int) scrollX / widthWithMargin;
                final int positionOffsetPixels = (int) scrollX % widthWithMargin;
                final float positionOffset = (float) positionOffsetPixels
                        / widthWithMargin;
                mOnPageChangeListener.onPageScrolled(position, positionOffset,
                        positionOffsetPixels);
            }
    
            // Synthesize an event for the VelocityTracker.
            final long time = SystemClock.uptimeMillis();
            final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time,
                    MotionEvent.ACTION_MOVE, mLastMotionX, 0, 0);
            mVelocityTracker.addMovement(ev);
            ev.recycle();
        }
    
        /**
         * Returns true if a fake drag is in progress.
         *
         * @return true if currently in a fake drag, false otherwise.
         * @see #beginFakeDrag()
         * @see #fakeDragBy(float)
         * @see #endFakeDrag()
         */
        public boolean isFakeDragging() {
            return mFakeDragging;
        }
    
        private void onSecondaryPointerUp(MotionEvent ev) {
            final int pointerIndex = MotionEventCompat.getActionIndex(ev);
            final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
            if (pointerId == mActivePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
                mActivePointerId = MotionEventCompat.getPointerId(ev,
                        newPointerIndex);
                if (mVelocityTracker != null) {
                    mVelocityTracker.clear();
                }
            }
        }
    
        private void endDrag() {
            mIsBeingDragged = false;
            mIsUnableToDrag = false;
    
            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
        }
    
        private void setScrollingCacheEnabled(boolean enabled) {
            if (mScrollingCacheEnabled != enabled) {
                mScrollingCacheEnabled = enabled;
                if (USE_CACHE) {
                    final int size = getChildCount();
                    for (int i = 0; i < size; ++i) {
                        final View child = getChildAt(i);
                        if (child.getVisibility() != GONE) {
                            child.setDrawingCacheEnabled(enabled);
                        }
                    }
                }
            }
        }
    
        /**
         * Tests scrollability within child views of v given a delta of dx.
         *
         * @param v      View to test for horizontal scrollability
         * @param checkV Whether the view v passed should itself be checked for
         *               scrollability (true), or just its children (false).
         * @param dx     Delta scrolled in pixels
         * @param x      X coordinate of the active touch point
         * @param y      Y coordinate of the active touch point
         * @return true if child views of v can be scrolled by delta of dx.
         */
        protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
            if (v instanceof ViewGroup) {
                final ViewGroup group = (ViewGroup) v;
                final int scrollX = v.getScrollX();
                final int scrollY = v.getScrollY();
                final int count = group.getChildCount();
                // Count backwards - let topmost views consume scroll distance
                // first.
                for (int i = count - 1; i >= 0; i--) {
                    // TODO: Add versioned support here for transformed views.
                    // This will not work for transformed views in Honeycomb+
                    final View child = group.getChildAt(i);
                    if (x + scrollX >= child.getLeft()
                            && x + scrollX < child.getRight()
                            && y + scrollY >= child.getTop()
                            && y + scrollY < child.getBottom()
                            && canScroll(child, true, dx,
                            x + scrollX - child.getLeft(), y + scrollY
                                    - child.getTop())) {
                        return true;
                    }
                }
            }
    
            return checkV && ViewCompat.canScrollHorizontally(v, -dx);
        }
    
        @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            // Let the focused view and/or our descendants get the key first
            return super.dispatchKeyEvent(event) || executeKeyEvent(event);
        }
    
        /**
         * You can call this function yourself to have the scroll view perform
         * scrolling from a key event, just as if the event had been dispatched to
         * it by the view hierarchy.
         *
         * @param event The key event to execute.
         * @return Return true if the event was handled, else false.
         */
        public boolean executeKeyEvent(KeyEvent event) {
            boolean handled = false;
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        handled = arrowScroll(FOCUS_LEFT);
                        break;
                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        handled = arrowScroll(FOCUS_RIGHT);
                        break;
                    case KeyEvent.KEYCODE_TAB:
                        if (KeyEventCompat.hasNoModifiers(event)) {
                            handled = arrowScroll(FOCUS_FORWARD);
                        } else if (KeyEventCompat.hasModifiers(event,
                                KeyEvent.META_SHIFT_ON)) {
                            handled = arrowScroll(FOCUS_BACKWARD);
                        }
                        break;
                }
            }
            return handled;
        }
    
        public boolean arrowScroll(int direction) {
            View currentFocused = findFocus();
            if (currentFocused == this)
                currentFocused = null;
    
            boolean handled = false;
    
            View nextFocused = FocusFinder.getInstance().findNextFocus(this,
                    currentFocused, direction);
            if (nextFocused != null && nextFocused != currentFocused) {
                if (direction == View.FOCUS_LEFT) {
                    // If there is nothing to the left, or this is causing us to
                    // jump to the right, then what we really want to do is page
                    // left.
                    if (currentFocused != null
                            && nextFocused.getLeft() >= currentFocused.getLeft()) {
                        handled = pageLeft();
                    } else {
                        handled = nextFocused.requestFocus();
                    }
                } else if (direction == View.FOCUS_RIGHT) {
                    // If there is nothing to the right, or this is causing us to
                    // jump to the left, then what we really want to do is page
                    // right.
                    if (currentFocused != null
                            && nextFocused.getLeft() <= currentFocused.getLeft()) {
                        handled = pageRight();
                    } else {
                        handled = nextFocused.requestFocus();
                    }
                }
            } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {
                // Trying to move left and nothing there; try to page.
                handled = pageLeft();
            } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {
                // Trying to move right and nothing there; try to page.
                handled = pageRight();
            }
            if (handled) {
                playSoundEffect(SoundEffectConstants
                        .getContantForFocusDirection(direction));
            }
            return handled;
        }
    
        boolean pageLeft() {
            if (mCurItem > 0) {
                setCurrentItem(mCurItem - 1, true);
                return true;
            }
            return false;
        }
    
        boolean pageRight() {
            if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {
                setCurrentItem(mCurItem + 1, true);
                return true;
            }
            return false;
        }
    
        /**
         * We only want the current page that is being shown to be focusable.
         */
        @Override
        public void addFocusables(ArrayList<View> views, int direction,
                                  int focusableMode) {
            final int focusableCount = views.size();
    
            final int descendantFocusability = getDescendantFocusability();
    
            if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
                for (int i = 0; i < getChildCount(); i++) {
                    final View child = getChildAt(i);
                    if (child.getVisibility() == VISIBLE) {
                        ItemInfo ii = infoForChild(child);
                        if (ii != null && ii.position == mCurItem) {
                            child.addFocusables(views, direction, focusableMode);
                        }
                    }
                }
            }
    
            // we add ourselves (if focusable) in all cases except for when we are
            // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.
            // this is
            // to avoid the focus search finding layouts when a more precise search
            // among the focusable children would be more interesting.
            if (descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
                    // No focusable descendants
                    (focusableCount == views.size())) {
                // Note that we can't call the superclass here, because it will
                // add all views in. So we need to do the same thing View does.
                if (!isFocusable()) {
                    return;
                }
                if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
                        && isInTouchMode() && !isFocusableInTouchMode()) {
                    return;
                }
                if (views != null) {
                    views.add(this);
                }
            }
        }
    
        /**
         * We only want the current page that is being shown to be touchable.
         */
        @Override
        public void addTouchables(ArrayList<View> views) {
            // Note that we don't call super.addTouchables(), which means that
            // we don't call View.addTouchables(). This is okay because a ViewPager
            // is itself not touchable.
            for (int i = 0; i < getChildCount(); i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() == VISIBLE) {
                    ItemInfo ii = infoForChild(child);
                    if (ii != null && ii.position == mCurItem) {
                        child.addTouchables(views);
                    }
                }
            }
        }
    
        /**
         * We only want the current page that is being shown to be focusable.
         */
        @Override
        protected boolean onRequestFocusInDescendants(int direction,
                                                      Rect previouslyFocusedRect) {
            int index;
            int increment;
            int end;
            int count = getChildCount();
            if ((direction & FOCUS_FORWARD) != 0) {
                index = 0;
                increment = 1;
                end = count;
            } else {
                index = count - 1;
                increment = -1;
                end = -1;
            }
            for (int i = index; i != end; i += increment) {
                View child = getChildAt(i);
                if (child.getVisibility() == VISIBLE) {
                    ItemInfo ii = infoForChild(child);
                    if (ii != null && ii.position == mCurItem) {
                        if (child.requestFocus(direction, previouslyFocusedRect)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    
        @Override
        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
            // ViewPagers should only report accessibility info for the current
            // page,
            // otherwise things get very confusing.
    
            // TODO: Should this note something about the paging container?
    
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() == VISIBLE) {
                    final ItemInfo ii = infoForChild(child);
                    if (ii != null && ii.position == mCurItem
                            && child.dispatchPopulateAccessibilityEvent(event)) {
                        return true;
                    }
                }
            }
    
            return false;
        }
    
        private class PagerObserver extends DataSetObserver {
    
            @Override
            public void onChanged() {
                dataSetChanged();
            }
    
            @Override
            public void onInvalidated() {
                dataSetChanged();
            }
        }
    }
    

    后记、

    1.声明:

    [1]本文由张风捷特烈原创,转载请注明
    [2]欢迎广大编程爱好者共同交流
    [3]个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
    [4]你的喜欢与支持将是我最大的动力

    2.连接传送门:

    更多安卓技术欢迎访问:安卓技术栈
    我的github地址:欢迎star
    简书首发,腾讯云+社区同步更新
    张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com

    3.联系我

    QQ:1981462002
    邮箱:1981462002@qq.com
    微信:zdl1994328

    4.欢迎关注我的微信公众号,最新精彩文章,及时送达:
    9414344-c474349cd3bd4b82.jpg
    公众号.jpg
  • 相关阅读:
    LeetCode 769. Max Chunks To Make Sorted
    LeetCode 845. Longest Mountain in Array
    LeetCode 1059. All Paths from Source Lead to Destination
    1129. Shortest Path with Alternating Colors
    LeetCode 785. Is Graph Bipartite?
    LeetCode 802. Find Eventual Safe States
    LeetCode 1043. Partition Array for Maximum Sum
    LeetCode 841. Keys and Rooms
    LeetCode 1061. Lexicographically Smallest Equivalent String
    LeetCode 1102. Path With Maximum Minimum Value
  • 原文地址:https://www.cnblogs.com/toly-top/p/9781923.html
Copyright © 2011-2022 走看看