(转)关于禁止ViewPager预加载问题,完美解决!

来源:互联网 发布:最新挣钱软件 编辑:程序博客网 时间:2024/06/07 18:00

原地址:http://blog.csdn.net/qq_21898059/article/details/51453938


       我最近上班又遇到一个小难题了,就是如题所述:ViewPager预加载的问题。相信用过ViewPager的人大抵都有遇到过这种情况,网上的解决办法也就那么几个,终于在我自己不断试验之下,完美解决了(禁止了)ViewPager的预加载。

好了,首先来说明一下,什么是ViewPager的预加载:ViewPager有一个 “预加载”的机制,默认会把ViewPager当前位置的左右相邻页面预先初始化(俗称的预加载),它的默认值是 1,这样做的好处就是ViewPager左右滑动会更加流畅。

可是我的情况很特殊,因为我 5 个Fragment里有一个Fragment是有SurfaceView的,这样造成的问题就是,我ViewPager滑动到其相邻页面时,含有SurfaceView的页面就会被预先初始化,然后SurfaceView就开始预览了,只是我们看不到而已。同样的,当我们从含有SurfaceView的页面滑倒其相邻的页面时,SurfaceView并不会回调其surfaceDestory方法。于是这给我造成了极大的困扰。

ok,下面言归正传,到底该怎么禁止ViewPager的这个预加载问题呢?

方案1:网上大多数说法是 懒加载,即让ViewPager预加载初始化UI,而具体一些数据,网络访问请求等延迟加载。这是靠Fragment里有一个setUserVisibleHint(boolean isVisibleToUser)的方法,我们可以在这个方法里做判断,当其True可见时(即切换到某一个具体Fragment)时才去加载数据,这样可以省流量。但这里并不满足我的需求,因为某一个Fragment并不会在ViewPager滑动到其相邻的Fragment时销毁。这个只可以解决部分人问题。

首先我们来深入了解下ViewPager的预加载机制:

上文提到过,ViewPager默认预加载的数量是1,这一点我们可以在ViewPager源码里看到。


DEFAULT_OFFSCREEN_PAGES 这里就定义了默认值是1, 所以网上 有种解决方案 说调用ViewPager的setOffscreenPageLimit(int limit),来设置ViewPager预加载的数量,但是这里很明确的告诉你,这种方案是不可行的,如下图ViewPager源码:


我们可以看到,如果你调用该方法传进来的值小于1是无效的,会被强行的拽回1。而且DEFAULT_OFFSCREEN_PAGES 这个值是private的,子类继承ViewPager也是不可见的。

方案2:然后网上有第二种说法,自定义一个ViewPager,把原生ViewPager全拷过来,修改这个DEFAULT_OFFSCREEN_PAGES 值为0。对,就是这种解决方案!!但是!!

但是!!!但是什么呢?但是我试过,没用。为什么呢?接下来就是本文的重点了。

因为现在Android都6.0了,版本都老高了,其实android虽然每个版本都有v4包,但是这些v4包是有差异的。这就造成高版本v4包里的ViewPager,即使你Copy它,将其DEFAULT_OFFSCREEN_PAGES的值改为0,还是不起作用的,其中的逻辑变了。具体哪里变了导致无效我也没有继续研究了,毕竟公司不会花时间让我研究这些啊。偷笑

完美解决方案:ok,所以关于禁止ViewPager预加载的完美解决方案就是,使用低版本v4包里的ViewPager,完全copy一份,将其中的DEFAULT_OFFSCREEN_PAGES值改为0即可。博主亲测 API 14 即 Android 4.0的v4包里ViewPager 有效。

当然,谷歌既然有这么一种ViewPager的机制肯定有它的道理,所以一般还是预加载的好。

