Android源码-ViewPager取消预加载

package com.bodybuilding.waycube.viewpagertest;

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.widget.Scroller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/**
 * Created by Administrator on 2017/9/12.
 */
import android.view.animation.Interpolator;

    /**
     * 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>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 MyViewPager extends ViewGroup {
        private static final String TAG = "myViewPager";
        private static final boolean DEBUG = false;

        private static final boolean USE_CACHE = false;

        private static final int DEFAULT_OFFSCREEN_PAGES = 0;//默认是1
        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 {@link android.support.v4.view.LazyViewPager.OnPageChangeListener} interface with stub
         * implementations of each method. Extend this if you do not intend to override
         * every method of {@link android.support.v4.view.LazyViewPager.OnPageChangeListener}.
         */
        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 MyViewPager(Context context) {
            super(context);
            initViewPager();
        }

        public MyViewPager(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>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>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(android.graphics.drawable.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 android.view.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 android.view.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>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>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();
            }
        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.pyy.viewpagerfragment; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.view.Display; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.pyy.viewpagerfragment.R; public class MainActivity extends FragmentActivity { public MainViewPager viewPager; public List<Fragment> fragments = new ArrayList<Fragment>(); public String hello = "hello "; private ImageView mTab1, mTab2, mTab3, mTab4; private ImageView mTabImg; private int currIndex = 0;// 当前页卡编号 private int zero = 0;// 动画图片偏移量 private int one;//单个水平动画位移 private int two; private int three; private FragmentManager fragmentManager; private int displayWidth, displayHeight; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Display currDisplay = getWindowManager().getDefaultDisplay();//获取屏幕当前分辨率 displayWidth = currDisplay.getWidth(); displayHeight = currDisplay.getHeight(); one = displayWidth / 4; //设置水平动画平移大小 two = one * 2; three = one * 3; initViews();//初始化控件 fragments.add(new TabAFragment()); fragments.add(new TabBFragment()); fragments.add(new TabCFragment()); fragments.add(new TabDFragment()); fragmentManager = this.getSupportFragmentManager(); viewPager = (MainViewPager) findViewById(R.id.viewPager); //viewPager.setSlipping(false);//设置ViewPager是否可以滑动 viewPager.setOffscreenPageLimit(4); viewPager.setOnPageChangeListener(new MyOnPageChangeListener()); viewPager.setAdapter(new MyPagerAdapter()); } /** *@Title: initViews *@Description: TODO初始化控件 */ public void initViews() { final LinearLayout tab1Layout = (LinearLayout) findViewById(R.id.tab1Layout); mTab1 = (ImageView) findViewById(R.id.img_weixin); mTab2 = (ImageView) findViewById(R.id.img_address); mTab3 = (ImageView) findViewById(R.id.img_friends); mTab4 = (ImageView) findViewById(R.id.img_settings); mTabImg = (ImageView) findViewById(R.id.img_tab_now); mTab1.setOnClickListener(new MyOnClickListener(0)); mTab2.setOnClickListener(new MyOnClickListener(1)); mTab3.setOnClickListener(new MyOnClickListener(2)); mTab4.setOnClickListener(new MyOnClickListener(3)); //以下是设置移动条的初始位置 RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mTabImg .getLayoutParams(); int[] location = new int[2]; mTab1.getLocationInWindow(location); int marginLeft = (displayWidth / 4 - 122) / 2;//122是tab下面移动条的宽度 lp.setMargins(marginLeft, 0, 0, 0); mTabImg.setLayoutParams(lp); mTabImg.setVisibility(View.VISIBLE); } /** * @ClassName: MyOnPageChangeListener * @Description: TODO页卡切换监听 * @author Panyy * @date 2013 2013年11月6日 下午2:08:10 * */ public class MyOnPageChangeListener implements OnPageChangeListener { @Override public void onPageSelected(int arg0) { Animation animation = null; switch (arg0) { case 0: mTab1.setImageDrawable(getResources().getDrawable( R.drawable.tab_weixin_pressed)); if (currIndex == 1) { animation = new TranslateAnimation(one, 0, 0, 0); mTab2.setImageDrawable(getResources().getDrawable( R.drawable.tab_address_normal)); } else if (currIndex == 2) { animation = new TranslateAnimation(two, 0, 0, 0); mTab3.setImageDrawable(getResources().getDrawable( R.drawable.tab_find_frd_normal)); } else if (currIndex == 3) { animation = new TranslateAnimation(three, 0, 0, 0); mTab4.setImageDrawable(getResources().getDrawable( R.drawable.tab_settings_normal)); } break; case 1: mTab2.setImageDrawable(getResources().getDrawable( R.drawable.tab_address_pressed)); if (currIndex == 0) { animation = new TranslateAnimation(zero, one, 0, 0); mTab1.setImageDrawable(getResources().getDrawable( R.drawable.tab_weixin_normal)); } else if (currIndex == 2) { animation = new TranslateAnimation(two, one, 0, 0); mTab3.setImageDrawable(getResources().getDrawable( R.drawable.tab_find_frd_normal)); } else if (currIndex == 3) { animation = new TranslateAnimation(three, one, 0, 0); mTab4.setImageDrawable(getResources().getDrawable( R.drawable.tab_settings_normal)); } break; case 2: mTab3.setImageDrawable(getResources().getDrawable( R.drawable.tab_find_frd_pressed)); if (currIndex == 0) { animation = new TranslateAnimation(zero, two, 0, 0); mTab1.setImageDrawable(getResources().getDrawable( R.drawable.tab_weixin_normal)); } else if (currIndex == 1) { animation = new TranslateAnimation(one, two, 0, 0); mTab2.setImageDrawable(getResources().getDrawable( R.drawable.tab_address_normal)); } else if (currIndex == 3) { animation = new TranslateAnimation(three, two, 0, 0); mTab4.setImageDrawable(getResources().getDrawable( R.drawable.tab_settings_normal)); } break; case 3: mTab4.setImageDrawable(getResources().getDrawable( R.drawable.tab_settings_pressed)); if (currIndex == 0) { animation = new TranslateAnimation(zero, three, 0, 0); mTab1.setImageDrawable(getResources().getDrawable( R.drawable.tab_weixin_normal)); } else if (currIndex == 1) { animation = new TranslateAnimation(one, three, 0, 0); mTab2.setImageDrawable(getResources().getDrawable( R.drawable.tab_address_normal)); } else if (currIndex == 2) { animation = new TranslateAnimation(two, three, 0, 0); mTab3.setImageDrawable(getResources().getDrawable( R.drawable.tab_find_frd_normal)); } break; } currIndex = arg0; animation.setFillAfter(true);// True:图片停在动画结束位置 animation.setDuration(150); mTabImg.startAnimation(animation); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } } /** * @ClassName: MyOnClickListener * @Description: TODO头标点击监听 * @author Panyy * @date 2013 2013年11月6日 下午2:46:08 * */ private class MyOnClickListener implements View.OnClickListener { private int index = 0; public MyOnClickListener(int i) { index = i; } @Override public void onClick(View v) { viewPager.setCurrentItem(index); } }; /** * @ClassName: MyPagerAdapter * @Description: TODO填充ViewPager的数据适配器 * @author Panyy * @date 2013 2013年11月6日 下午2:37:47 * */ private class MyPagerAdapter extends PagerAdapter { @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public int getCount() { return fragments.size(); } @Override public void destroyItem(View container, int position, Object object) { ((ViewPager) container).removeView(fragments.get(position) .getView()); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = fragments.get(position); if (!fragment.isAdded()) { // 如果fragment还没有added FragmentTransaction ft = fragmentManager.beginTransaction(); ft.add(fragment, fragment.getClass().getSimpleName()); ft.commit(); /** * 在用FragmentTransaction.commit()方法提交FragmentTransaction对象后 * 会在进程的主线程中,用异步的方式来执行。 * 如果想要立即执行这个等待中的操作,就要调用这个方法(只能在主线程中调用)。 * 要注意的是,所有的回调和相关的行为都会在这个调用中被执行完成,因此要仔细确认这个方法的调用位置。 */ fragmentManager.executePendingTransactions(); } if (fragment.getView().getParent() == null) { container.addView(fragment.getView()); // 为viewpager增加布局 } return fragment.getView(); } }; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值