美文网首页
自定义不缓存的viewpage---androidx

自定义不缓存的viewpage---androidx

作者: 奥利奥龙卷风 | 来源:发表于2020-09-17 14:58 被阅读0次

    private static final StringTAG ="NoPreloadViewPager";

        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 {

    Objectobject;

            int position;

            boolean scrolling;

        }

    private static final ComparatorCOMPARATOR =new Comparator(){

    @Override

            public int compare(ItemInfo lhs, ItemInfo rhs) {

    return lhs.position - rhs.position;

            }};

        private static final InterpolatorsInterpolator =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 ArrayListmItems =new ArrayList();

        private PagerAdaptermAdapter;

        private int mCurItem;  // Index of currently displayed page.

        private int mRestoredCurItem = -1;

        private ParcelablemRestoredAdapterState =null;

        private ClassLoadermRestoredClassLoader =null;

        private ScrollermScroller;

        private PagerObservermObserver;

        private int mPageMargin;

        private DrawablemMarginDrawable;

        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 VelocityTrackermVelocityTracker;

        private int mMinimumVelocity;

        private int mMaximumVelocity;

        private float mBaseLineFlingVelocity;

        private float mFlingVelocityInfluence;

        private boolean mFakeDragging;

        private long mFakeDragBeginTime;

        private EdgeEffectCompatmLeftEdge;

        private EdgeEffectCompatmRightEdge;

        private boolean mFirstLayout =true;

        private OnPageChangeListenermOnPageChangeListener;

        /**

    * 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.

    *

    */

            public void onPageScrollStateChanged(int state);

        }

    /**

    *

    */

        public static class SimpleOnPageChangeListenerimplements 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 NoPreloadViewPager(Context context) {

    super(context);

            initViewPager();

        }

    public NoPreloadViewPager(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

    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 PagerAdaptergetAdapter() {

    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.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>You should keep this limit low, especially if your pages have complex layouts.

        * This setting defaults to 1.

        *

        * @param limit How many pages will be kept offscreen in an idle state.

    */

        public void setOffscreenPageLimit(int limit) {

    if (limit

    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()

            int newCurrItem = -1;

            for (int i =0; 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

    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

    Log.i(TAG, "#" + i +": page " +mItems.get(i).position);

                }

    }

    ItemInfo curItem =null;

            for (int i=0; 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

    View child = getChildAt(i);

                        ii = infoForChild(child);

                        if (ii !=null && ii.position ==mCurItem) {

    if (child.requestFocus(FOCUS_FORWARD)) {

    break;

                            }

    }

    }

    }

    }

    }

    public static class SavedStateextends BaseSavedState {

    int position;

            ParcelableadapterState;

            ClassLoaderloader;

            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 StringtoString() {

    return "FragmentPager.SavedState{"

                        + Integer.toHexString(System.identityHashCode(this))

    +" position=" +position +"}";

            }

    public static final CreatorCREATOR

                    = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks() {

    @Override

                public SavedStatecreateFromParcel(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 ParcelableonSaveInstanceState() {

    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 (!(stateinstanceof 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);

                }

    }

    }

    ItemInfoinfoForChild(View child) {

    for (int i=0; i

    ItemInfo ii =mItems.get(i);

                if (mAdapter.isViewFromObject(child, ii.object)) {

    return ii;

                }

    }

    return null;

        }

    ItemInfoinfoForAnyChild(View child) {

    ViewParent parent;

            while ((parent=child.getParent()) !=this) {

    if (parent ==null || !(parentinstanceof 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

    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 (vinstanceof 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 (event.hasNoModifiers()) {

    handled = arrowScroll(FOCUS_FORWARD);

                        }else if (event.hasModifiers(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 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 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 PagerObserverextends DataSetObserver {

    @Override

            public void onChanged() {

    dataSetChanged();

            }

    @Override

            public void onInvalidated() {

    dataSetChanged();

            }

    }

    }

    用法:在代码中设置mViewPager.setOffscreenPageLimit(0);

    相关文章

      网友评论

          本文标题:自定义不缓存的viewpage---androidx

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