ViewPager+Fragment实现页卡切换

来源:互联网 发布:自动签到脚本 python 编辑:程序博客网 时间:2024/04/30 14:51

这里写图片描述
  现在很多的APP都是这种ui设计模式,在实际开发中经常使用,所以以前我也写过类似的,但基本都是拿来主义,现在正好又遇到了,也不是很复杂,所以就试着自己写一个。
  首先是Activity的布局代码:
  

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.sy.callme.activities.MainActivity">    <android.support.v4.view.ViewPager        android:id="@+id/vp_main_content"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="10"></android.support.v4.view.ViewPager>    <LinearLayout        android:id="@+id/ll_main_menu"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1"        android:background="#EEEEEE"        android:gravity="center"        android:orientation="horizontal">        <TextView            android:id="@+id/tv_main_main1"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:drawableTop="@mipmap/main"            android:gravity="center"            android:text="@string/main"            android:textColor="#000000"            android:textSize="10sp" />        <TextView            android:id="@+id/tv_main_main2"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:drawableTop="@mipmap/main"            android:gravity="center"            android:text="@string/map"            android:textColor="#000000"            android:textSize="10sp" />        <TextView            android:id="@+id/tv_main_main3"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:drawableTop="@mipmap/main"            android:gravity="center"            android:text="@string/order"            android:textColor="#000000"            android:textSize="10sp" />        <TextView            android:id="@+id/tv_main_main4"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:drawableTop="@mipmap/main"            android:gravity="center"            android:text="@string/me"            android:textColor="#000000"            android:textSize="10sp" />    </LinearLayout></LinearLayout>

  布局很简单就是上面一个viewpager,下面一个LinnearLayout,LinnearLayout里按权重放了四个TextView。
  在Activity的onCreate里我写了这几个方法,看名字就能看出来,分别是用来初始化控件、数据和事件的。
  

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        initView();        initData();        initEvent();        vp_content.setCurrentItem(2);        vp_content.setCurrentItem(0);    }

  在initView方法里初始化TextView和fragment,并把它们放到list集合中。
  

 private void initView() {        setContentView(R.layout.activity_main);        vp_content = (ViewPager) findViewById(R.id.vp_main_content);        tv_main1 = (TextView) findViewById(R.id.tv_main_main1);        tv_main2 = (TextView) findViewById(R.id.tv_main_main2);        tv_main3 = (TextView) findViewById(R.id.tv_main_main3);        tv_main4 = (TextView) findViewById(R.id.tv_main_main4);        tvList = new ArrayList<TextView>();        tvList.add(tv_main1);        tvList.add(tv_main2);        tvList.add(tv_main3);        tvList.add(tv_main4);        MainFragment1 fragment1 = new MainFragment1();        MainFragment2 fragment2 = new MainFragment2();        MainFragment3 fragment3 = new MainFragment3();        MainFragment4 fragment4 = new MainFragment4();        fragmentList = new ArrayList<Fragment>();        fragmentList.add(fragment1);        fragmentList.add(fragment2);        fragmentList.add(fragment3);        fragmentList.add(fragment4);    }

  在initData方法里初始化ViewPager的适配器Adapter。并给viewpager设置好adapter。

 private void initData() {        FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {            @Override            public Fragment getItem(int position) {                return fragmentList.get(position);            }            @Override            public int getCount() {                return fragmentList.size();            }        };        vp_content.setAdapter(adapter);    }

  initEvent方法里主要是监听ViewPager的滑动事件,滑动的时候改变下方TextView的图片和文字颜色。另外同样的监听TextView,然后通过ViewPager的setCurrentItem()方法,切换到对应的页面。就是这么简单,这样就基本完成了。demo里的fragment的布局和代码都很简单就不贴了。
  
  我也以为就这样就完成了,万万没想到啊,在onCreate方法里调用 vp_content.setCurrentItem(0);设置初始化的时候第一个TextView处于选中状态,然而光写这一句居然不起作用,后来我又改成setCurrentItem(1)、setCurrentItem(2)都是可以正常运行的,进入页面的时候默认选中的是就是位置为1或者2的。但是setCurrentItem(0),就没有效果。没办法,只能打个断点,debug进去看源码。ViewPager的源码如下:
  

