转自:http://blog.csdn.net/qq_21898059/article/details/51453938#comments

我最近上班又遇到一个小难题了,就是如题所述: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就能用了。

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

NoPreloadViewPager

关于禁止ViewPager预加载问题【转】相关推荐

  1. ViewPager+Fragment 组合的预加载和懒加载

    ViewPager+Fragment 组合的预加载和懒加载 转载自http://www.crocutax.com 预加载介绍 ViewPager+Fragment的搭配在日常开发中也比较常见,可用于切 ...

  2. android预加载布局,Android 懒加载优化

    目录介绍 1.什么是懒加载 1.1 什么是预加载 1.2 懒加载介绍 1.3 懒加载概括 2.实际应用中有哪些懒加载案例 2.1 ViewPager+Fragment组合 2.2 分析源码 3.Vie ...

  3. android viewpage预加载和懒加载问题

    1.本人理解懒加载和预加载问题某种情况下可以归结为一类问题,下面我就说一下我遇到的预加载问题和懒加载问题及解决的相应方法: - [1 ] 预加载问题         描述:我用到了三个fragment ...

  4. android Viewpager取消预加载及Fragment方法的学习

    1.在使用ViewPager嵌套Fragment的时候,由于VIewPager的几个Adapter的设置来说,都会有一定的预加载.通过设置setOffscreenPageLimit(int numbe ...

  5. android 移除fragment,Android Viewpager+Fragment取消预加载及Fragment方法的学习

    1.在使用ViewPager嵌套Fragment的时候,由于VIewPager的几个Adapter的设置来说,都会有一定的预加载.通过设置setOffscreenPageLimit(int numbe ...

  6. android调用h5预加载图片,使用HTML5的页面资源预加载(Link prefetch)功能加速你的页面加载速度...

    不管是浏览器的开发者还是普通web应用的开发者,他们都在做一个共同的努力:让Web浏览有更快的速度感觉.有很多已知的技术都可以让你的网站速度变得更快:使用CSS sprites,使用图片优化工具,使用 ...

  7. Fragment懒加载预加载

    1. 预加载viewpager.setOffscreenPageLimit(2);,默认是预加载1,可以结合懒加载使用. 如果希望进入viewpager,Fragment只加载一次,再次滑动不需加载( ...

  8. 一种网页游戏图片预加载方案

    一种网页游戏图片预加载方案 上个月我写了一篇关于网页游戏图片预加载技术的文章,叫<很山寨的网页游戏图片预加载技术>.这个方案用到项目上后,发现了一些问题,经过大家的努力,这些问题基本得到解 ...

  9. Electron使用preload预加载及安全策略

    使用 Electron 很重要的一点是要理解 Electron 不是一个 Web 浏览器. 它允许您使用熟悉的 Web 技术构建功能丰富的桌面应用程序,但是您的代码具有更强大的功能. JavaScri ...

  10. Cocos creator加载场景、加载场景回调、预加载场景

    注意场景名不要写错,是根据字符串去加载场景的. 同时这也在暗示我们,一个游戏应尽可能地少切换场景~ 官方文档:加载和切换场景 加载场景 加载场景调用如下API: cc.director.loadSce ...

最新文章

  1. 大促下的智能运维挑战:阿里如何抗住“双11猫晚”?
  2. 小小的Python编程故事
  3. 编程小白学python知乎周刊_在知乎上学 Python - 入门篇
  4. 手动添加ubuntu服务
  5. python 3d大数据可视化软件_十大顶级大数据可视化工具推荐
  6. 不需要安装max或者xcode的object C开发环境
  7. 2020科目一考试口诀_二级建造师考试科目有哪些2020
  8. 如何开发ORACLE存储过程
  9. Radware LP 增加线路接口操作
  10. 厦门信息集团与EMC战略合作共建智慧厦门
  11. Android的第一天
  12. 计算机编程思想 —— 缓存
  13. java 正则表达式提取字符串
  14. 微积分总结(数列与无穷级数)
  15. 【Camera】Camera中光圈系数概念以及光圈的作用
  16. 如何使用KALI攻击“恶意网站“实验
  17. 计算股票季度收益率、年收益率和相对收益率并可视化展示。
  18. 企业现金流和资金链区别
  19. Qt 错误qt.network.ssl: QSslSocket: cannot call unresolved function ERR_get_error
  20. K. chino with c language(水题)

热门文章

  1. java枚举类型详解
  2. Request.getparameternames有什么用
  3. 【学习笔记】【C语言】循环结构-do while
  4. 我的oh my zsh主题
  5. Hero In Maze
  6. Android项目中JNI技术生成并调用.so动态库实现详解
  7. 软件测试——JUnit基础
  8. struts2 获取前台表单的值?? 原理??
  9. Elasticsearch从0.90(0.90.x)到1.2(1.x)API的变化-二
  10. 如何往一个指定的地址写入一个值呢