最后,因为低版本的源码越来越少的人会去下载,这里直接把这个禁止了预加载的ViewPager贴上来,需要的人就拿去吧。copy就能用了。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.plumcot.usb.view;  
  2.   
  3. /* 
  4.  * Copyright (C) 2011 The Android Open Source Project 
  5.  * 
  6.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  7.  * you may not use this file except in compliance with the License. 
  8.  * You may obtain a copy of the License at 
  9.  * 
  10.  *      http://www.apache.org/licenses/LICENSE-2.0 
  11.  * 
  12.  * Unless required by applicable law or agreed to in writing, software 
  13.  * distributed under the License is distributed on an "AS IS" BASIS, 
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  15.  * See the License for the specific language governing permissions and 
  16.  * limitations under the License. 
  17.  */  
  18.   
  19.   
  20. import android.content.Context;  
  21. import android.database.DataSetObserver;  
  22. import android.graphics.Canvas;  
  23. import android.graphics.Rect;  
  24. import android.graphics.drawable.Drawable;  
  25. import android.os.Parcel;  
  26. import android.os.Parcelable;  
  27. import android.os.SystemClock;  
  28. import android.support.v4.os.ParcelableCompat;  
  29. import android.support.v4.os.ParcelableCompatCreatorCallbacks;  
  30. import android.support.v4.view.KeyEventCompat;  
  31. import android.support.v4.view.MotionEventCompat;  
  32. import android.support.v4.view.PagerAdapter;  
  33. import android.support.v4.view.VelocityTrackerCompat;  
  34. import android.support.v4.view.ViewCompat;  
  35. import android.support.v4.view.ViewConfigurationCompat;  
  36. import android.support.v4.widget.EdgeEffectCompat;  
  37. import android.util.AttributeSet;  
  38. import android.util.Log;  
  39. import android.view.FocusFinder;  
  40. import android.view.KeyEvent;  
  41. import android.view.MotionEvent;  
  42. import android.view.SoundEffectConstants;  
  43. import android.view.VelocityTracker;  
  44. import android.view.View;  
  45. import android.view.ViewConfiguration;  
  46. import android.view.ViewGroup;  
  47. import android.view.ViewParent;  
  48. import android.view.accessibility.AccessibilityEvent;  
  49. import android.view.animation.Interpolator;  
  50. import android.widget.Scroller;  
  51.   
  52. import java.util.ArrayList;  
  53. import java.util.Collections;  
  54. import java.util.Comparator;  
  55.   
  56. /** 
  57.  * Layout manager that allows the user to flip left and right 
  58.  * through pages of data.  You supply an implementation of a 
  59.  * {@link android.support.v4.view.PagerAdapter} to generate the pages that the view shows. 
  60.  * 
  61.  * <p>Note this class is currently under early design and 
  62.  * development.  The API will likely change in later updates of 
  63.  * the compatibility library, requiring changes to the source code 
  64.  * of apps when they are compiled against the newer version.</p> 
  65.  */  
  66. public class NoPreloadViewPager extends ViewGroup {  
  67.     private static final String TAG = "<span style="font-family:Arial, Helvetica, sans-serif;">NoPreLoadViewPager</span>";  
  68.     private static final boolean DEBUG = false;  
  69.   
  70.     private static final boolean USE_CACHE = false;  
  71.   
  72.     private static final int DEFAULT_OFFSCREEN_PAGES = 0;//默认是1  
  73.     private static final int MAX_SETTLE_DURATION = 600// ms  
  74.   
  75.     static class ItemInfo {  
  76.         Object object;  
  77.         int position;  
  78.         boolean scrolling;  
  79.     }  
  80.   
  81.     private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>(){  
  82.         @Override  
  83.         public int compare(ItemInfo lhs, ItemInfo rhs) {  
  84.             return lhs.position - rhs.position;  
  85.         }};  
  86.   
  87.     private static final Interpolator sInterpolator = new Interpolator() {  
  88.         public float getInterpolation(float t) {  
  89.             // _o(t) = t * t * ((tension + 1) * t + tension)  
  90.             // o(t) = _o(t - 1) + 1  
  91.             t -= 1.0f;  
  92.             return t * t * t + 1.0f;  
  93.         }  
  94.     };  
  95.   
  96.     private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();  
  97.   
  98.     private PagerAdapter mAdapter;  
  99.     private int mCurItem;   // Index of currently displayed page.  
  100.     private int mRestoredCurItem = -1;  
  101.     private Parcelable mRestoredAdapterState = null;  
  102.     private ClassLoader mRestoredClassLoader = null;  
  103.     private Scroller mScroller;  
  104.     private PagerObserver mObserver;  
  105.   
  106.     private int mPageMargin;  
  107.     private Drawable mMarginDrawable;  
  108.   
  109.     private int mChildWidthMeasureSpec;  
  110.     private int mChildHeightMeasureSpec;  
  111.     private boolean mInLayout;  
  112.   
  113.     private boolean mScrollingCacheEnabled;  
  114.   
  115.     private boolean mPopulatePending;  
  116.     private boolean mScrolling;  
  117.     private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;  
  118.   
  119.     private boolean mIsBeingDragged;  
  120.     private boolean mIsUnableToDrag;  
  121.     private int mTouchSlop;  
  122.     private float mInitialMotionX;  
  123.     /** 
  124.      * Position of the last motion event. 
  125.      */  
  126.     private float mLastMotionX;  
  127.     private float mLastMotionY;  
  128.     /** 
  129.      * ID of the active pointer. This is used to retain consistency during 
  130.      * drags/flings if multiple pointers are used. 
  131.      */  
  132.     private int mActivePointerId = INVALID_POINTER;  
  133.     /** 
  134.      * Sentinel value for no current active pointer. 
  135.      * Used by {@link #mActivePointerId}. 
  136.      */  
  137.     private static final int INVALID_POINTER = -1;  
  138.   
  139.     /** 
  140.      * Determines speed during touch scrolling 
  141.      */  
  142.     private VelocityTracker mVelocityTracker;  
  143.     private int mMinimumVelocity;  
  144.     private int mMaximumVelocity;  
  145.     private float mBaseLineFlingVelocity;  
  146.     private float mFlingVelocityInfluence;  
  147.   
  148.     private boolean mFakeDragging;  
  149.     private long mFakeDragBeginTime;  
  150.   
  151.     private EdgeEffectCompat mLeftEdge;  
  152.     private EdgeEffectCompat mRightEdge;  
  153.   
  154.     private boolean mFirstLayout = true;  
  155.   
  156.     private OnPageChangeListener mOnPageChangeListener;  
  157.   
  158.     /** 
  159.      * Indicates that the pager is in an idle, settled state. The current page 
  160.      * is fully in view and no animation is in progress. 
  161.      */  
  162.     public static final int SCROLL_STATE_IDLE = 0;  
  163.   
  164.     /** 
  165.      * Indicates that the pager is currently being dragged by the user. 
  166.      */  
  167.     public static final int SCROLL_STATE_DRAGGING = 1;  
  168.   
  169.     /** 
  170.      * Indicates that the pager is in the process of settling to a final position. 
  171.      */  
  172.     public static final int SCROLL_STATE_SETTLING = 2;  
  173.   
  174.     private int mScrollState = SCROLL_STATE_IDLE;  
  175.   
  176.     /** 
  177.      * Callback interface for responding to changing state of the selected page. 
  178.      */  
  179.     public interface OnPageChangeListener {  
  180.   
  181.         /** 
  182.          * This method will be invoked when the current page is scrolled, either as part 
  183.          * of a programmatically initiated smooth scroll or a user initiated touch scroll. 
  184.          * 
  185.          * @param position Position index of the first page currently being displayed. 
  186.          *                 Page position+1 will be visible if positionOffset is nonzero. 
  187.          * @param positionOffset Value from [0, 1) indicating the offset from the page at position. 
  188.          * @param positionOffsetPixels Value in pixels indicating the offset from position. 
  189.          */  
  190.         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);  
  191.   
  192.         /** 
  193.          * This method will be invoked when a new page becomes selected. Animation is not 
  194.          * necessarily complete. 
  195.          * 
  196.          * @param position Position index of the new selected page. 
  197.          */  
  198.         public void onPageSelected(int position);  
  199.   
  200.         /** 
  201.          * Called when the scroll state changes. Useful for discovering when the user 
  202.          * begins dragging, when the pager is automatically settling to the current page, 
  203.          * or when it is fully stopped/idle. 
  204.          * 
  205.          * @param state The new scroll state. 
  206.          * @see android.support.v4.view.ViewPager#SCROLL_STATE_IDLE 
  207.          * @see android.support.v4.view.ViewPager#SCROLL_STATE_DRAGGING 
  208.          * @see android.support.v4.view.ViewPager#SCROLL_STATE_SETTLING 
  209.          */  
  210.         public void onPageScrollStateChanged(int state);  
  211.     }  
  212.   
  213.     /** 
  214.      * Simple implementation of the {@link android.support.v4.view.LazyViewPager.OnPageChangeListener} interface with stub 
  215.      * implementations of each method. Extend this if you do not intend to override 
  216.      * every method of {@link android.support.v4.view.LazyViewPager.OnPageChangeListener}. 
  217.      */  
  218.     public static class SimpleOnPageChangeListener implements OnPageChangeListener {  
  219.         @Override  
  220.         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  221.             // This space for rent  
  222.         }  
  223.   
  224.         @Override  
  225.         public void onPageSelected(int position) {  
  226.             // This space for rent  
  227.         }  
  228.   
  229.         @Override  
  230.         public void onPageScrollStateChanged(int state) {  
  231.             // This space for rent  
  232.         }  
  233.     }  
  234.   
  235.     public NoPreloadViewPager(Context context) {  
  236.         super(context);  
  237.         initViewPager();  
  238.     }  
  239.   
  240.     public NoPreloadViewPager(Context context, AttributeSet attrs) {  
  241.         super(context, attrs);  
  242.         initViewPager();  
  243.     }  
  244.   
  245.     void initViewPager() {  
  246.         setWillNotDraw(false);  
  247.         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);  
  248.         setFocusable(true);  
  249.         final Context context = getContext();  
  250.         mScroller = new Scroller(context, sInterpolator);  
  251.         final ViewConfiguration configuration = ViewConfiguration.get(context);  
  252.         mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);  
  253.         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();  
  254.         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();  
  255.         mLeftEdge = new EdgeEffectCompat(context);  
  256.         mRightEdge = new EdgeEffectCompat(context);  
  257.   
  258.         float density = context.getResources().getDisplayMetrics().density;  
  259.         mBaseLineFlingVelocity = 2500.0f * density;  
  260.         mFlingVelocityInfluence = 0.4f;  
  261.     }  
  262.   
  263.     private void setScrollState(int newState) {  
  264.         if (mScrollState == newState) {  
  265.             return;  
  266.         }  
  267.   
  268.         mScrollState = newState;  
  269.         if (mOnPageChangeListener != null) {  
  270.             mOnPageChangeListener.onPageScrollStateChanged(newState);  
  271.         }  
  272.     }  
  273.   
  274.     public void setAdapter(PagerAdapter adapter) {  
  275.         if (mAdapter != null) {  
  276. //            mAdapter.unregisterDataSetObserver(mObserver);  
  277.             mAdapter.startUpdate(this);  
  278.             for (int i = 0; i < mItems.size(); i++) {  
  279.                 final ItemInfo ii = mItems.get(i);  
  280.                 mAdapter.destroyItem(this, ii.position, ii.object);  
  281.             }  
  282.             mAdapter.finishUpdate(this);  
  283.             mItems.clear();  
  284.             removeAllViews();  
  285.             mCurItem = 0;  
  286.             scrollTo(00);  
  287.         }  
  288.   
  289.         mAdapter = adapter;  
  290.   
  291.         if (mAdapter != null) {  
  292.             if (mObserver == null) {  
  293.                 mObserver = new PagerObserver();  
  294.             }  
  295. //            mAdapter.registerDataSetObserver(mObserver);  
  296.             mPopulatePending = false;  
  297.             if (mRestoredCurItem >= 0) {  
  298.                 mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);  
  299.                 setCurrentItemInternal(mRestoredCurItem, falsetrue);  
  300.                 mRestoredCurItem = -1;  
  301.                 mRestoredAdapterState = null;  
  302.                 mRestoredClassLoader = null;  
  303.             } else {  
  304.                 populate();  
  305.             }  
  306.         }  
  307.     }  
  308.   
  309.     public PagerAdapter getAdapter() {  
  310.         return mAdapter;  
  311.     }  
  312.   
  313.     /** 
  314.      * Set the currently selected page. If the ViewPager has already been through its first 
  315.      * layout there will be a smooth animated transition between the current item and the 
  316.      * specified item. 
  317.      * 
  318.      * @param item Item index to select 
  319.      */  
  320.     public void setCurrentItem(int item) {  
  321.         mPopulatePending = false;  
  322.         setCurrentItemInternal(item, !mFirstLayout, false);  
  323.     }  
  324.   
  325.     /** 
  326.      * Set the currently selected page. 
  327.      * 
  328.      * @param item Item index to select 
  329.      * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately 
  330.      */  
  331.     public void setCurrentItem(int item, boolean smoothScroll) {  
  332.         mPopulatePending = false;  
  333.         setCurrentItemInternal(item, smoothScroll, false);  
  334.     }  
  335.   
  336.     public int getCurrentItem() {  
  337.         return mCurItem;  
  338.     }  
  339.   
  340.     void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {  
  341.         setCurrentItemInternal(item, smoothScroll, always, 0);  
  342.     }  
  343.   
  344.     void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {  
  345.         if (mAdapter == null || mAdapter.getCount() <= 0) {  
  346.             setScrollingCacheEnabled(false);  
  347.             return;  
  348.         }  
  349.         if (!always && mCurItem == item && mItems.size() != 0) {  
  350.             setScrollingCacheEnabled(false);  
  351.             return;  
  352.         }  
  353.         if (item < 0) {  
  354.             item = 0;  
  355.         } else if (item >= mAdapter.getCount()) {  
  356.             item = mAdapter.getCount() - 1;  
  357.         }  
  358.         final int pageLimit = mOffscreenPageLimit;  
  359.         if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {  
  360.             // We are doing a jump by more than one page.  To avoid  
  361.             // glitches, we want to keep all current pages in the view  
  362.             // until the scroll ends.  
  363.             for (int i=0; i<mItems.size(); i++) {  
  364.                 mItems.get(i).scrolling = true;  
  365.             }  
  366.         }  
  367.   
  368.         final boolean dispatchSelected = mCurItem != item;  
  369.         mCurItem = item;  
  370.         populate();  
  371.         final int destX = (getWidth() + mPageMargin) * item;  
  372.         if (smoothScroll) {  
  373.             smoothScrollTo(destX, 0, velocity);  
  374.             if (dispatchSelected && mOnPageChangeListener != null) {  
  375.                 mOnPageChangeListener.onPageSelected(item);  
  376.             }  
  377.         } else {  
  378.             if (dispatchSelected && mOnPageChangeListener != null) {  
  379.                 mOnPageChangeListener.onPageSelected(item);  
  380.             }  
  381.             completeScroll();  
  382.             scrollTo(destX, 0);  
  383.         }  
  384.     }  
  385.   
  386.     public void setOnPageChangeListener(OnPageChangeListener listener) {  
  387.         mOnPageChangeListener = listener;  
  388.     }  
  389.   
  390.     /** 
  391.      * Returns the number of pages that will be retained to either side of the 
  392.      * current page in the view hierarchy in an idle state. Defaults to 1. 
  393.      * 
  394.      * @return How many pages will be kept offscreen on either side 
  395.      * @see #setOffscreenPageLimit(int) 
  396.      */  
  397.     public int getOffscreenPageLimit() {  
  398.         return mOffscreenPageLimit;  
  399.     }  
  400.   
  401.     /** 
  402.      * Set the number of pages that should be retained to either side of the 
  403.      * current page in the view hierarchy in an idle state. Pages beyond this 
  404.      * limit will be recreated from the adapter when needed. 
  405.      * 
  406.      * <p>This is offered as an optimization. If you know in advance the number 
  407.      * of pages you will need to support or have lazy-loading mechanisms in place 
  408.      * on your pages, tweaking this setting can have benefits in perceived smoothness 
  409.      * of paging animations and interaction. If you have a small number of pages (3-4) 
  410.      * that you can keep active all at once, less time will be spent in layout for 
  411.      * newly created view subtrees as the user pages back and forth.</p> 
  412.      * 
  413.      * <p>You should keep this limit low, especially if your pages have complex layouts. 
  414.      * This setting defaults to 1.</p> 
  415.      * 
  416.      * @param limit How many pages will be kept offscreen in an idle state. 
  417.      */  
  418.     public void setOffscreenPageLimit(int limit) {  
  419.         if (limit < DEFAULT_OFFSCREEN_PAGES) {  
  420.             Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +  
  421.                     DEFAULT_OFFSCREEN_PAGES);  
  422.             limit = DEFAULT_OFFSCREEN_PAGES;  
  423.         }  
  424.         if (limit != mOffscreenPageLimit) {  
  425.             mOffscreenPageLimit = limit;  
  426.             populate();  
  427.         }  
  428.     }  
  429.   
  430.     /** 
  431.      * Set the margin between pages. 
  432.      * 
  433.      * @param marginPixels Distance between adjacent pages in pixels 
  434.      * @see #getPageMargin() 
  435.      * @see #setPageMarginDrawable(android.graphics.drawable.Drawable) 
  436.      * @see #setPageMarginDrawable(int) 
  437.      */  
  438.     public void setPageMargin(int marginPixels) {  
  439.         final int oldMargin = mPageMargin;  
  440.         mPageMargin = marginPixels;  
  441.   
  442.         final int width = getWidth();  
  443.         recomputeScrollPosition(width, width, marginPixels, oldMargin);  
  444.   
  445.         requestLayout();  
  446.     }  
  447.   
  448.     /** 
  449.      * Return the margin between pages. 
  450.      * 
  451.      * @return The size of the margin in pixels 
  452.      */  
  453.     public int getPageMargin() {  
  454.         return mPageMargin;  
  455.     }  
  456.   
  457.     /** 
  458.      * Set a drawable that will be used to fill the margin between pages. 
  459.      * 
  460.      * @param d Drawable to display between pages 
  461.      */  
  462.     public void setPageMarginDrawable(Drawable d) {  
  463.         mMarginDrawable = d;  
  464.         if (d != null) refreshDrawableState();  
  465.         setWillNotDraw(d == null);  
  466.         invalidate();  
  467.     }  
  468.   
  469.     /** 
  470.      * Set a drawable that will be used to fill the margin between pages. 
  471.      * 
  472.      * @param resId Resource ID of a drawable to display between pages 
  473.      */  
  474.     public void setPageMarginDrawable(int resId) {  
  475.         setPageMarginDrawable(getContext().getResources().getDrawable(resId));  
  476.     }  
  477.   
  478.     @Override  
  479.     protected boolean verifyDrawable(Drawable who) {  
  480.         return super.verifyDrawable(who) || who == mMarginDrawable;  
  481.     }  
  482.   
  483.     @Override  
  484.     protected void drawableStateChanged() {  
  485.         super.drawableStateChanged();  
  486.         final Drawable d = mMarginDrawable;  
  487.         if (d != null && d.isStateful()) {  
  488.             d.setState(getDrawableState());  
  489.         }  
  490.     }  
  491.   
  492.     // We want the duration of the page snap animation to be influenced by the distance that  
  493.     // the screen has to travel, however, we don't want this duration to be effected in a  
  494.     // purely linear fashion. Instead, we use this method to moderate the effect that the distance  
  495.     // of travel has on the overall snap duration.  
  496.     float distanceInfluenceForSnapDuration(float f) {  
  497.         f -= 0.5f; // center the values about 0.  
  498.         f *= 0.3f * Math.PI / 2.0f;  
  499.         return (float) Math.sin(f);  
  500.     }  
  501.   
  502.     /** 
  503.      * Like {@link android.view.View#scrollBy}, but scroll smoothly instead of immediately. 
  504.      * 
  505.      * @param x the number of pixels to scroll by on the X axis 
  506.      * @param y the number of pixels to scroll by on the Y axis 
  507.      */  
  508.     void smoothScrollTo(int x, int y) {  
  509.         smoothScrollTo(x, y, 0);  
  510.     }  
  511.   
  512.     /** 
  513.      * Like {@link android.view.View#scrollBy}, but scroll smoothly instead of immediately. 
  514.      * 
  515.      * @param x the number of pixels to scroll by on the X axis 
  516.      * @param y the number of pixels to scroll by on the Y axis 
  517.      * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) 
  518.      */  
  519.     void smoothScrollTo(int x, int y, int velocity) {  
  520.         if (getChildCount() == 0) {  
  521.             // Nothing to do.  
  522.             setScrollingCacheEnabled(false);  
  523.             return;  
  524.         }  
  525.         int sx = getScrollX();  
  526.         int sy = getScrollY();  
  527.         int dx = x - sx;  
  528.         int dy = y - sy;  
  529.         if (dx == 0 && dy == 0) {  
  530.             completeScroll();  
  531.             setScrollState(SCROLL_STATE_IDLE);  
  532.             return;  
  533.         }  
  534.   
  535.         setScrollingCacheEnabled(true);  
  536.         mScrolling = true;  
  537.         setScrollState(SCROLL_STATE_SETTLING);  
  538.   
  539.         final float pageDelta = (float) Math.abs(dx) / (getWidth() + mPageMargin);  
  540.         int duration = (int) (pageDelta * 100);  
  541.   
  542.         velocity = Math.abs(velocity);  
  543.         if (velocity > 0) {  
  544.             duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence;  
  545.         } else {  
  546.             duration += 100;  
  547.         }  
  548.         duration = Math.min(duration, MAX_SETTLE_DURATION);  
  549.   
  550.         mScroller.startScroll(sx, sy, dx, dy, duration);  
  551.         invalidate();  
  552.     }  
  553.   
  554.     void addNewItem(int position, int index) {  
  555.         ItemInfo ii = new ItemInfo();  
  556.         ii.position = position;  
  557.         ii.object = mAdapter.instantiateItem(this, position);  
  558.         if (index < 0) {  
  559.             mItems.add(ii);  
  560.         } else {  
  561.             mItems.add(index, ii);  
  562.         }  
  563.     }  
  564.   
  565.     void dataSetChanged() {  
  566.         // This method only gets called if our observer is attached, so mAdapter is non-null.  
  567.   
  568.         boolean needPopulate = mItems.size() < 3 && mItems.size() < mAdapter.getCount();  
  569.         int newCurrItem = -1;  
  570.   
  571.         for (int i = 0; i < mItems.size(); i++) {  
  572.             final ItemInfo ii = mItems.get(i);  
  573.             final int newPos = mAdapter.getItemPosition(ii.object);  
  574.   
  575.             if (newPos == PagerAdapter.POSITION_UNCHANGED) {  
  576.                 continue;  
  577.             }  
  578.   
  579.             if (newPos == PagerAdapter.POSITION_NONE) {  
  580.                 mItems.remove(i);  
  581.                 i--;  
  582.                 mAdapter.destroyItem(this, ii.position, ii.object);  
  583.                 needPopulate = true;  
  584.   
  585.                 if (mCurItem == ii.position) {  
  586.                     // Keep the current item in the valid range  
  587.                     newCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1));  
  588.                 }  
  589.                 continue;  
  590.             }  
  591.   
  592.             if (ii.position != newPos) {  
  593.                 if (ii.position == mCurItem) {  
  594.                     // Our current item changed position. Follow it.  
  595.                     newCurrItem = newPos;  
  596.                 }  
  597.   
  598.                 ii.position = newPos;  
  599.                 needPopulate = true;  
  600.             }  
  601.         }  
  602.   
  603.         Collections.sort(mItems, COMPARATOR);  
  604.   
  605.         if (newCurrItem >= 0) {  
  606.             // TODO This currently causes a jump.  
  607.             setCurrentItemInternal(newCurrItem, falsetrue);  
  608.             needPopulate = true;  
  609.         }  
  610.         if (needPopulate) {  
  611.             populate();  
  612.             requestLayout();  
  613.         }  
  614.     }  
  615.   
  616.     void populate() {  
  617.         if (mAdapter == null) {  
  618.             return;  
  619.         }  
  620.   
  621.         // Bail now if we are waiting to populate.  This is to hold off  
  622.         // on creating views from the time the user releases their finger to  
  623.         // fling to a new position until we have finished the scroll to  
  624.         // that position, avoiding glitches from happening at that point.  
  625.         if (mPopulatePending) {  
  626.             if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");  
  627.             return;  
  628.         }  
  629.   
  630.         // Also, don't populate until we are attached to a window.  This is to  
  631.         // avoid trying to populate before we have restored our view hierarchy  
  632.         // state and conflicting with what is restored.  
  633.         if (getWindowToken() == null) {  
  634.             return;  
  635.         }  
  636.   
  637.         mAdapter.startUpdate(this);  
  638.   
  639.         final int pageLimit = mOffscreenPageLimit;  
  640.         final int startPos = Math.max(0, mCurItem - pageLimit);  
  641.         final int N = mAdapter.getCount();  
  642.         final int endPos = Math.min(N-1, mCurItem + pageLimit);  
  643.   
  644.         if (DEBUG) Log.v(TAG, "populating: startPos=" + startPos + " endPos=" + endPos);  
  645.   
  646.         // Add and remove pages in the existing list.  
  647.         int lastPos = -1;  
  648.         for (int i=0; i<mItems.size(); i++) {  
  649.             ItemInfo ii = mItems.get(i);  
  650.             if ((ii.position < startPos || ii.position > endPos) && !ii.scrolling) {  
  651.                 if (DEBUG) Log.i(TAG, "removing: " + ii.position + " @ " + i);  
  652.                 mItems.remove(i);  
  653.                 i--;  
  654.                 mAdapter.destroyItem(this, ii.position, ii.object);  
  655.             } else if (lastPos < endPos && ii.position > startPos) {  
  656.                 // The next item is outside of our range, but we have a gap  
  657.                 // between it and the last item where we want to have a page  
  658.                 // shown.  Fill in the gap.  
  659.                 lastPos++;  
  660.                 if (lastPos < startPos) {  
  661.                     lastPos = startPos;  
  662.                 }  
  663.                 while (lastPos <= endPos && lastPos < ii.position) {  
  664.                     if (DEBUG) Log.i(TAG, "inserting: " + lastPos + " @ " + i);  
  665.                     addNewItem(lastPos, i);  
  666.                     lastPos++;  
  667.                     i++;  
  668.                 }  
  669.             }  
  670.             lastPos = ii.position;  
  671.         }  
  672.   
  673.         // Add any new pages we need at the end.  
  674.         lastPos = mItems.size() > 0 ? mItems.get(mItems.size()-1).position : -1;  
  675.         if (lastPos < endPos) {  
  676.             lastPos++;  
  677.             lastPos = lastPos > startPos ? lastPos : startPos;  
  678.             while (lastPos <= endPos) {  
  679.                 if (DEBUG) Log.i(TAG, "appending: " + lastPos);  
  680.                 addNewItem(lastPos, -1);  
  681.                 lastPos++;  
  682.             }  
  683.         }  
  684.   
  685.         if (DEBUG) {  
  686.             Log.i(TAG, "Current page list:");  
  687.             for (int i=0; i<mItems.size(); i++) {  
  688.                 Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);  
  689.             }  
  690.         }  
  691.   
  692.         ItemInfo curItem = null;  
  693.         for (int i=0; i<mItems.size(); i++) {  
  694.             if (mItems.get(i).position == mCurItem) {  
  695.                 curItem = mItems.get(i);  
  696.                 break;  
  697.             }  
  698.         }  
  699.         mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);  
  700.   
  701.         mAdapter.finishUpdate(this);  
  702.   
  703.         if (hasFocus()) {  
  704.             View currentFocused = findFocus();  
  705.             ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;  
  706.             if (ii == null || ii.position != mCurItem) {  
  707.                 for (int i=0; i<getChildCount(); i++) {  
  708.                     View child = getChildAt(i);  
  709.                     ii = infoForChild(child);  
  710.                     if (ii != null && ii.position == mCurItem) {  
  711.                         if (child.requestFocus(FOCUS_FORWARD)) {  
  712.                             break;  
  713.                         }  
  714.                     }  
  715.                 }  
  716.             }  
  717.         }  
  718.     }  
  719.   
  720.     public static class SavedState extends BaseSavedState {  
  721.         int position;  
  722.         Parcelable adapterState;  
  723.         ClassLoader loader;  
  724.   
  725.         public SavedState(Parcelable superState) {  
  726.             super(superState);  
  727.         }  
  728.   
  729.         @Override  
  730.         public void writeToParcel(Parcel out, int flags) {  
  731.             super.writeToParcel(out, flags);  
  732.             out.writeInt(position);  
  733.             out.writeParcelable(adapterState, flags);  
  734.         }  
  735.   
  736.         @Override  
  737.         public String toString() {  
  738.             return "FragmentPager.SavedState{"  
  739.                     + Integer.toHexString(System.identityHashCode(this))  
  740.                     + " position=" + position + "}";  
  741.         }  
  742.   
  743.         public static final Creator<SavedState> CREATOR  
  744.                 = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {  
  745.                     @Override  
  746.                     public SavedState createFromParcel(Parcel in, ClassLoader loader) {  
  747.                         return new SavedState(in, loader);  
  748.                     }  
  749.                     @Override  
  750.                     public SavedState[] newArray(int size) {  
  751.                         return new SavedState[size];  
  752.                     }  
  753.                 });  
  754.   
  755.         SavedState(Parcel in, ClassLoader loader) {  
  756.             super(in);  
  757.             if (loader == null) {  
  758.                 loader = getClass().getClassLoader();  
  759.             }  
  760.             position = in.readInt();  
  761.             adapterState = in.readParcelable(loader);  
  762.             this.loader = loader;  
  763.         }  
  764.     }  
  765.   
  766.     @Override  
  767.     public Parcelable onSaveInstanceState() {  
  768.         Parcelable superState = super.onSaveInstanceState();  
  769.         SavedState ss = new SavedState(superState);  
  770.         ss.position = mCurItem;  
  771.         if (mAdapter != null) {  
  772.             ss.adapterState = mAdapter.saveState();  
  773.         }  
  774.         return ss;  
  775.     }  
  776.   
  777.     @Override  
  778.     public void onRestoreInstanceState(Parcelable state) {  
  779.         if (!(state instanceof SavedState)) {  
  780.             super.onRestoreInstanceState(state);  
  781.             return;  
  782.         }  
  783.   
  784.         SavedState ss = (SavedState)state;  
  785.         super.onRestoreInstanceState(ss.getSuperState());  
  786.   
  787.         if (mAdapter != null) {  
  788.             mAdapter.restoreState(ss.adapterState, ss.loader);  
  789.             setCurrentItemInternal(ss.position, falsetrue);  
  790.         } else {  
  791.             mRestoredCurItem = ss.position;  
  792.             mRestoredAdapterState = ss.adapterState;  
  793.             mRestoredClassLoader = ss.loader;  
  794.         }  
  795.     }  
  796.   
  797.     @Override  
  798.     public void addView(View child, int index, LayoutParams params) {  
  799.         if (mInLayout) {  
  800.             addViewInLayout(child, index, params);  
  801.             child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);  
  802.         } else {  
  803.             super.addView(child, index, params);  
  804.         }  
  805.   
  806.         if (USE_CACHE) {  
  807.             if (child.getVisibility() != GONE) {  
  808.                 child.setDrawingCacheEnabled(mScrollingCacheEnabled);  
  809.             } else {  
  810.                 child.setDrawingCacheEnabled(false);  
  811.             }  
  812.         }  
  813.     }  
  814.   
  815.     ItemInfo infoForChild(View child) {  
  816.         for (int i=0; i<mItems.size(); i++) {  
  817.             ItemInfo ii = mItems.get(i);  
  818.             if (mAdapter.isViewFromObject(child, ii.object)) {  
  819.                 return ii;  
  820.             }  
  821.         }  
  822.         return null;  
  823.     }  
  824.   
  825.     ItemInfo infoForAnyChild(View child) {  
  826.         ViewParent parent;  
  827.         while ((parent=child.getParent()) != this) {  
  828.             if (parent == null || !(parent instanceof View)) {  
  829.                 return null;  
  830.             }  
  831.             child = (View)parent;  
  832.         }  
  833.         return infoForChild(child);  
  834.     }  
  835.   
  836.     @Override  
  837.     protected void onAttachedToWindow() {  
  838.         super.onAttachedToWindow();  
  839.         mFirstLayout = true;  
  840.     }  
  841.   
  842.     @Override  
  843.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  844.         // For simple implementation, or internal size is always 0.  
  845.         // We depend on the container to specify the layout size of  
  846.         // our view.  We can't really know what it is since we will be  
  847.         // adding and removing different arbitrary views and do not  
  848.         // want the layout to change as this happens.  
  849.         setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),  
  850.                 getDefaultSize(0, heightMeasureSpec));  
  851.   
  852.         // Children are just made to fill our space.  
  853.         mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -  
  854.                 getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY);  
  855.         mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -  
  856.                 getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);  
  857.   
  858.         // Make sure we have created all fragments that we need to have shown.  
  859.         mInLayout = true;  
  860.         populate();  
  861.         mInLayout = false;  
  862.   
  863.         // Make sure all children have been properly measured.  
  864.         final int size = getChildCount();  
  865.         for (int i = 0; i < size; ++i) {  
  866.             final View child = getChildAt(i);  
  867.             if (child.getVisibility() != GONE) {  
  868.                 if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child  
  869.                 + ": " + mChildWidthMeasureSpec);  
  870.                 child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);  
  871.             }  
  872.         }  
  873.     }  
  874.   
  875.     @Override  
  876.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  877.         super.onSizeChanged(w, h, oldw, oldh);  
  878.   
  879.         // Make sure scroll position is set correctly.  
  880.         if (w != oldw) {  
  881.             recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);  
  882.         }  
  883.     }  
  884.   
  885.     private void recomputeScrollPosition(int width, int oldWidth, int margin, int oldMargin) {  
  886.         final int widthWithMargin = width + margin;  
  887.         if (oldWidth > 0) {  
  888.             final int oldScrollPos = getScrollX();  
  889.             final int oldwwm = oldWidth + oldMargin;  
  890.             final int oldScrollItem = oldScrollPos / oldwwm;  
  891.             final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm;  
  892.             final int scrollPos = (int) ((oldScrollItem + scrollOffset) * widthWithMargin);  
  893.             scrollTo(scrollPos, getScrollY());  
  894.             if (!mScroller.isFinished()) {  
  895.                 // We now return to your regularly scheduled scroll, already in progress.  
  896.                 final int newDuration = mScroller.getDuration() - mScroller.timePassed();  
  897.                 mScroller.startScroll(scrollPos, 0, mCurItem * widthWithMargin, 0, newDuration);  
  898.             }  
  899.         } else {  
  900.             int scrollPos = mCurItem * widthWithMargin;  
  901.             if (scrollPos != getScrollX()) {  
  902.                 completeScroll();  
  903.                 scrollTo(scrollPos, getScrollY());  
  904.             }  
  905.         }  
  906.     }  
  907.   
  908.     @Override  
  909.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  910.         mInLayout = true;  
  911.         populate();  
  912.         mInLayout = false;  
  913.   
  914.         final int count = getChildCount();  
  915.         final int width = r-l;  
  916.   
  917.         for (int i = 0; i < count; i++) {  
  918.             View child = getChildAt(i);  
  919.             ItemInfo ii;  
  920.             if (child.getVisibility() != GONE && (ii=infoForChild(child)) != null) {  
  921.                 int loff = (width + mPageMargin) * ii.position;  
  922.                 int childLeft = getPaddingLeft() + loff;  
  923.                 int childTop = getPaddingTop();  
  924.                 if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object  
  925.                 + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()  
  926.                 + "x" + child.getMeasuredHeight());  
  927.                 child.layout(childLeft, childTop,  
  928.                         childLeft + child.getMeasuredWidth(),  
  929.                         childTop + child.getMeasuredHeight());  
  930.             }  
  931.         }  
  932.         mFirstLayout = false;  
  933.     }  
  934.   
  935.     @Override  
  936.     public void computeScroll() {  
  937.         if (DEBUG) Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished());  
  938.         if (!mScroller.isFinished()) {  
  939.             if (mScroller.computeScrollOffset()) {  
  940.                 if (DEBUG) Log.i(TAG, "computeScroll: still scrolling");  
  941.                 int oldX = getScrollX();  
  942.                 int oldY = getScrollY();  
  943.                 int x = mScroller.getCurrX();  
  944.                 int y = mScroller.getCurrY();  
  945.   
  946.                 if (oldX != x || oldY != y) {  
  947.                     scrollTo(x, y);  
  948.                 }  
  949.   
  950.                 if (mOnPageChangeListener != null) {  
  951.                     final int widthWithMargin = getWidth() + mPageMargin;  
  952.                     final int position = x / widthWithMargin;  
  953.                     final int offsetPixels = x % widthWithMargin;  
  954.                     final float offset = (float) offsetPixels / widthWithMargin;  
  955.                     mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);  
  956.                 }  
  957.   
  958.                 // Keep on drawing until the animation has finished.  
  959.                 invalidate();  
  960.                 return;  
  961.             }  
  962.         }  
  963.   
  964.         // Done with scroll, clean up state.  
  965.         completeScroll();  
  966.     }  
  967.   
  968.     private void completeScroll() {  
  969.         boolean needPopulate = mScrolling;  
  970.         if (needPopulate) {  
  971.             // Done with scroll, no longer want to cache view drawing.  
  972.             setScrollingCacheEnabled(false);  
  973.             mScroller.abortAnimation();  
  974.             int oldX = getScrollX();  
  975.             int oldY = getScrollY();  
  976.             int x = mScroller.getCurrX();  
  977.             int y = mScroller.getCurrY();  
  978.             if (oldX != x || oldY != y) {  
  979.                 scrollTo(x, y);  
  980.             }  
  981.             setScrollState(SCROLL_STATE_IDLE);  
  982.         }  
  983.         mPopulatePending = false;  
  984.         mScrolling = false;  
  985.         for (int i=0; i<mItems.size(); i++) {  
  986.             ItemInfo ii = mItems.get(i);  
  987.             if (ii.scrolling) {  
  988.                 needPopulate = true;  
  989.                 ii.scrolling = false;  
  990.             }  
  991.         }  
  992.         if (needPopulate) {  
  993.             populate();  
  994.         }  
  995.     }  
  996.   
  997.     @Override  
  998.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  999.         /* 
  1000.          * This method JUST determines whether we want to intercept the motion. 
  1001.          * If we return true, onMotionEvent will be called and we do the actual 
  1002.          * scrolling there. 
  1003.          */  
  1004.   
  1005.         final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;  
  1006.   
  1007.         // Always take care of the touch gesture being complete.  
  1008.         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {  
  1009.             // Release the drag.  
  1010.             if (DEBUG) Log.v(TAG, "Intercept done!");  
  1011.             mIsBeingDragged = false;  
  1012.             mIsUnableToDrag = false;  
  1013.             mActivePointerId = INVALID_POINTER;  
  1014.             return false;  
  1015.         }  
  1016.   
  1017.         // Nothing more to do here if we have decided whether or not we  
  1018.         // are dragging.  
  1019.         if (action != MotionEvent.ACTION_DOWN) {  
  1020.             if (mIsBeingDragged) {  
  1021.                 if (DEBUG) Log.v(TAG, "Intercept returning true!");  
  1022.                 return true;  
  1023.             }  
  1024.             if (mIsUnableToDrag) {  
  1025.                 if (DEBUG) Log.v(TAG, "Intercept returning false!");  
  1026.                 return false;  
  1027.             }  
  1028.         }  
  1029.   
  1030.         switch (action) {  
  1031.             case MotionEvent.ACTION_MOVE: {  
  1032.                 /* 
  1033.                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 
  1034.                  * whether the user has moved far enough from his original down touch. 
  1035.                  */  
  1036.   
  1037.                 /* 
  1038.                 * Locally do absolute value. mLastMotionY is set to the y value 
  1039.                 * of the down event. 
  1040.                 */  
  1041.                 final int activePointerId = mActivePointerId;  
  1042.                 if (activePointerId == INVALID_POINTER) {  
  1043.                     // If we don't have a valid id, the touch down wasn't on content.  
  1044.                     break;  
  1045.                 }  
  1046.   
  1047.                 final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);  
  1048.                 final float x = MotionEventCompat.getX(ev, pointerIndex);  
  1049.                 final float dx = x - mLastMotionX;  
  1050.                 final float xDiff = Math.abs(dx);  
  1051.                 final float y = MotionEventCompat.getY(ev, pointerIndex);  
  1052.                 final float yDiff = Math.abs(y - mLastMotionY);  
  1053.                 final int scrollX = getScrollX();  
  1054.                 final boolean atEdge = (dx > 0 && scrollX == 0) || (dx < 0 && mAdapter != null &&  
  1055.                         scrollX >= (mAdapter.getCount() - 1) * getWidth() - 1);  
  1056.                 if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);  
  1057.   
  1058.                 if (canScroll(thisfalse, (int) dx, (int) x, (int) y)) {  
  1059.                     // Nested view has scrollable area under this point. Let it be handled there.  
  1060.                     mInitialMotionX = mLastMotionX = x;  
  1061.                     mLastMotionY = y;  
  1062.                     return false;  
  1063.                 }  
  1064.                 if (xDiff > mTouchSlop && xDiff > yDiff) {  
  1065.                     if (DEBUG) Log.v(TAG, "Starting drag!");  
  1066.                     mIsBeingDragged = true;  
  1067.                     setScrollState(SCROLL_STATE_DRAGGING);  
  1068.                     mLastMotionX = x;  
  1069.                     setScrollingCacheEnabled(true);  
  1070.                 } else {  
  1071.                     if (yDiff > mTouchSlop) {  
  1072.                         // The finger has moved enough in the vertical  
  1073.                         // direction to be counted as a drag...  abort  
  1074.                         // any attempt to drag horizontally, to work correctly  
  1075.                         // with children that have scrolling containers.  
  1076.                         if (DEBUG) Log.v(TAG, "Starting unable to drag!");  
  1077.                         mIsUnableToDrag = true;  
  1078.                     }  
  1079.                 }  
  1080.                 break;  
  1081.             }  
  1082.   
  1083.             case MotionEvent.ACTION_DOWN: {  
  1084.                 /* 
  1085.                  * Remember location of down touch. 
  1086.                  * ACTION_DOWN always refers to pointer index 0. 
  1087.                  */  
  1088.                 mLastMotionX = mInitialMotionX = ev.getX();  
  1089.                 mLastMotionY = ev.getY();  
  1090.                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);  
  1091.   
  1092.                 if (mScrollState == SCROLL_STATE_SETTLING) {  
  1093.                     // Let the user 'catch' the pager as it animates.  
  1094.                     mIsBeingDragged = true;  
  1095.                     mIsUnableToDrag = false;  
  1096.                     setScrollState(SCROLL_STATE_DRAGGING);  
  1097.                 } else {  
  1098.                     completeScroll();  
  1099.                     mIsBeingDragged = false;  
  1100.                     mIsUnableToDrag = false;  
  1101.                 }  
  1102.   
  1103.                 if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY  
  1104.                         + " mIsBeingDragged=" + mIsBeingDragged  
  1105.                         + "mIsUnableToDrag=" + mIsUnableToDrag);  
  1106.                 break;  
  1107.             }  
  1108.   
  1109.             case MotionEventCompat.ACTION_POINTER_UP:  
  1110.                 onSecondaryPointerUp(ev);  
  1111.                 break;  
  1112.         }  
  1113.   
  1114.         /* 
  1115.         * The only time we want to intercept motion events is if we are in the 
  1116.         * drag mode. 
  1117.         */  
  1118.         return mIsBeingDragged;  
  1119.     }  
  1120.   
  1121.     @Override  
  1122.     public boolean onTouchEvent(MotionEvent ev) {  
  1123.         if (mFakeDragging) {  
  1124.             // A fake drag is in progress already, ignore this real one  
  1125.             // but still eat the touch events.  
  1126.             // (It is likely that the user is multi-touching the screen.)  
  1127.             return true;  
  1128.         }  
  1129.   
  1130.         if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {  
  1131.             // Don't handle edge touches immediately -- they may actually belong to one of our  
  1132.             // descendants.  
  1133.             return false;  
  1134.         }  
  1135.   
  1136.         if (mAdapter == null || mAdapter.getCount() == 0) {  
  1137.             // Nothing to present or scroll; nothing to touch.  
  1138.             return false;  
  1139.         }  
  1140.   
  1141.         if (mVelocityTracker == null) {  
  1142.             mVelocityTracker = VelocityTracker.obtain();  
  1143.         }  
  1144.         mVelocityTracker.addMovement(ev);  
  1145.   
  1146.         final int action = ev.getAction();  
  1147.         boolean needsInvalidate = false;  
  1148.   
  1149.         switch (action & MotionEventCompat.ACTION_MASK) {  
  1150.             case MotionEvent.ACTION_DOWN: {  
  1151.                 /* 
  1152.                  * If being flinged and user touches, stop the fling. isFinished 
  1153.                  * will be false if being flinged. 
  1154.                  */  
  1155.                 completeScroll();  
  1156.   
  1157.                 // Remember where the motion event started  
  1158.                 mLastMotionX = mInitialMotionX = ev.getX();  
  1159.                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);  
  1160.                 break;  
  1161.             }  
  1162.             case MotionEvent.ACTION_MOVE:  
  1163.                 if (!mIsBeingDragged) {  
  1164.                     final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);  
  1165.                     final float x = MotionEventCompat.getX(ev, pointerIndex);  
  1166.                     final float xDiff = Math.abs(x - mLastMotionX);  
  1167.                     final float y = MotionEventCompat.getY(ev, pointerIndex);  
  1168.                     final float yDiff = Math.abs(y - mLastMotionY);  
  1169.                     if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);  
  1170.                     if (xDiff > mTouchSlop && xDiff > yDiff) {  
  1171.                         if (DEBUG) Log.v(TAG, "Starting drag!");  
  1172.                         mIsBeingDragged = true;  
  1173.                         mLastMotionX = x;  
  1174.                         setScrollState(SCROLL_STATE_DRAGGING);  
  1175.                         setScrollingCacheEnabled(true);  
  1176.                     }  
  1177.                 }  
  1178.                 if (mIsBeingDragged) {  
  1179.                     // Scroll to follow the motion event  
  1180.                     final int activePointerIndex = MotionEventCompat.findPointerIndex(  
  1181.                             ev, mActivePointerId);  
  1182.                     final float x = MotionEventCompat.getX(ev, activePointerIndex);  
  1183.                     final float deltaX = mLastMotionX - x;  
  1184.                     mLastMotionX = x;  
  1185.                     float oldScrollX = getScrollX();  
  1186.                     float scrollX = oldScrollX + deltaX;  
  1187.                     final int width = getWidth();  
  1188.                     final int widthWithMargin = width + mPageMargin;  
  1189.   
  1190.                     final int lastItemIndex = mAdapter.getCount() - 1;  
  1191.                     final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);  
  1192.                     final float rightBound =  
  1193.                             Math.min(mCurItem + 1, lastItemIndex) * widthWithMargin;  
  1194.                     if (scrollX < leftBound) {  
  1195.                         if (leftBound == 0) {  
  1196.                             float over = -scrollX;  
  1197.                             needsInvalidate = mLeftEdge.onPull(over / width);  
  1198.                         }  
  1199.                         scrollX = leftBound;  
  1200.                     } else if (scrollX > rightBound) {  
  1201.                         if (rightBound == lastItemIndex * widthWithMargin) {  
  1202.                             float over = scrollX - rightBound;  
  1203.                             needsInvalidate = mRightEdge.onPull(over / width);  
  1204.                         }  
  1205.                         scrollX = rightBound;  
  1206.                     }  
  1207.                     // Don't lose the rounded component  
  1208.                     mLastMotionX += scrollX - (int) scrollX;  
  1209.                     scrollTo((int) scrollX, getScrollY());  
  1210.                     if (mOnPageChangeListener != null) {  
  1211.                         final int position = (int) scrollX / widthWithMargin;  
  1212.                         final int positionOffsetPixels = (int) scrollX % widthWithMargin;  
  1213.                         final float positionOffset = (float) positionOffsetPixels / widthWithMargin;  
  1214.                         mOnPageChangeListener.onPageScrolled(position, positionOffset,  
  1215.                                 positionOffsetPixels);  
  1216.                     }  
  1217.                 }  
  1218.                 break;  
  1219.             case MotionEvent.ACTION_UP:  
  1220.                 if (mIsBeingDragged) {  
  1221.                     final VelocityTracker velocityTracker = mVelocityTracker;  
  1222.                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
  1223.                     int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(  
  1224.                             velocityTracker, mActivePointerId);  
  1225.                     mPopulatePending = true;  
  1226.                     final int widthWithMargin = getWidth() + mPageMargin;  
  1227.                     final int scrollX = getScrollX();  
  1228.                     final int currentPage = scrollX / widthWithMargin;  
  1229.                     int nextPage = initialVelocity > 0 ? currentPage : currentPage + 1;  
  1230.                     setCurrentItemInternal(nextPage, truetrue, initialVelocity);  
  1231.   
  1232.                     mActivePointerId = INVALID_POINTER;  
  1233.                     endDrag();  
  1234.                     needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();  
  1235.                 }  
  1236.                 break;  
  1237.             case MotionEvent.ACTION_CANCEL:  
  1238.                 if (mIsBeingDragged) {  
  1239.                     setCurrentItemInternal(mCurItem, truetrue);  
  1240.                     mActivePointerId = INVALID_POINTER;  
  1241.                     endDrag();  
  1242.                     needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();  
  1243.                 }  
  1244.                 break;  
  1245.             case MotionEventCompat.ACTION_POINTER_DOWN: {  
  1246.                 final int index = MotionEventCompat.getActionIndex(ev);  
  1247.                 final float x = MotionEventCompat.getX(ev, index);  
  1248.                 mLastMotionX = x;  
  1249.                 mActivePointerId = MotionEventCompat.getPointerId(ev, index);  
  1250.                 break;  
  1251.             }  
  1252.             case MotionEventCompat.ACTION_POINTER_UP:  
  1253.                 onSecondaryPointerUp(ev);  
  1254.                 mLastMotionX = MotionEventCompat.getX(ev,  
  1255.                         MotionEventCompat.findPointerIndex(ev, mActivePointerId));  
  1256.                 break;  
  1257.         }  
  1258.         if (needsInvalidate) {  
  1259.             invalidate();  
  1260.         }  
  1261.         return true;  
  1262.     }  
  1263.   
  1264.     @Override  
  1265.     public void draw(Canvas canvas) {  
  1266.         super.draw(canvas);  
  1267.         boolean needsInvalidate = false;  
  1268.   
  1269.         final int overScrollMode = ViewCompat.getOverScrollMode(this);  
  1270.         if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||  
  1271.                 (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&  
  1272.                         mAdapter != null && mAdapter.getCount() > 1)) {  
  1273.             if (!mLeftEdge.isFinished()) {  
  1274.                 final int restoreCount = canvas.save();  
  1275.                 final int height = getHeight() - getPaddingTop() - getPaddingBottom();  
  1276.   
  1277.                 canvas.rotate(270);  
  1278.                 canvas.translate(-height + getPaddingTop(), 0);  
  1279.                 mLeftEdge.setSize(height, getWidth());  
  1280.                 needsInvalidate |= mLeftEdge.draw(canvas);  
  1281.                 canvas.restoreToCount(restoreCount);  
  1282.             }  
  1283.             if (!mRightEdge.isFinished()) {  
  1284.                 final int restoreCount = canvas.save();  
  1285.                 final int width = getWidth();  
  1286.                 final int height = getHeight() - getPaddingTop() - getPaddingBottom();  
  1287.                 final int itemCount = mAdapter != null ? mAdapter.getCount() : 1;  
  1288.   
  1289.                 canvas.rotate(90);  
  1290.                 canvas.translate(-getPaddingTop(),  
  1291.                         -itemCount * (width + mPageMargin) + mPageMargin);  
  1292.                 mRightEdge.setSize(height, width);  
  1293.                 needsInvalidate |= mRightEdge.draw(canvas);  
  1294.                 canvas.restoreToCount(restoreCount);  
  1295.             }  
  1296.         } else {  
  1297.             mLeftEdge.finish();  
  1298.             mRightEdge.finish();  
  1299.         }  
  1300.   
  1301.         if (needsInvalidate) {  
  1302.             // Keep animating  
  1303.             invalidate();  
  1304.         }  
  1305.     }  
  1306.   
  1307.     @Override  
  1308.     protected void onDraw(Canvas canvas) {  
  1309.         super.onDraw(canvas);  
  1310.   
  1311.         // Draw the margin drawable if needed.  
  1312.         if (mPageMargin > 0 && mMarginDrawable != null) {  
  1313.             final int scrollX = getScrollX();  
  1314.             final int width = getWidth();  
  1315.             final int offset = scrollX % (width + mPageMargin);  
  1316.             if (offset != 0) {  
  1317.                 // Pages fit completely when settled; we only need to draw when in between  
  1318.                 final int left = scrollX - offset + width;  
  1319.                 mMarginDrawable.setBounds(left, 0, left + mPageMargin, getHeight());  
  1320.                 mMarginDrawable.draw(canvas);  
  1321.             }  
  1322.         }  
  1323.     }  
  1324.   
  1325.     /** 
  1326.      * Start a fake drag of the pager. 
  1327.      * 
  1328.      * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager 
  1329.      * with the touch scrolling of another view, while still letting the ViewPager 
  1330.      * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.) 
  1331.      * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call 
  1332.      * {@link #endFakeDrag()} to complete the fake drag and fling as necessary. 
  1333.      * 
  1334.      * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag 
  1335.      * is already in progress, this method will return false. 
  1336.      * 
  1337.      * @return true if the fake drag began successfully, false if it could not be started. 
  1338.      * 
  1339.      * @see #fakeDragBy(float) 
  1340.      * @see #endFakeDrag() 
  1341.      */  
  1342.     public boolean beginFakeDrag() {  
  1343.         if (mIsBeingDragged) {  
  1344.             return false;  
  1345.         }  
  1346.         mFakeDragging = true;  
  1347.         setScrollState(SCROLL_STATE_DRAGGING);  
  1348.         mInitialMotionX = mLastMotionX = 0;  
  1349.         if (mVelocityTracker == null) {  
  1350.             mVelocityTracker = VelocityTracker.obtain();  
  1351.         } else {  
  1352.             mVelocityTracker.clear();  
  1353.         }  
  1354.         final long time = SystemClock.uptimeMillis();  
  1355.         final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 000);  
  1356.         mVelocityTracker.addMovement(ev);  
  1357.         ev.recycle();  
  1358.         mFakeDragBeginTime = time;  
  1359.         return true;  
  1360.     }  
  1361.   
  1362.     /** 
  1363.      * End a fake drag of the pager. 
  1364.      * 
  1365.      * @see #beginFakeDrag() 
  1366.      * @see #fakeDragBy(float) 
  1367.      */  
  1368.     public void endFakeDrag() {  
  1369.         if (!mFakeDragging) {  
  1370.             throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");  
  1371.         }  
  1372.   
  1373.         final VelocityTracker velocityTracker = mVelocityTracker;  
  1374.         velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
  1375.         int initialVelocity = (int)VelocityTrackerCompat.getYVelocity(  
  1376.                 velocityTracker, mActivePointerId);  
  1377.         mPopulatePending = true;  
  1378.         if ((Math.abs(initialVelocity) > mMinimumVelocity)  
  1379.                 || Math.abs(mInitialMotionX-mLastMotionX) >= (getWidth()/3)) {  
  1380.             if (mLastMotionX > mInitialMotionX) {  
  1381.                 setCurrentItemInternal(mCurItem-1truetrue);  
  1382.             } else {  
  1383.                 setCurrentItemInternal(mCurItem+1truetrue);  
  1384.             }  
  1385.         } else {  
  1386.             setCurrentItemInternal(mCurItem, truetrue);  
  1387.         }  
  1388.         endDrag();  
  1389.   
  1390.         mFakeDragging = false;  
  1391.     }  
  1392.   
  1393.     /** 
  1394.      * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first. 
  1395.      * 
  1396.      * @param xOffset Offset in pixels to drag by. 
  1397.      * @see #beginFakeDrag() 
  1398.      * @see #endFakeDrag() 
  1399.      */  
  1400.     public void fakeDragBy(float xOffset) {  
  1401.         if (!mFakeDragging) {  
  1402.             throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");  
  1403.         }  
  1404.   
  1405.         mLastMotionX += xOffset;  
  1406.         float scrollX = getScrollX() - xOffset;  
  1407.         final int width = getWidth();  
  1408.         final int widthWithMargin = width + mPageMargin;  
  1409.   
  1410.         final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);  
  1411.         final float rightBound =  
  1412.                 Math.min(mCurItem + 1, mAdapter.getCount() - 1) * widthWithMargin;  
  1413.         if (scrollX < leftBound) {  
  1414.             scrollX = leftBound;  
  1415.         } else if (scrollX > rightBound) {  
  1416.             scrollX = rightBound;  
  1417.         }  
  1418.         // Don't lose the rounded component  
  1419.         mLastMotionX += scrollX - (int) scrollX;  
  1420.         scrollTo((int) scrollX, getScrollY());  
  1421.         if (mOnPageChangeListener != null) {  
  1422.             final int position = (int) scrollX / widthWithMargin;  
  1423.             final int positionOffsetPixels = (int) scrollX % widthWithMargin;  
  1424.             final float positionOffset = (float) positionOffsetPixels / widthWithMargin;  
  1425.             mOnPageChangeListener.onPageScrolled(position, positionOffset,  
  1426.                     positionOffsetPixels);  
  1427.         }  
  1428.   
  1429.         // Synthesize an event for the VelocityTracker.  
  1430.         final long time = SystemClock.uptimeMillis();  
  1431.         final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,  
  1432.                 mLastMotionX, 00);  
  1433.         mVelocityTracker.addMovement(ev);  
  1434.         ev.recycle();  
  1435.     }  
  1436.   
  1437.     /** 
  1438.      * Returns true if a fake drag is in progress. 
  1439.      * 
  1440.      * @return true if currently in a fake drag, false otherwise. 
  1441.      * 
  1442.      * @see #beginFakeDrag() 
  1443.      * @see #fakeDragBy(float) 
  1444.      * @see #endFakeDrag() 
  1445.      */  
  1446.     public boolean isFakeDragging() {  
  1447.         return mFakeDragging;  
  1448.     }  
  1449.   
  1450.     private void onSecondaryPointerUp(MotionEvent ev) {  
  1451.         final int pointerIndex = MotionEventCompat.getActionIndex(ev);  
  1452.         final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);  
  1453.         if (pointerId == mActivePointerId) {  
  1454.             // This was our active pointer going up. Choose a new  
  1455.             // active pointer and adjust accordingly.  
  1456.             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;  
  1457.             mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);  
  1458.             mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);  
  1459.             if (mVelocityTracker != null) {  
  1460.                 mVelocityTracker.clear();  
  1461.             }  
  1462.         }  
  1463.     }  
  1464.   
  1465.     private void endDrag() {  
  1466.         mIsBeingDragged = false;  
  1467.         mIsUnableToDrag = false;  
  1468.   
  1469.         if (mVelocityTracker != null) {  
  1470.             mVelocityTracker.recycle();  
  1471.             mVelocityTracker = null;  
  1472.         }  
  1473.     }  
  1474.   
  1475.     private void setScrollingCacheEnabled(boolean enabled) {  
  1476.         if (mScrollingCacheEnabled != enabled) {  
  1477.             mScrollingCacheEnabled = enabled;  
  1478.             if (USE_CACHE) {  
  1479.                 final int size = getChildCount();  
  1480.                 for (int i = 0; i < size; ++i) {  
  1481.                     final View child = getChildAt(i);  
  1482.                     if (child.getVisibility() != GONE) {  
  1483.                         child.setDrawingCacheEnabled(enabled);  
  1484.                     }  
  1485.                 }  
  1486.             }  
  1487.         }  
  1488.     }  
  1489.   
  1490.     /** 
  1491.      * Tests scrollability within child views of v given a delta of dx. 
  1492.      * 
  1493.      * @param v View to test for horizontal scrollability 
  1494.      * @param checkV Whether the view v passed should itself be checked for scrollability (true), 
  1495.      *               or just its children (false). 
  1496.      * @param dx Delta scrolled in pixels 
  1497.      * @param x X coordinate of the active touch point 
  1498.      * @param y Y coordinate of the active touch point 
  1499.      * @return true if child views of v can be scrolled by delta of dx. 
  1500.      */  
  1501.     protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {  
  1502.         if (v instanceof ViewGroup) {  
  1503.             final ViewGroup group = (ViewGroup) v;  
  1504.             final int scrollX = v.getScrollX();  
  1505.             final int scrollY = v.getScrollY();  
  1506.             final int count = group.getChildCount();  
  1507.             // Count backwards - let topmost views consume scroll distance first.  
  1508.             for (int i = count - 1; i >= 0; i--) {  
  1509.                 // TODO: Add versioned support here for transformed views.  
  1510.                 // This will not work for transformed views in Honeycomb+  
  1511.                 final View child = group.getChildAt(i);  
  1512.                 if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&  
  1513.                         y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&  
  1514.                         canScroll(child, true, dx, x + scrollX - child.getLeft(),  
  1515.                                 y + scrollY - child.getTop())) {  
  1516.                     return true;  
  1517.                 }  
  1518.             }  
  1519.         }  
  1520.   
  1521.         return checkV && ViewCompat.canScrollHorizontally(v, -dx);  
  1522.     }  
  1523.   
  1524.     @Override  
  1525.     public boolean dispatchKeyEvent(KeyEvent event) {  
  1526.         // Let the focused view and/or our descendants get the key first  
  1527.         return super.dispatchKeyEvent(event) || executeKeyEvent(event);  
  1528.     }  
  1529.   
  1530.     /** 
  1531.      * You can call this function yourself to have the scroll view perform 
  1532.      * scrolling from a key event, just as if the event had been dispatched to 
  1533.      * it by the view hierarchy. 
  1534.      * 
  1535.      * @param event The key event to execute. 
  1536.      * @return Return true if the event was handled, else false. 
  1537.      */  
  1538.     public boolean executeKeyEvent(KeyEvent event) {  
  1539.         boolean handled = false;  
  1540.         if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  1541.             switch (event.getKeyCode()) {  
  1542.                 case KeyEvent.KEYCODE_DPAD_LEFT:  
  1543.                     handled = arrowScroll(FOCUS_LEFT);  
  1544.                     break;  
  1545.                 case KeyEvent.KEYCODE_DPAD_RIGHT:  
  1546.                     handled = arrowScroll(FOCUS_RIGHT);  
  1547.                     break;  
  1548.                 case KeyEvent.KEYCODE_TAB:  
  1549.                     if (KeyEventCompat.hasNoModifiers(event)) {  
  1550.                         handled = arrowScroll(FOCUS_FORWARD);  
  1551.                     } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {  
  1552.                         handled = arrowScroll(FOCUS_BACKWARD);  
  1553.                     }  
  1554.                     break;  
  1555.             }  
  1556.         }  
  1557.         return handled;  
  1558.     }  
  1559.   
  1560.     public boolean arrowScroll(int direction) {  
  1561.         View currentFocused = findFocus();  
  1562.         if (currentFocused == this) currentFocused = null;  
  1563.   
  1564.         boolean handled = false;  
  1565.   
  1566.         View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,  
  1567.                 direction);  
  1568.         if (nextFocused != null && nextFocused != currentFocused) {  
  1569.             if (direction == View.FOCUS_LEFT) {  
  1570.                 // If there is nothing to the left, or this is causing us to  
  1571.                 // jump to the right, then what we really want to do is page left.  
  1572.                 if (currentFocused != null && nextFocused.getLeft() >= currentFocused.getLeft()) {  
  1573.                     handled = pageLeft();  
  1574.                 } else {  
  1575.                     handled = nextFocused.requestFocus();  
  1576.                 }  
  1577.             } else if (direction == View.FOCUS_RIGHT) {  
  1578.                 // If there is nothing to the right, or this is causing us to  
  1579.                 // jump to the left, then what we really want to do is page right.  
  1580.                 if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) {  
  1581.                     handled = pageRight();  
  1582.                 } else {  
  1583.                     handled = nextFocused.requestFocus();  
  1584.                 }  
  1585.             }  
  1586.         } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {  
  1587.             // Trying to move left and nothing there; try to page.  
  1588.             handled = pageLeft();  
  1589.         } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {  
  1590.             // Trying to move right and nothing there; try to page.  
  1591.             handled = pageRight();  
  1592.         }  
  1593.         if (handled) {  
  1594.             playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));  
  1595.         }  
  1596.         return handled;  
  1597.     }  
  1598.   
  1599.     boolean pageLeft() {  
  1600.         if (mCurItem > 0) {  
  1601.             setCurrentItem(mCurItem-1true);  
  1602.             return true;  
  1603.         }  
  1604.         return false;  
  1605.     }  
  1606.   
  1607.     boolean pageRight() {  
  1608.         if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {  
  1609.             setCurrentItem(mCurItem+1true);  
  1610.             return true;  
  1611.         }  
  1612.         return false;  
  1613.     }  
  1614.   
  1615.     /** 
  1616.      * We only want the current page that is being shown to be focusable. 
  1617.      */  
  1618.     @Override  
  1619.     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {  
  1620.         final int focusableCount = views.size();  
  1621.   
  1622.         final int descendantFocusability = getDescendantFocusability();  
  1623.   
  1624.         if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {  
  1625.             for (int i = 0; i < getChildCount(); i++) {  
  1626.                 final View child = getChildAt(i);  
  1627.                 if (child.getVisibility() == VISIBLE) {  
  1628.                     ItemInfo ii = infoForChild(child);  
  1629.                     if (ii != null && ii.position == mCurItem) {  
  1630.                         child.addFocusables(views, direction, focusableMode);  
  1631.                     }  
  1632.                 }  
  1633.             }  
  1634.         }  
  1635.   
  1636.         // we add ourselves (if focusable) in all cases except for when we are  
  1637.         // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is  
  1638.         // to avoid the focus search finding layouts when a more precise search  
  1639.         // among the focusable children would be more interesting.  
  1640.         if (  
  1641.             descendantFocusability != FOCUS_AFTER_DESCENDANTS ||  
  1642.                 // No focusable descendants  
  1643.                 (focusableCount == views.size())) {  
  1644.             // Note that we can't call the superclass here, because it will  
  1645.             // add all views in.  So we need to do the same thing View does.  
  1646.             if (!isFocusable()) {  
  1647.                 return;  
  1648.             }  
  1649.             if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&  
  1650.                     isInTouchMode() && !isFocusableInTouchMode()) {  
  1651.                 return;  
  1652.             }  
  1653.             if (views != null) {  
  1654.                 views.add(this);  
  1655.             }  
  1656.         }  
  1657.     }  
  1658.   
  1659.     /** 
  1660.      * We only want the current page that is being shown to be touchable. 
  1661.      */  
  1662.     @Override  
  1663.     public void addTouchables(ArrayList<View> views) {  
  1664.         // Note that we don't call super.addTouchables(), which means that  
  1665.         // we don't call View.addTouchables().  This is okay because a ViewPager  
  1666.         // is itself not touchable.  
  1667.         for (int i = 0; i < getChildCount(); i++) {  
  1668.             final View child = getChildAt(i);  
  1669.             if (child.getVisibility() == VISIBLE) {  
  1670.                 ItemInfo ii = infoForChild(child);  
  1671.                 if (ii != null && ii.position == mCurItem) {  
  1672.                     child.addTouchables(views);  
  1673.                 }  
  1674.             }  
  1675.         }  
  1676.     }  
  1677.   
  1678.     /** 
  1679.      * We only want the current page that is being shown to be focusable. 
  1680.      */  
  1681.     @Override  
  1682.     protected boolean onRequestFocusInDescendants(int direction,  
  1683.             Rect previouslyFocusedRect) {  
  1684.         int index;  
  1685.         int increment;  
  1686.         int end;  
  1687.         int count = getChildCount();  
  1688.         if ((direction & FOCUS_FORWARD) != 0) {  
  1689.             index = 0;  
  1690.             increment = 1;  
  1691.             end = count;  
  1692.         } else {  
  1693.             index = count - 1;  
  1694.             increment = -1;  
  1695.             end = -1;  
  1696.         }  
  1697.         for (int i = index; i != end; i += increment) {  
  1698.             View child = getChildAt(i);  
  1699.             if (child.getVisibility() == VISIBLE) {  
  1700.                 ItemInfo ii = infoForChild(child);  
  1701.                 if (ii != null && ii.position == mCurItem) {  
  1702.                     if (child.requestFocus(direction, previouslyFocusedRect)) {  
  1703.                         return true;  
  1704.                     }  
  1705.                 }  
  1706.             }  
  1707.         }  
  1708.         return false;  
  1709.     }  
  1710.   
  1711.     @Override  
  1712.     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {  
  1713.         // ViewPagers should only report accessibility info for the current page,  
  1714.         // otherwise things get very confusing.  
  1715.   
  1716.         // TODO: Should this note something about the paging container?  
  1717.   
  1718.         final int childCount = getChildCount();  
  1719.         for (int i = 0; i < childCount; i++) {  
  1720.             final View child = getChildAt(i);  
  1721.             if (child.getVisibility() == VISIBLE) {  
  1722.                 final ItemInfo ii = infoForChild(child);  
  1723.                 if (ii != null && ii.position == mCurItem &&  
  1724.                         child.dispatchPopulateAccessibilityEvent(event)) {  
  1725.                     return true;  
  1726.                 }  
  1727.             }  
  1728.         }  
  1729.   
  1730.         return false;  
  1731.     }  
  1732.   
  1733.     private class PagerObserver extends DataSetObserver {  
  1734.   
  1735.         @Override  
  1736.         public void onChanged() {  
  1737.             dataSetChanged();  
  1738.         }  
  1739.   
  1740.         @Override  
  1741.         public void onInvalidated() {  
  1742.             dataSetChanged();  
  1743.         }  
  1744.     }  
  1745. }

0 0