/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package android.support.v4.view;import android.content.Context;import android.content.res.Resources;import android.content.res.TypedArray;import android.database.DataSetObserver;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.os.Build;import android.os.Bundle;import android.os.Parcel;import android.os.Parcelable;import android.os.SystemClock;import android.support.annotation.CallSuper;import android.support.annotation.DrawableRes;import android.support.v4.os.ParcelableCompat;import android.support.v4.os.ParcelableCompatCreatorCallbacks;import android.support.v4.view.accessibility.AccessibilityEventCompat;import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;import android.support.v4.view.accessibility.AccessibilityRecordCompat;import android.support.v4.widget.EdgeEffectCompat;import android.util.AttributeSet;import android.util.Log;import android.view.FocusFinder;import android.view.Gravity;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.SoundEffectConstants;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.ViewParent;import android.view.accessibility.AccessibilityEvent;import android.view.animation.Interpolator;import android.widget.Scroller;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;/** * Layout manager that allows the user to flip left and right * through pages of data.  You supply an implementation of a * {@link 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> * * <p>ViewPager is most often used in conjunction with {@link android.app.Fragment}, * which is a convenient way to supply and manage the lifecycle of each page. * There are standard adapters implemented for using fragments with the ViewPager, * which cover the most common use cases.  These are * {@link android.support.v4.app.FragmentPagerAdapter} and  * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these * classes have simple code showing how to build a full user interface * with them. * * <p>For more information about how to use ViewPager, read <a * href="{@docRoot}training/implementing-navigation/lateral.html">Creating Swipe Views with * Tabs</a>.</p> * * <p>Below is a more complicated example of ViewPager, using it in conjunction * with {@link android.app.ActionBar} tabs.  You can find other examples of using * ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code. * * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java *      complete} */public class ViewPager extends ViewGroup {    private static final String TAG = "ViewPager";    private static final boolean DEBUG = false;    private static final boolean USE_CACHE = false;    private static final int DEFAULT_OFFSCREEN_PAGES = 1;    private static final int MAX_SETTLE_DURATION = 600; // ms    private static final int MIN_DISTANCE_FOR_FLING = 25; // dips    private static final int DEFAULT_GUTTER_SIZE = 16; // dips    private static final int MIN_FLING_VELOCITY = 400; // dips    private static final int[] LAYOUT_ATTRS = new int[] {        android.R.attr.layout_gravity    };    /**     * Used to track what the expected number of items in the adapter should be.     * If the app changes this when we don't expect it, we'll throw a big obnoxious exception.     */    private int mExpectedAdapterCount;    static class ItemInfo {        Object object;        int position;        boolean scrolling;        float widthFactor;        float offset;    }    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) {            t -= 1.0f;            return t * t * t * t * t + 1.0f;        }    };    private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();    private final ItemInfo mTempItem = new ItemInfo();    private final Rect mTempRect = new Rect();    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 boolean mIsScrollStarted;    private PagerObserver mObserver;    private int mPageMargin;    private Drawable mMarginDrawable;    private int mTopPageBounds;    private int mBottomPageBounds;    // Offsets of the first and last items, if known.    // Set during population, used to determine if we are at the beginning    // or end of the pager data set during touch scrolling.    private float mFirstOffset = -Float.MAX_VALUE;    private float mLastOffset = Float.MAX_VALUE;    private int mChildWidthMeasureSpec;    private int mChildHeightMeasureSpec;    private boolean mInLayout;    private boolean mScrollingCacheEnabled;    private boolean mPopulatePending;    private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;    private boolean mIsBeingDragged;    private boolean mIsUnableToDrag;    private int mDefaultGutterSize;    private int mGutterSize;    private int mTouchSlop;    /**     * Position of the last motion event.     */    private float mLastMotionX;    private float mLastMotionY;    private float mInitialMotionX;    private float mInitialMotionY;    /**     * 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 int mFlingDistance;    private int mCloseEnough;    // If the pager is at least this close to its final position, complete the scroll    // on touch down and let the user interact with the content inside instead of    // "catching" the flinging pager.    private static final int CLOSE_ENOUGH = 2; // dp    private boolean mFakeDragging;    private long mFakeDragBeginTime;    private EdgeEffectCompat mLeftEdge;    private EdgeEffectCompat mRightEdge;    private boolean mFirstLayout = true;    private boolean mNeedCalculatePageOffsets = false;    private boolean mCalledSuper;    private int mDecorChildCount;    private List<OnPageChangeListener> mOnPageChangeListeners;    private OnPageChangeListener mOnPageChangeListener;    private OnPageChangeListener mInternalPageChangeListener;    private OnAdapterChangeListener mAdapterChangeListener;    private PageTransformer mPageTransformer;    private Method mSetChildrenDrawingOrderEnabled;    private static final int DRAW_ORDER_DEFAULT = 0;    private static final int DRAW_ORDER_FORWARD = 1;    private static final int DRAW_ORDER_REVERSE = 2;    private int mDrawingOrder;    private ArrayList<View> mDrawingOrderedChildren;    private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();    /**     * 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 final Runnable mEndScrollRunnable = new Runnable() {        public void run() {            setScrollState(SCROLL_STATE_IDLE);            populate();        }    };    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 ViewPager#SCROLL_STATE_IDLE         * @see ViewPager#SCROLL_STATE_DRAGGING         * @see ViewPager#SCROLL_STATE_SETTLING         */        public void onPageScrollStateChanged(int state);    }    /**     * Simple implementation of the {@link OnPageChangeListener} interface with stub     * implementations of each method. Extend this if you do not intend to override     * every method of {@link 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        }    }    /**     * A PageTransformer is invoked whenever a visible/attached page is scrolled.     * This offers an opportunity for the application to apply a custom transformation     * to the page views using animation properties.     *     * <p>As property animation is only supported as of Android 3.0 and forward,     * setting a PageTransformer on a ViewPager on earlier platform versions will     * be ignored.</p>     */    public interface PageTransformer {        /**         * Apply a property transformation to the given page.         *         * @param page Apply the transformation to this page         * @param position Position of page relative to the current front-and-center         *                 position of the pager. 0 is front and center. 1 is one full         *                 page position to the right, and -1 is one page position to the left.         */        public void transformPage(View page, float position);    }    /**     * Used internally to monitor when adapters are switched.     */    interface OnAdapterChangeListener {        public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);    }    /**     * Used internally to tag special types of child views that should be added as     * pager decorations by default.     */    interface Decor {}    public ViewPager(Context context) {        super(context);        initViewPager();    }    public ViewPager(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);        final float density = context.getResources().getDisplayMetrics().density;        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);        mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();        mLeftEdge = new EdgeEffectCompat(context);        mRightEdge = new EdgeEffectCompat(context);        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);        mCloseEnough = (int) (CLOSE_ENOUGH * density);        mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);        ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());        if (ViewCompat.getImportantForAccessibility(this)                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {            ViewCompat.setImportantForAccessibility(this,                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);        }        ViewCompat.setOnApplyWindowInsetsListener(this,                new android.support.v4.view.OnApplyWindowInsetsListener() {                    private final Rect mTempRect = new Rect();                    @Override                    public WindowInsetsCompat onApplyWindowInsets(final View v,                            final WindowInsetsCompat originalInsets) {                        // First let the ViewPager itself try and consume them...                        final WindowInsetsCompat applied =                                ViewCompat.onApplyWindowInsets(v, originalInsets);                        if (applied.isConsumed()) {                            // If the ViewPager consumed all insets, return now                            return applied;                        }                        // Now we'll manually dispatch the insets to our children. Since ViewPager                        // children are always full-height, we do not want to use the standard                        // ViewGroup dispatchApplyWindowInsets since if child 0 consumes them,                        // the rest of the children will not receive any insets. To workaround this                        // we manually dispatch the applied insets, not allowing children to                        // consume them from each other. We do however keep track of any insets                        // which are consumed, returning the union of our children's consumption                        final Rect res = mTempRect;                        res.left = applied.getSystemWindowInsetLeft();                        res.top = applied.getSystemWindowInsetTop();                        res.right = applied.getSystemWindowInsetRight();                        res.bottom = applied.getSystemWindowInsetBottom();                        for (int i = 0, count = getChildCount(); i < count; i++) {                            final WindowInsetsCompat childInsets = ViewCompat                                    .dispatchApplyWindowInsets(getChildAt(i), applied);                            // Now keep track of any consumed by tracking each dimension's min                            // value                            res.left = Math.min(childInsets.getSystemWindowInsetLeft(),                                    res.left);                            res.top = Math.min(childInsets.getSystemWindowInsetTop(),                                    res.top);                            res.right = Math.min(childInsets.getSystemWindowInsetRight(),                                    res.right);                            res.bottom = Math.min(childInsets.getSystemWindowInsetBottom(),                                    res.bottom);                        }                        // Now return a new WindowInsets, using the consumed window insets                        return applied.replaceSystemWindowInsets(                                res.left, res.top, res.right, res.bottom);                    }                });    }    @Override    protected void onDetachedFromWindow() {        removeCallbacks(mEndScrollRunnable);        // To be on the safe side, abort the scroller        if ((mScroller != null) && !mScroller.isFinished()) {            mScroller.abortAnimation();        }        super.onDetachedFromWindow();    }    private void setScrollState(int newState) {        if (mScrollState == newState) {            return;        }        mScrollState = newState;        if (mPageTransformer != null) {            // PageTransformers can do complex things that benefit from hardware layers.            enableLayers(newState != SCROLL_STATE_IDLE);        }        dispatchOnScrollStateChanged(newState);    }    /**     * Set a PagerAdapter that will supply views for this pager as needed.     *     * @param adapter Adapter to use     */    public void setAdapter(PagerAdapter adapter) {        if (mAdapter != null) {            mAdapter.setViewPagerObserver(null);            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();            removeNonDecorViews();            mCurItem = 0;            scrollTo(0, 0);        }        final PagerAdapter oldAdapter = mAdapter;        mAdapter = adapter;        mExpectedAdapterCount = 0;        if (mAdapter != null) {            if (mObserver == null) {                mObserver = new PagerObserver();            }            mAdapter.setViewPagerObserver(mObserver);            mPopulatePending = false;            final boolean wasFirstLayout = mFirstLayout;            mFirstLayout = true;            mExpectedAdapterCount = mAdapter.getCount();            if (mRestoredCurItem >= 0) {                mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);                setCurrentItemInternal(mRestoredCurItem, false, true);                mRestoredCurItem = -1;                mRestoredAdapterState = null;                mRestoredClassLoader = null;            } else if (!wasFirstLayout) {                populate();            } else {                requestLayout();            }        }        if (mAdapterChangeListener != null && oldAdapter != adapter) {            mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);        }    }    private void removeNonDecorViews() {        for (int i = 0; i < getChildCount(); i++) {            final View child = getChildAt(i);            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            if (!lp.isDecor) {                removeViewAt(i);                i--;            }        }    }    /**     * Retrieve the current adapter supplying pages.     *     * @return The currently registered PagerAdapter     */    public PagerAdapter getAdapter() {        return mAdapter;    }    void setOnAdapterChangeListener(OnAdapterChangeListener listener) {        mAdapterChangeListener = listener;    }    private int getClientWidth() {        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();    }    /**     * Set the currently selected page. If the ViewPager has already been through its first     * layout with its current adapter 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;        if (mFirstLayout) {            // We don't have any idea how big we are yet and shouldn't have any pages either.            // Just set things up and let the pending layout handle things.            mCurItem = item;            if (dispatchSelected) {                dispatchOnPageSelected(item);            }            requestLayout();        } else {            populate(item);            scrollToItem(item, smoothScroll, velocity, dispatchSelected);        }    }    private void scrollToItem(int item, boolean smoothScroll, int velocity,            boolean dispatchSelected) {        final ItemInfo curInfo = infoForPosition(item);        int destX = 0;        if (curInfo != null) {            final int width = getClientWidth();            destX = (int) (width * Math.max(mFirstOffset,                    Math.min(curInfo.offset, mLastOffset)));        }        if (smoothScroll) {            smoothScrollTo(destX, 0, velocity);            if (dispatchSelected) {                dispatchOnPageSelected(item);            }        } else {            if (dispatchSelected) {                dispatchOnPageSelected(item);            }            completeScroll(false);            scrollTo(destX, 0);            pageScrolled(destX);        }    }    /**     * Set a listener that will be invoked whenever the page changes or is incrementally     * scrolled. See {@link OnPageChangeListener}.     *     * @param listener Listener to set     *     * @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)}     * and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead.     */    @Deprecated    public void setOnPageChangeListener(OnPageChangeListener listener) {        mOnPageChangeListener = listener;    }    /**     * Add a listener that will be invoked whenever the page changes or is incrementally     * scrolled. See {@link OnPageChangeListener}.     *     * <p>Components that add a listener should take care to remove it when finished.     * Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()}     * to remove all attached listeners.</p>     *     * @param listener listener to add     */    public void addOnPageChangeListener(OnPageChangeListener listener) {        if (mOnPageChangeListeners == null) {            mOnPageChangeListeners = new ArrayList<>();        }        mOnPageChangeListeners.add(listener);    }    /**     * Remove a listener that was previously added via     * {@link #addOnPageChangeListener(OnPageChangeListener)}.     *     * @param listener listener to remove     */    public void removeOnPageChangeListener(OnPageChangeListener listener) {        if (mOnPageChangeListeners != null) {            mOnPageChangeListeners.remove(listener);        }    }    /**     * Remove all listeners that are notified of any changes in scroll state or position.     */    public void clearOnPageChangeListeners() {        if (mOnPageChangeListeners != null) {            mOnPageChangeListeners.clear();        }    }    /**     * Set a {@link PageTransformer} that will be called for each attached page whenever     * the scroll position is changed. This allows the application to apply custom property     * transformations to each page, overriding the default sliding look and feel.     *     * <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did not exist.     * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.</p>     *     * @param reverseDrawingOrder true if the supplied PageTransformer requires page views     *                            to be drawn from last to first instead of first to last.     * @param transformer PageTransformer that will modify each page's animation properties     */    public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {        if (Build.VERSION.SDK_INT >= 11) {            final boolean hasTransformer = transformer != null;            final boolean needsPopulate = hasTransformer != (mPageTransformer != null);            mPageTransformer = transformer;            setChildrenDrawingOrderEnabledCompat(hasTransformer);            if (hasTransformer) {                mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;            } else {                mDrawingOrder = DRAW_ORDER_DEFAULT;            }            if (needsPopulate) populate();        }    }    void setChildrenDrawingOrderEnabledCompat(boolean enable) {        if (Build.VERSION.SDK_INT >= 7) {            if (mSetChildrenDrawingOrderEnabled == null) {                try {                    mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(                            "setChildrenDrawingOrderEnabled", new Class[] { Boolean.TYPE });                } catch (NoSuchMethodException e) {                    Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e);                }            }            try {                mSetChildrenDrawingOrderEnabled.invoke(this, enable);            } catch (Exception e) {                Log.e(TAG, "Error changing children drawing order", e);            }        }    }    @Override    protected int getChildDrawingOrder(int childCount, int i) {        final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;        final int result = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;        return result;    }    /**     * Set a separate OnPageChangeListener for internal use by the support library.     *     * @param listener Listener to set     * @return The old listener that was set, if any.     */    OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {        OnPageChangeListener oldListener = mInternalPageChangeListener;        mInternalPageChangeListener = listener;        return oldListener;    }    /**     * 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(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(@DrawableRes int resId) {        setPageMarginDrawable(getContext().getResources().getDrawable(resId));    }    @Override    protected boolean verifyDrawable(Drawable who) {        return super.verifyDrawable(who) || who == mMarginDrawable;    }    @Override    protected void drawableStateChanged() {        super.drawableStateChanged();        final Drawable d = mMarginDrawable;        if (d != null && d.isStateful()) {            d.setState(getDrawableState());        }    }    // We want the duration of the page snap animation to be influenced by the distance that    // the screen has to travel, however, we don't want this duration to be effected in a    // purely linear fashion. Instead, we use this method to moderate the effect that the distance    // of travel has on the overall snap duration.    float distanceInfluenceForSnapDuration(float f) {        f -= 0.5f; // center the values about 0.        f *= 0.3f * Math.PI / 2.0f;        return (float) Math.sin(f);    }    /**     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.     *     * @param x the number of pixels to scroll by on the X axis     * @param y the number of pixels to scroll by on the Y axis     */    void smoothScrollTo(int x, int y) {        smoothScrollTo(x, y, 0);    }    /**     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.     *     * @param x the number of pixels to scroll by on the X axis     * @param y the number of pixels to scroll by on the Y axis     * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)     */    void smoothScrollTo(int x, int y, int velocity) {        if (getChildCount() == 0) {            // Nothing to do.            setScrollingCacheEnabled(false);            return;        }        int sx;        boolean wasScrolling = (mScroller != null) && !mScroller.isFinished();        if (wasScrolling) {            // We're in the middle of a previously initiated scrolling. Check to see            // whether that scrolling has actually started (if we always call getStartX            // we can get a stale value from the scroller if it hadn't yet had its first            // computeScrollOffset call) to decide what is the current scrolling position.            sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX();            // And abort the current scrolling.            mScroller.abortAnimation();            setScrollingCacheEnabled(false);        } else {            sx = getScrollX();        }        int sy = getScrollY();        int dx = x - sx;        int dy = y - sy;        if (dx == 0 && dy == 0) {            completeScroll(false);            populate();            setScrollState(SCROLL_STATE_IDLE);            return;        }        setScrollingCacheEnabled(true);        setScrollState(SCROLL_STATE_SETTLING);        final int width = getClientWidth();        final int halfWidth = width / 2;        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);        final float distance = halfWidth + halfWidth *                distanceInfluenceForSnapDuration(distanceRatio);        int duration;        velocity = Math.abs(velocity);        if (velocity > 0) {            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));        } else {            final float pageWidth = width * mAdapter.getPageWidth(mCurItem);            final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);            duration = (int) ((pageDelta + 1) * 100);        }        duration = Math.min(duration, MAX_SETTLE_DURATION);        // Reset the "scroll started" flag. It will be flipped to true in all places        // where we call computeScrollOffset().        mIsScrollStarted = false;        mScroller.startScroll(sx, sy, dx, dy, duration);        ViewCompat.postInvalidateOnAnimation(this);    }    ItemInfo addNewItem(int position, int index) {        ItemInfo ii = new ItemInfo();        ii.position = position;        ii.object = mAdapter.instantiateItem(this, position);        ii.widthFactor = mAdapter.getPageWidth(position);        if (index < 0 || index >= mItems.size()) {            mItems.add(ii);        } else {            mItems.add(index, ii);        }        return ii;    }    void dataSetChanged() {        // This method only gets called if our observer is attached, so mAdapter is non-null.        final int adapterCount = mAdapter.getCount();        mExpectedAdapterCount = adapterCount;        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 &&                mItems.size() < adapterCount;        int newCurrItem = mCurItem;        boolean isUpdating = false;        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--;                if (!isUpdating) {                    mAdapter.startUpdate(this);                    isUpdating = true;                }                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, adapterCount - 1));                    needPopulate = true;                }                continue;            }            if (ii.position != newPos) {                if (ii.position == mCurItem) {                    // Our current item changed position. Follow it.                    newCurrItem = newPos;                }                ii.position = newPos;                needPopulate = true;            }        }        if (isUpdating) {            mAdapter.finishUpdate(this);        }        Collections.sort(mItems, COMPARATOR);        if (needPopulate) {            // Reset our known page widths; populate will recompute them.            final int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                final View child = getChildAt(i);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (!lp.isDecor) {                    lp.widthFactor = 0.f;                }            }            setCurrentItemInternal(newCurrItem, false, true);            requestLayout();        }    }    void populate() {        populate(mCurItem);    }    void populate(int newCurrentItem) {        ItemInfo oldCurInfo = null;        if (mCurItem != newCurrentItem) {            oldCurInfo = infoForPosition(mCurItem);            mCurItem = newCurrentItem;        }        if (mAdapter == null) {            sortChildDrawingOrder();            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...");            sortChildDrawingOrder();            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 (N != mExpectedAdapterCount) {            String resName;            try {                resName = getResources().getResourceName(getId());            } catch (Resources.NotFoundException e) {                resName = Integer.toHexString(getId());            }            throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +                    " contents without calling PagerAdapter#notifyDataSetChanged!" +                    " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +                    " Pager id: " + resName +                    " Pager class: " + getClass() +                    " Problematic adapter: " + mAdapter.getClass());        }        // Locate the currently focused item or add it if needed.        int curIndex = -1;        ItemInfo curItem = null;        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {            final ItemInfo ii = mItems.get(curIndex);            if (ii.position >= mCurItem) {                if (ii.position == mCurItem) curItem = ii;                break;            }        }        if (curItem == null && N > 0) {            curItem = addNewItem(mCurItem, curIndex);        }        // Fill 3x the available width or up to the number of offscreen        // pages requested to either side, whichever is larger.        // If we have no current item we have no work to do.        if (curItem != null) {            float extraWidthLeft = 0.f;            int itemIndex = curIndex - 1;            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;            final int clientWidth = getClientWidth();            final float leftWidthNeeded = clientWidth <= 0 ? 0 :                    2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;            for (int pos = mCurItem - 1; pos >= 0; pos--) {                if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {                    if (ii == null) {                        break;                    }                    if (pos == ii.position && !ii.scrolling) {                        mItems.remove(itemIndex);                        mAdapter.destroyItem(this, pos, ii.object);                        if (DEBUG) {                            Log.i(TAG, "populate() - destroyItem() with pos: " + pos +                                    " view: " + ((View) ii.object));                        }                        itemIndex--;                        curIndex--;                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;                    }                } else if (ii != null && pos == ii.position) {                    extraWidthLeft += ii.widthFactor;                    itemIndex--;                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;                } else {                    ii = addNewItem(pos, itemIndex + 1);                    extraWidthLeft += ii.widthFactor;                    curIndex++;                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;                }            }            float extraWidthRight = curItem.widthFactor;            itemIndex = curIndex + 1;            if (extraWidthRight < 2.f) {                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                final float rightWidthNeeded = clientWidth <= 0 ? 0 :                        (float) getPaddingRight() / (float) clientWidth + 2.f;                for (int pos = mCurItem + 1; pos < N; pos++) {                    if (extraWidthRight >= rightWidthNeeded && pos > endPos) {                        if (ii == null) {                            break;                        }                        if (pos == ii.position && !ii.scrolling) {                            mItems.remove(itemIndex);                            mAdapter.destroyItem(this, pos, ii.object);                            if (DEBUG) {                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos +                                        " view: " + ((View) ii.object));                            }                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                        }                    } else if (ii != null && pos == ii.position) {                        extraWidthRight += ii.widthFactor;                        itemIndex++;                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                    } else {                        ii = addNewItem(pos, itemIndex);                        itemIndex++;                        extraWidthRight += ii.widthFactor;                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;                    }                }            }            calculatePageOffsets(curItem, curIndex, oldCurInfo);        }        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);            }        }        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);        mAdapter.finishUpdate(this);        // Check width measurement of current pages and drawing sort order.        // Update LayoutParams as needed.        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = getChildAt(i);            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            lp.childIndex = i;            if (!lp.isDecor && lp.widthFactor == 0.f) {                // 0 means requery the adapter for this, it doesn't have a valid width.                final ItemInfo ii = infoForChild(child);                if (ii != null) {                    lp.widthFactor = ii.widthFactor;                    lp.position = ii.position;                }            }        }        sortChildDrawingOrder();        if (hasFocus()) {            View currentFocused = findFocus();            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;            if (ii == null || ii.position != mCurItem) {                for (int i=0; i<getChildCount(); i++) {                    View child = getChildAt(i);                    ii = infoForChild(child);                    if (ii != null && ii.position == mCurItem) {                        if (child.requestFocus(View.FOCUS_FORWARD)) {                            break;                        }                    }                }            }        }    }    private void sortChildDrawingOrder() {        if (mDrawingOrder != DRAW_ORDER_DEFAULT) {            if (mDrawingOrderedChildren == null) {                mDrawingOrderedChildren = new ArrayList<View>();            } else {                mDrawingOrderedChildren.clear();            }            final int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                final View child = getChildAt(i);                mDrawingOrderedChildren.add(child);            }            Collections.sort(mDrawingOrderedChildren, sPositionComparator);        }    }    private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {        final int N = mAdapter.getCount();        final int width = getClientWidth();        final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;        // Fix up offsets for later layout.        if (oldCurInfo != null) {            final int oldCurPosition = oldCurInfo.position;            // Base offsets off of oldCurInfo.            if (oldCurPosition < curItem.position) {                int itemIndex = 0;                ItemInfo ii = null;                float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;                for (int pos = oldCurPosition + 1;                        pos <= curItem.position && itemIndex < mItems.size(); pos++) {                    ii = mItems.get(itemIndex);                    while (pos > ii.position && itemIndex < mItems.size() - 1) {                        itemIndex++;                        ii = mItems.get(itemIndex);                    }                    while (pos < ii.position) {                        // We don't have an item populated for this,                        // ask the adapter for an offset.                        offset += mAdapter.getPageWidth(pos) + marginOffset;                        pos++;                    }                    ii.offset = offset;                    offset += ii.widthFactor + marginOffset;                }            } else if (oldCurPosition > curItem.position) {                int itemIndex = mItems.size() - 1;                ItemInfo ii = null;                float offset = oldCurInfo.offset;                for (int pos = oldCurPosition - 1;                        pos >= curItem.position && itemIndex >= 0; pos--) {                    ii = mItems.get(itemIndex);                    while (pos < ii.position && itemIndex > 0) {                        itemIndex--;                        ii = mItems.get(itemIndex);                    }                    while (pos > ii.position) {                        // We don't have an item populated for this,                        // ask the adapter for an offset.                        offset -= mAdapter.getPageWidth(pos) + marginOffset;                        pos--;                    }                    offset -= ii.widthFactor + marginOffset;                    ii.offset = offset;                }            }        }        // Base all offsets off of curItem.        final int itemCount = mItems.size();        float offset = curItem.offset;        int pos = curItem.position - 1;        mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;        mLastOffset = curItem.position == N - 1 ?                curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;        // Previous pages        for (int i = curIndex - 1; i >= 0; i--, pos--) {            final ItemInfo ii = mItems.get(i);            while (pos > ii.position) {                offset -= mAdapter.getPageWidth(pos--) + marginOffset;            }            offset -= ii.widthFactor + marginOffset;            ii.offset = offset;            if (ii.position == 0) mFirstOffset = offset;        }        offset = curItem.offset + curItem.widthFactor + marginOffset;        pos = curItem.position + 1;        // Next pages        for (int i = curIndex + 1; i < itemCount; i++, pos++) {            final ItemInfo ii = mItems.get(i);            while (pos < ii.position) {                offset += mAdapter.getPageWidth(pos++) + marginOffset;            }            if (ii.position == N - 1) {                mLastOffset = offset + ii.widthFactor - 1;            }            ii.offset = offset;            offset += ii.widthFactor + marginOffset;        }        mNeedCalculatePageOffsets = false;    }    /**     * This is the persistent state that is saved by ViewPager.  Only needed     * if you are creating a sublass of ViewPager that must save its own     * state, in which case it should implement a subclass of this which     * contains that state.     */    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 Parcelable.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, ViewGroup.LayoutParams params) {        if (!checkLayoutParams(params)) {            params = generateLayoutParams(params);        }        final LayoutParams lp = (LayoutParams) params;        lp.isDecor |= child instanceof Decor;        if (mInLayout) {            if (lp != null && lp.isDecor) {                throw new IllegalStateException("Cannot add pager decor view during layout");            }            lp.needsMeasure = true;            addViewInLayout(child, index, params);        } else {            super.addView(child, index, params);        }        if (USE_CACHE) {            if (child.getVisibility() != GONE) {                child.setDrawingCacheEnabled(mScrollingCacheEnabled);            } else {                child.setDrawingCacheEnabled(false);            }        }    }    @Override    public void removeView(View view) {        if (mInLayout) {            removeViewInLayout(view);        } else {            super.removeView(view);        }    }    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);    }    ItemInfo infoForPosition(int position) {        for (int i = 0; i < mItems.size(); i++) {            ItemInfo ii = mItems.get(i);            if (ii.position == position) {                return ii;            }        }        return null;    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        mFirstLayout = true;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // For simple implementation, our internal size is always 0.        // We depend on the container to specify the layout size of        // our view.  We can't really know what it is since we will be        // adding and removing different arbitrary views and do not        // want the layout to change as this happens.        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),                getDefaultSize(0, heightMeasureSpec));        final int measuredWidth = getMeasuredWidth();        final int maxGutterSize = measuredWidth / 10;        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);        // Children are just made to fill our space.        int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();        int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();        /*         * Make sure all children have been properly measured. Decor views first.         * Right now we cheat and make this less complicated by assuming decor         * views won't intersect. We will pin to edges based on gravity.         */        int size = getChildCount();        for (int i = 0; i < size; ++i) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (lp != null && lp.isDecor) {                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;                    int widthMode = MeasureSpec.AT_MOST;                    int heightMode = MeasureSpec.AT_MOST;                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;                    if (consumeVertical) {                        widthMode = MeasureSpec.EXACTLY;                    } else if (consumeHorizontal) {                        heightMode = MeasureSpec.EXACTLY;                    }                    int widthSize = childWidthSize;                    int heightSize = childHeightSize;                    if (lp.width != LayoutParams.WRAP_CONTENT) {                        widthMode = MeasureSpec.EXACTLY;                        if (lp.width != LayoutParams.FILL_PARENT) {                            widthSize = lp.width;                        }                    }                    if (lp.height != LayoutParams.WRAP_CONTENT) {                        heightMode = MeasureSpec.EXACTLY;                        if (lp.height != LayoutParams.FILL_PARENT) {                            heightSize = lp.height;                        }                    }                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);                    child.measure(widthSpec, heightSpec);                    if (consumeVertical) {                        childHeightSize -= child.getMeasuredHeight();                    } else if (consumeHorizontal) {                        childWidthSize -= child.getMeasuredWidth();                    }                }            }        }        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);        // Make sure we have created all fragments that we need to have shown.        mInLayout = true;        populate();        mInLayout = false;        // Page views next.        size = getChildCount();        for (int i = 0; i < size; ++i) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child                        + ": " + mChildWidthMeasureSpec);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (lp == null || !lp.isDecor) {                    final int widthSpec = MeasureSpec.makeMeasureSpec(                            (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);                    child.measure(widthSpec, mChildHeightMeasureSpec);                }            }        }    }    @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) {        if (oldWidth > 0 && !mItems.isEmpty()) {            if (!mScroller.isFinished()) {                mScroller.setFinalX(getCurrentItem() * getClientWidth());            } else {                final int widthWithMargin = width - getPaddingLeft() - getPaddingRight() + margin;                final int oldWidthWithMargin = oldWidth - getPaddingLeft() - getPaddingRight()                        + oldMargin;                final int xpos = getScrollX();                final float pageOffset = (float) xpos / oldWidthWithMargin;                final int newOffsetPixels = (int) (pageOffset * widthWithMargin);                scrollTo(newOffsetPixels, getScrollY());            }        } else {            final ItemInfo ii = infoForPosition(mCurItem);            final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;            final int scrollPos = (int) (scrollOffset *                                         (width - getPaddingLeft() - getPaddingRight()));            if (scrollPos != getScrollX()) {                completeScroll(false);                scrollTo(scrollPos, getScrollY());            }        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int count = getChildCount();        int width = r - l;        int height = b - t;        int paddingLeft = getPaddingLeft();        int paddingTop = getPaddingTop();        int paddingRight = getPaddingRight();        int paddingBottom = getPaddingBottom();        final int scrollX = getScrollX();        int decorCount = 0;        // First pass - decor views. We need to do this in two passes so that        // we have the proper offsets for non-decor views later.        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                int childLeft = 0;                int childTop = 0;                if (lp.isDecor) {                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;                    switch (hgrav) {                        default:                            childLeft = paddingLeft;                            break;                        case Gravity.LEFT:                            childLeft = paddingLeft;                            paddingLeft += child.getMeasuredWidth();                            break;                        case Gravity.CENTER_HORIZONTAL:                            childLeft = Math.max((width - child.getMeasuredWidth()) / 2,                                    paddingLeft);                            break;                        case Gravity.RIGHT:                            childLeft = width - paddingRight - child.getMeasuredWidth();                            paddingRight += child.getMeasuredWidth();                            break;                    }                    switch (vgrav) {                        default:                            childTop = paddingTop;                            break;                        case Gravity.TOP:                            childTop = paddingTop;                            paddingTop += child.getMeasuredHeight();                            break;                        case Gravity.CENTER_VERTICAL:                            childTop = Math.max((height - child.getMeasuredHeight()) / 2,                                    paddingTop);                            break;                        case Gravity.BOTTOM:                            childTop = height - paddingBottom - child.getMeasuredHeight();                            paddingBottom += child.getMeasuredHeight();                            break;                    }                    childLeft += scrollX;                    child.layout(childLeft, childTop,                            childLeft + child.getMeasuredWidth(),                            childTop + child.getMeasuredHeight());                    decorCount++;                }            }        }        final int childWidth = width - paddingLeft - paddingRight;        // Page views. Do this once we have the right padding offsets from above.        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                ItemInfo ii;                if (!lp.isDecor && (ii = infoForChild(child)) != null) {                    int loff = (int) (childWidth * ii.offset);                    int childLeft = paddingLeft + loff;                    int childTop = paddingTop;                    if (lp.needsMeasure) {                        // This was added during layout and needs measurement.                        // Do it now that we know what we're working with.                        lp.needsMeasure = false;                        final int widthSpec = MeasureSpec.makeMeasureSpec(                                (int) (childWidth * lp.widthFactor),                                MeasureSpec.EXACTLY);                        final int heightSpec = MeasureSpec.makeMeasureSpec(                                (int) (height - paddingTop - paddingBottom),                                MeasureSpec.EXACTLY);                        child.measure(widthSpec, heightSpec);                    }                    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());                }            }        }        mTopPageBounds = paddingTop;        mBottomPageBounds = height - paddingBottom;        mDecorChildCount = decorCount;        if (mFirstLayout) {            scrollToItem(mCurItem, false, 0, false);        }        mFirstLayout = false;    }    @Override    public void computeScroll() {        mIsScrollStarted = true;        if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {            int oldX = getScrollX();            int oldY = getScrollY();            int x = mScroller.getCurrX();            int y = mScroller.getCurrY();            if (oldX != x || oldY != y) {                scrollTo(x, y);                if (!pageScrolled(x)) {                    mScroller.abortAnimation();                    scrollTo(0, y);                }            }            // Keep on drawing until the animation has finished.            ViewCompat.postInvalidateOnAnimation(this);            return;        }        // Done with scroll, clean up state.        completeScroll(true);    }    private boolean pageScrolled(int xpos) {        if (mItems.size() == 0) {            mCalledSuper = false;            onPageScrolled(0, 0, 0);            if (!mCalledSuper) {                throw new IllegalStateException(                        "onPageScrolled did not call superclass implementation");            }            return false;        }        final ItemInfo ii = infoForCurrentScrollPosition();        final int width = getClientWidth();        final int widthWithMargin = width + mPageMargin;        final float marginOffset = (float) mPageMargin / width;        final int currentPage = ii.position;        final float pageOffset = (((float) xpos / width) - ii.offset) /                (ii.widthFactor + marginOffset);        final int offsetPixels = (int) (pageOffset * widthWithMargin);        mCalledSuper = false;        onPageScrolled(currentPage, pageOffset, offsetPixels);        if (!mCalledSuper) {            throw new IllegalStateException(                    "onPageScrolled did not call superclass implementation");        }        return true;    }    /**     * 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.     * If you override this method you must call through to the superclass implementation     * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled     * returns.     *     * @param position Position index of the first page currently being displayed.     *                 Page position+1 will be visible if positionOffset is nonzero.     * @param offset Value from [0, 1) indicating the offset from the page at position.     * @param offsetPixels Value in pixels indicating the offset from position.     */    @CallSuper    protected void onPageScrolled(int position, float offset, int offsetPixels) {        // Offset any decor views if needed - keep them on-screen at all times.        if (mDecorChildCount > 0) {            final int scrollX = getScrollX();            int paddingLeft = getPaddingLeft();            int paddingRight = getPaddingRight();            final int width = getWidth();            final int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                final View child = getChildAt(i);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (!lp.isDecor) continue;                final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;                int childLeft = 0;                switch (hgrav) {                    default:                        childLeft = paddingLeft;                        break;                    case Gravity.LEFT:                        childLeft = paddingLeft;                        paddingLeft += child.getWidth();                        break;                    case Gravity.CENTER_HORIZONTAL:                        childLeft = Math.max((width - child.getMeasuredWidth()) / 2,                                paddingLeft);                        break;                    case Gravity.RIGHT:                        childLeft = width - paddingRight - child.getMeasuredWidth();                        paddingRight += child.getMeasuredWidth();                        break;                }                childLeft += scrollX;                final int childOffset = childLeft - child.getLeft();                if (childOffset != 0) {                    child.offsetLeftAndRight(childOffset);                }            }        }        dispatchOnPageScrolled(position, offset, offsetPixels);        if (mPageTransformer != null) {            final int scrollX = getScrollX();            final int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                final View child = getChildAt(i);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                if (lp.isDecor) continue;                final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();                mPageTransformer.transformPage(child, transformPos);            }        }        mCalledSuper = true;    }    private void dispatchOnPageScrolled(int position, float offset, int offsetPixels) {        if (mOnPageChangeListener != null) {            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);        }        if (mOnPageChangeListeners != null) {            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {                OnPageChangeListener listener = mOnPageChangeListeners.get(i);                if (listener != null) {                    listener.onPageScrolled(position, offset, offsetPixels);                }            }        }        if (mInternalPageChangeListener != null) {            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);        }    }    private void dispatchOnPageSelected(int position) {        if (mOnPageChangeListener != null) {            mOnPageChangeListener.onPageSelected(position);        }        if (mOnPageChangeListeners != null) {            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {                OnPageChangeListener listener = mOnPageChangeListeners.get(i);                if (listener != null) {                    listener.onPageSelected(position);                }            }        }        if (mInternalPageChangeListener != null) {            mInternalPageChangeListener.onPageSelected(position);        }    }    private void dispatchOnScrollStateChanged(int state) {        if (mOnPageChangeListener != null) {            mOnPageChangeListener.onPageScrollStateChanged(state);        }        if (mOnPageChangeListeners != null) {            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {                OnPageChangeListener listener = mOnPageChangeListeners.get(i);                if (listener != null) {                    listener.onPageScrollStateChanged(state);                }            }        }        if (mInternalPageChangeListener != null) {            mInternalPageChangeListener.onPageScrollStateChanged(state);        }    }    private void completeScroll(boolean postEvents) {        boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;        if (needPopulate) {            // Done with scroll, no longer want to cache view drawing.            setScrollingCacheEnabled(false);            boolean wasScrolling = !mScroller.isFinished();            if (wasScrolling) {                mScroller.abortAnimation();                int oldX = getScrollX();                int oldY = getScrollY();                int x = mScroller.getCurrX();                int y = mScroller.getCurrY();                if (oldX != x || oldY != y) {                    scrollTo(x, y);                    if (x != oldX) {                        pageScrolled(x);                    }                }            }        }        mPopulatePending = false;        for (int i=0; i<mItems.size(); i++) {            ItemInfo ii = mItems.get(i);            if (ii.scrolling) {                needPopulate = true;                ii.scrolling = false;            }        }        if (needPopulate) {            if (postEvents) {                ViewCompat.postOnAnimation(this, mEndScrollRunnable);            } else {                mEndScrollRunnable.run();            }        }    }    private boolean isGutterDrag(float x, float dx) {        return (x < mGutterSize && dx > 0) || (x > getWidth() - mGutterSize && dx < 0);    }    private void enableLayers(boolean enable) {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final int layerType = enable ?                    ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;            ViewCompat.setLayerType(getChildAt(i), layerType, null);        }    }    @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!");            resetTouch();            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 - mInitialMotionY);                if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);                if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&                        canScroll(this, false, (int) dx, (int) x, (int) y)) {                    // Nested view has scrollable area under this point. Let it be handled there.                    mLastMotionX = x;                    mLastMotionY = y;                    mIsUnableToDrag = true;                    return false;                }                if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {                    if (DEBUG) Log.v(TAG, "Starting drag!");                    mIsBeingDragged = true;                    requestParentDisallowInterceptTouchEvent(true);                    setScrollState(SCROLL_STATE_DRAGGING);                    mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :                            mInitialMotionX - mTouchSlop;                    mLastMotionY = y;                    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;                }                if (mIsBeingDragged) {                    // Scroll to follow the motion event                    if (performDrag(x)) {                        ViewCompat.postInvalidateOnAnimation(this);                    }                }                break;            }            case MotionEvent.ACTION_DOWN: {                /*                 * Remember location of down touch.                 * ACTION_DOWN always refers to pointer index 0.                 */                mLastMotionX = mInitialMotionX = ev.getX();                mLastMotionY = mInitialMotionY = ev.getY();                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);                mIsUnableToDrag = false;                mIsScrollStarted = true;                mScroller.computeScrollOffset();                if (mScrollState == SCROLL_STATE_SETTLING &&                        Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {                    // Let the user 'catch' the pager as it animates.                    mScroller.abortAnimation();                    mPopulatePending = false;                    populate();                    mIsBeingDragged = true;                    requestParentDisallowInterceptTouchEvent(true);                    setScrollState(SCROLL_STATE_DRAGGING);                } else {                    completeScroll(false);                    mIsBeingDragged = false;                }                if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY                        + " mIsBeingDragged=" + mIsBeingDragged                        + "mIsUnableToDrag=" + mIsUnableToDrag);                break;            }            case MotionEventCompat.ACTION_POINTER_UP:                onSecondaryPointerUp(ev);                break;        }        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(ev);        /*         * 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: {                mScroller.abortAnimation();                mPopulatePending = false;                populate();                // Remember where the motion event started                mLastMotionX = mInitialMotionX = ev.getX();                mLastMotionY = mInitialMotionY = ev.getY();                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);                break;            }            case MotionEvent.ACTION_MOVE:                if (!mIsBeingDragged) {                    final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);                    if (pointerIndex == -1) {                        // A child has consumed some touch events and put us into an inconsistent state.                        needsInvalidate = resetTouch();                        break;                    }                    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;                        requestParentDisallowInterceptTouchEvent(true);                        mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :                                mInitialMotionX - mTouchSlop;                        mLastMotionY = y;                        setScrollState(SCROLL_STATE_DRAGGING);                        setScrollingCacheEnabled(true);                        // Disallow Parent Intercept, just in case                        ViewParent parent = getParent();                        if (parent != null) {                            parent.requestDisallowInterceptTouchEvent(true);                        }                    }                }                // Not else! Note that mIsBeingDragged can be set above.                if (mIsBeingDragged) {                    // Scroll to follow the motion event                    final int activePointerIndex = MotionEventCompat.findPointerIndex(                            ev, mActivePointerId);                    final float x = MotionEventCompat.getX(ev, activePointerIndex);                    needsInvalidate |= performDrag(x);                }                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 width = getClientWidth();                    final int scrollX = getScrollX();                    final ItemInfo ii = infoForCurrentScrollPosition();                    final int currentPage = ii.position;                    final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;                    final int activePointerIndex =                            MotionEventCompat.findPointerIndex(ev, mActivePointerId);                    final float x = MotionEventCompat.getX(ev, activePointerIndex);                    final int totalDelta = (int) (x - mInitialMotionX);                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,                            totalDelta);                    setCurrentItemInternal(nextPage, true, true, initialVelocity);                    needsInvalidate = resetTouch();                }                break;            case MotionEvent.ACTION_CANCEL:                if (mIsBeingDragged) {                    scrollToItem(mCurItem, true, 0, false);                    needsInvalidate = resetTouch();                }                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) {            ViewCompat.postInvalidateOnAnimation(this);        }        return true;    }    private boolean resetTouch() {        boolean needsInvalidate;        mActivePointerId = INVALID_POINTER;        endDrag();        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();        return needsInvalidate;    }    private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {        final ViewParent parent = getParent();        if (parent != null) {            parent.requestDisallowInterceptTouchEvent(disallowIntercept);        }    }    private boolean performDrag(float x) {        boolean needsInvalidate = false;        final float deltaX = mLastMotionX - x;        mLastMotionX = x;        float oldScrollX = getScrollX();        float scrollX = oldScrollX + deltaX;        final int width = getClientWidth();        float leftBound = width * mFirstOffset;        float rightBound = width * mLastOffset;        boolean leftAbsolute = true;        boolean rightAbsolute = true;        final ItemInfo firstItem = mItems.get(0);        final ItemInfo lastItem = mItems.get(mItems.size() - 1);        if (firstItem.position != 0) {            leftAbsolute = false;            leftBound = firstItem.offset * width;        }        if (lastItem.position != mAdapter.getCount() - 1) {            rightAbsolute = false;            rightBound = lastItem.offset * width;        }        if (scrollX < leftBound) {            if (leftAbsolute) {                float over = leftBound - scrollX;                needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);            }            scrollX = leftBound;        } else if (scrollX > rightBound) {            if (rightAbsolute) {                float over = scrollX - rightBound;                needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);            }            scrollX = rightBound;        }        // Don't lose the rounded component        mLastMotionX += scrollX - (int) scrollX;        scrollTo((int) scrollX, getScrollY());        pageScrolled((int) scrollX);        return needsInvalidate;    }    /**     * @return Info about the page at the current scroll position.     *         This can be synthetic for a missing middle page; the 'object' field can be null.     */    private ItemInfo infoForCurrentScrollPosition() {        final int width = getClientWidth();        final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;        final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;        int lastPos = -1;        float lastOffset = 0.f;        float lastWidth = 0.f;        boolean first = true;        ItemInfo lastItem = null;        for (int i = 0; i < mItems.size(); i++) {            ItemInfo ii = mItems.get(i);            float offset;            if (!first && ii.position != lastPos + 1) {                // Create a synthetic item for a missing page.                ii = mTempItem;                ii.offset = lastOffset + lastWidth + marginOffset;                ii.position = lastPos + 1;                ii.widthFactor = mAdapter.getPageWidth(ii.position);                i--;            }            offset = ii.offset;            final float leftBound = offset;            final float rightBound = offset + ii.widthFactor + marginOffset;            if (first || scrollOffset >= leftBound) {                if (scrollOffset < rightBound || i == mItems.size() - 1) {                    return ii;                }            } else {                return lastItem;            }            first = false;            lastPos = ii.position;            lastOffset = offset;            lastWidth = ii.widthFactor;            lastItem = ii;        }        return lastItem;    }    private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {        int targetPage;        if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {            targetPage = velocity > 0 ? currentPage : currentPage + 1;        } else {            final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;            targetPage = (int) (currentPage + pageOffset + truncator);        }        if (mItems.size() > 0) {            final ItemInfo firstItem = mItems.get(0);            final ItemInfo lastItem = mItems.get(mItems.size() - 1);            // Only let the user target pages we have items for            targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));        }        return targetPage;    }    @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();                final int width = getWidth();                canvas.rotate(270);                canvas.translate(-height + getPaddingTop(), mFirstOffset * width);                mLeftEdge.setSize(height, width);                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();                canvas.rotate(90);                canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width);                mRightEdge.setSize(height, width);                needsInvalidate |= mRightEdge.draw(canvas);                canvas.restoreToCount(restoreCount);            }        } else {            mLeftEdge.finish();            mRightEdge.finish();        }        if (needsInvalidate) {            // Keep animating            ViewCompat.postInvalidateOnAnimation(this);        }    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // Draw the margin drawable between pages if needed.        if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {            final int scrollX = getScrollX();            final int width = getWidth();            final float marginOffset = (float) mPageMargin / width;            int itemIndex = 0;            ItemInfo ii = mItems.get(0);            float offset = ii.offset;            final int itemCount = mItems.size();            final int firstPos = ii.position;            final int lastPos = mItems.get(itemCount - 1).position;            for (int pos = firstPos; pos < lastPos; pos++) {                while (pos > ii.position && itemIndex < itemCount) {                    ii = mItems.get(++itemIndex);                }                float drawAt;                if (pos == ii.position) {                    drawAt = (ii.offset + ii.widthFactor) * width;                    offset = ii.offset + ii.widthFactor + marginOffset;                } else {                    float widthFactor = mAdapter.getPageWidth(pos);                    drawAt = (offset + widthFactor) * width;                    offset += widthFactor + marginOffset;                }                if (drawAt + mPageMargin > scrollX) {                    mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,                            (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);                    mMarginDrawable.draw(canvas);                }                if (drawAt > scrollX + width) {                    break; // No more visible, no sense in continuing                }            }        }    }    /**     * 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.");        }        if (mAdapter != null) {            final VelocityTracker velocityTracker = mVelocityTracker;            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);            int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(                    velocityTracker, mActivePointerId);            mPopulatePending = true;            final int width = getClientWidth();            final int scrollX = getScrollX();            final ItemInfo ii = infoForCurrentScrollPosition();            final int currentPage = ii.position;            final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;            final int totalDelta = (int) (mLastMotionX - mInitialMotionX);            int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,                    totalDelta);            setCurrentItemInternal(nextPage, true, true, initialVelocity);        }        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.");        }        if (mAdapter == null) {            return;        }        mLastMotionX += xOffset;        float oldScrollX = getScrollX();        float scrollX = oldScrollX - xOffset;        final int width = getClientWidth();        float leftBound = width * mFirstOffset;        float rightBound = width * mLastOffset;        final ItemInfo firstItem = mItems.get(0);        final ItemInfo lastItem = mItems.get(mItems.size() - 1);        if (firstItem.position != 0) {            leftBound = firstItem.offset * width;        }        if (lastItem.position != mAdapter.getCount() - 1) {            rightBound = lastItem.offset * width;        }        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());        pageScrolled((int) scrollX);        // 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);                    }                }            }        }    }    public boolean canScrollHorizontally(int direction) {        if (mAdapter == null) {            return false;        }        final int width = getClientWidth();        final int scrollX = getScrollX();        if (direction < 0) {            return (scrollX > (int) (width * mFirstOffset));        } else if (direction > 0) {            return (scrollX < (int) (width * mLastOffset));        } else {            return false;        }    }    /**     * 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 (Build.VERSION.SDK_INT >= 11) {                        // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD                        // before Android 3.0. Ignore the tab key on those devices.                        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;        } else if (currentFocused != null) {            boolean isChild = false;            for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;                    parent = parent.getParent()) {                if (parent == this) {                    isChild = true;                    break;                }            }            if (!isChild) {                // This would cause the focus search down below to fail in fun ways.                final StringBuilder sb = new StringBuilder();                sb.append(currentFocused.getClass().getSimpleName());                for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;                        parent = parent.getParent()) {                    sb.append(" => ").append(parent.getClass().getSimpleName());                }                Log.e(TAG, "arrowScroll tried to find focus based on non-child " +                        "current focused view " + sb.toString());                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.                final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;                final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;                if (currentFocused != null && nextLeft >= currLeft) {                    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.                final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;                final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;                if (currentFocused != null && nextLeft <= currLeft) {                    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;    }    private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {        if (outRect == null) {            outRect = new Rect();        }        if (child == null) {            outRect.set(0, 0, 0, 0);            return outRect;        }        outRect.left = child.getLeft();        outRect.right = child.getRight();        outRect.top = child.getTop();        outRect.bottom = child.getBottom();        ViewParent parent = child.getParent();        while (parent instanceof ViewGroup && parent != this) {            final ViewGroup group = (ViewGroup) parent;            outRect.left += group.getLeft();            outRect.right += group.getRight();            outRect.top += group.getTop();            outRect.bottom += group.getBottom();            parent = group.getParent();        }        return outRect;    }    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) {        // Dispatch scroll events from this ViewPager.        if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {            return super.dispatchPopulateAccessibilityEvent(event);        }        // Dispatch all other accessibility events from the current page.        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;    }    @Override    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {        return new LayoutParams();    }    @Override    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {        return generateDefaultLayoutParams();    }    @Override    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {        return p instanceof LayoutParams && super.checkLayoutParams(p);    }    @Override    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {        return new LayoutParams(getContext(), attrs);    }    class MyAccessibilityDelegate extends AccessibilityDelegateCompat {        @Override        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {            super.onInitializeAccessibilityEvent(host, event);            event.setClassName(ViewPager.class.getName());            final AccessibilityRecordCompat recordCompat =                    AccessibilityEventCompat.asRecord(event);            recordCompat.setScrollable(canScroll());            if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED                    && mAdapter != null) {                recordCompat.setItemCount(mAdapter.getCount());                recordCompat.setFromIndex(mCurItem);                recordCompat.setToIndex(mCurItem);            }        }        @Override        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {            super.onInitializeAccessibilityNodeInfo(host, info);            info.setClassName(ViewPager.class.getName());            info.setScrollable(canScroll());            if (canScrollHorizontally(1)) {                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);            }            if (canScrollHorizontally(-1)) {                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);            }        }        @Override        public boolean performAccessibilityAction(View host, int action, Bundle args) {            if (super.performAccessibilityAction(host, action, args)) {                return true;            }            switch (action) {                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {                    if (canScrollHorizontally(1)) {                        setCurrentItem(mCurItem + 1);                        return true;                    }                } return false;                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {                    if (canScrollHorizontally(-1)) {                        setCurrentItem(mCurItem - 1);                        return true;                    }                } return false;            }            return false;        }        private boolean canScroll() {            return (mAdapter != null) && (mAdapter.getCount() > 1);        }    }    private class PagerObserver extends DataSetObserver {        @Override        public void onChanged() {            dataSetChanged();        }        @Override        public void onInvalidated() {            dataSetChanged();        }    }    /**     * Layout parameters that should be supplied for views added to a     * ViewPager.     */    public static class LayoutParams extends ViewGroup.LayoutParams {        /**         * true if this view is a decoration on the pager itself and not         * a view supplied by the adapter.         */        public boolean isDecor;        /**         * Gravity setting for use on decor views only:         * Where to position the view page within the overall ViewPager         * container; constants are defined in {@link android.view.Gravity}.         */        public int gravity;        /**         * Width as a 0-1 multiplier of the measured pager width         */        float widthFactor = 0.f;        /**         * true if this view was added during layout and needs to be measured         * before being positioned.         */        boolean needsMeasure;        /**         * Adapter position this view is for if !isDecor         */        int position;        /**         * Current child index within the ViewPager that this view occupies         */        int childIndex;        public LayoutParams() {            super(FILL_PARENT, FILL_PARENT);        }        public LayoutParams(Context context, AttributeSet attrs) {            super(context, attrs);            final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);            gravity = a.getInteger(0, Gravity.TOP);            a.recycle();        }    }    static class ViewPositionComparator implements Comparator<View> {        @Override        public int compare(View lhs, View rhs) {            final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();            final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();            if (llp.isDecor != rlp.isDecor) {                return llp.isDecor ? 1 : -1;            }            return llp.position - rlp.position;        }    }}

呃……这么长,贴出来唬人的。具体看setCurrentItem方法里的这一段:

   final boolean dispatchSelected = mCurItem != item;        if (mFirstLayout) {            // We don't have any idea how big we are yet and shouldn't have any pages either.            // Just set things up and let the pending layout handle things.            mCurItem = item;            if (dispatchSelected) {                dispatchOnPageSelected(item);            }            requestLayout();        } else {            populate(item);            scrollToItem(item, smoothScroll, velocity, dispatchSelected);        }

  这里的item就是在setCurrentItem(item)里面传进来的要跳转到第几页的,然后用这个item与mCurItem 比较,mCurItem 表示当前选中页item,在给ViewPager 设置适配器的时候初始化为0.

    private int mCurItem;   // Index of currently displayed page.
   public void setAdapter(PagerAdapter adapter) {        if (mAdapter != null) {            mAdapter.setViewPagerObserver(null);            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();            removeNonDecorViews();            mCurItem = 0;//这里初始化为0            scrollTo(0, 0);        }

  如果我传进来的item也是0,则与mCurItem 相等,dispatchSelected 为false,就不会走 dispatchOnPageSelected(item);这个方法,不做任何操作。所以我在初始化的时候设置 vp_content.setCurrentItem(0)就没有任何效果。所以我就先setCurrentItem(2);再 setCurrentItem(0);
  暂时不知道有没有更好的解决方法。
 
  
  

0 0
原创粉丝点击