n rel="diff-5fcb712c66f51f4414ecca75fb28fb973d63135cR95">95
            t -= 1.0f;
96
            return t * t * t * t * t + 1.0f;
97
        }
98
    };
99
100
    private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
101
    private final ItemInfo mTempItem = new ItemInfo();
102
103
    private final Rect mTempRect = new Rect();
104
105
    private PagerAdapter mAdapter;
106
    private int mCurItem;   // Index of currently displayed page.
107
    private int mRestoredCurItem = -1;
108
    private Parcelable mRestoredAdapterState = null;
109
    private ClassLoader mRestoredClassLoader = null;
110
    private Scroller mScroller;
111
    private PagerObserver mObserver;
112
113
    private int mPageMargin;
114
    private Drawable mMarginDrawable;
115
    private int mLeftPageBounds;
116
    private int mRightPageBounds;
117
118
    // Offsets of the first and last items, if known.
119
    // Set during population, used to determine if we are at the beginning
120
    // or end of the pager data set during touch scrolling.
121
    private float mFirstOffset = -Float.MAX_VALUE;
122
    private float mLastOffset = Float.MAX_VALUE;
123
124
    private int mChildWidthMeasureSpec;
125
    private int mChildHeightMeasureSpec;
126
    private boolean mInLayout;
127
128
    private boolean mScrollingCacheEnabled;
129
130
    private boolean mPopulatePending;
131
    private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
132
133
    private boolean mIsBeingDragged;
134
    private boolean mIsUnableToDrag;
135
    private boolean mIgnoreGutter;
136
    private int mDefaultGutterSize;
137
    private int mGutterSize;
138
    private int mTouchSlop;
139
    /**
140
     * Position of the last motion event.
141
     */
142
    private float mLastMotionX;
143
    private float mLastMotionY;
144
    private float mInitialMotionX;
145
    private float mInitialMotionY;
146
    /**
147
     * ID of the active pointer. This is used to retain consistency during
148
     * drags/flings if multiple pointers are used.
149
     */
150
    private int mActivePointerId = INVALID_POINTER;
151
    /**
152
     * Sentinel value for no current active pointer.
153
     * Used by {@link #mActivePointerId}.
154
     */
155
    private static final int INVALID_POINTER = -1;
156
157
    /**
158
     * Determines speed during touch scrolling
159
     */
160
    private VelocityTracker mVelocityTracker;
161
    private int mMinimumVelocity;
162
    private int mMaximumVelocity;
163
    private int mFlingDistance;
164
    private int mCloseEnough;
165
166
    // If the pager is at least this close to its final position, complete the scroll
167
    // on touch down and let the user interact with the content inside instead of
168
    // "catching" the flinging pager.
169
    private static final int CLOSE_ENOUGH = 2; // dp
170
171
    private boolean mFakeDragging;
172
    private long mFakeDragBeginTime;
173
174
    private EdgeEffectCompat mTopEdge;
175
    private EdgeEffectCompat mBottomEdge;
176
177
    private boolean mFirstLayout = true;
178
    private boolean mNeedCalculatePageOffsets = false;
179
    private boolean mCalledSuper;
180
    private int mDecorChildCount;
181
182
    private ViewPager.OnPageChangeListener mOnPageChangeListener;
183
    private ViewPager.OnPageChangeListener mInternalPageChangeListener;
184
    private OnAdapterChangeListener mAdapterChangeListener;
185
    private ViewPager.PageTransformer mPageTransformer;
186
    private Method mSetChildrenDrawingOrderEnabled;
187
188
    private static final int DRAW_ORDER_DEFAULT = 0;
189
    private static final int DRAW_ORDER_FORWARD = 1;
190
    private static final int DRAW_ORDER_REVERSE = 2;
191
    private int mDrawingOrder;
192
    private ArrayList<View> mDrawingOrderedChildren;
193
    private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();
194
195
    /**
196
     * Indicates that the pager is in an idle, settled state. The current page
197
     * is fully in view and no animation is in progress.
198
     */
199
    public static final int SCROLL_STATE_IDLE = 0;
200
201
    /**
202
     * Indicates that the pager is currently being dragged by the user.
203
     */
204
    public static final int SCROLL_STATE_DRAGGING = 1;
205
206
    /**
207
     * Indicates that the pager is in the process of settling to a final position.
208
     */
209
    public static final int SCROLL_STATE_SETTLING = 2;
210
211
    private final Runnable mEndScrollRunnable = new Runnable() {
212
        @Override
213
        public void run() {
214
            setScrollState(SCROLL_STATE_IDLE);
215
            populate();
216
        }
217
    };
218
219
    private int mScrollState = SCROLL_STATE_IDLE;
220
221
    /**
222
     * Used internally to monitor when adapters are switched.
223
     */
224
    interface OnAdapterChangeListener {
225
        public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);
226
    }
227
228
    /**
229
     * Used internally to tag special types of child views that should be added as
230
     * pager decorations by default.
231
     */
232
    interface Decor {
233
    }
234
235
    public VerticalViewPager(Context context) {
236
        super(context);
237
        initViewPager();
238
    }
239
240
    public VerticalViewPager(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
        final float density = context.getResources().getDisplayMetrics().density;
253
254
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
255
        mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
256
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
257
        mTopEdge = new EdgeEffectCompat(context);
258
        mBottomEdge = new EdgeEffectCompat(context);
259
260
        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
261
        mCloseEnough = (int) (CLOSE_ENOUGH * density);
262
        mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
263
264
        ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());
265
266
        if (ViewCompat.getImportantForAccessibility(this)
267
                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
268
            ViewCompat.setImportantForAccessibility(this,
269
                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
270
        }
271
    }
272
273
    @Override
274
    protected void onDetachedFromWindow() {
275
        removeCallbacks(mEndScrollRunnable);
276
        super.onDetachedFromWindow();
277
    }
278
279
    private void setScrollState(int newState) {
280
        if (mScrollState == newState) {
281
            return;
282
        }
283
284
        mScrollState = newState;
285
        if (mPageTransformer != null) {
286
            // PageTransformers can do complex things that benefit from hardware layers.
287
            enableLayers(newState != SCROLL_STATE_IDLE);
288
        }
289
        if (mOnPageChangeListener != null) {
290
            mOnPageChangeListener.onPageScrollStateChanged(newState);
291
        }
292
    }
293
294
    /**
295
     * Set a PagerAdapter that will supply views for this pager as needed.
296
     *
297
     * @param adapter Adapter to use
298
     */
299
    public void setAdapter(PagerAdapter adapter) {
300
        if (mAdapter != null) {
301
            mAdapter.unregisterDataSetObserver(mObserver);
302
            mAdapter.startUpdate(this);
303
            for (int i = 0; i < mItems.size(); i++) {
304
                final ItemInfo ii = mItems.get(i);
305
                mAdapter.destroyItem(this, ii.position, ii.object);
306
            }
307
            mAdapter.finishUpdate(this);
308
            mItems.clear();
309
            removeNonDecorViews();
310
            mCurItem = 0;
311
            scrollTo(0, 0);
312
        }
313
314
        final PagerAdapter oldAdapter = mAdapter;
315
        mAdapter = adapter;
316
        mExpectedAdapterCount = 0;
317
318
        if (mAdapter != null) {
319
            if (mObserver == null) {
320
                mObserver = new PagerObserver();
321
            }
322
            mAdapter.registerDataSetObserver(mObserver);
323
            mPopulatePending = false;
324
            final boolean wasFirstLayout = mFirstLayout;
325
            mFirstLayout = true;
326
            mExpectedAdapterCount = mAdapter.getCount();
327
            if (mRestoredCurItem >= 0) {
328
                mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
329
                setCurrentItemInternal(mRestoredCurItem, false, true);
330
                mRestoredCurItem = -1;
331
                mRestoredAdapterState = null;
332
                mRestoredClassLoader = null;
333
            } else if (!wasFirstLayout) {
334
                populate();
335
            } else {
336
                requestLayout();
337
            }
338
        }
339
340
        if (mAdapterChangeListener != null && oldAdapter != adapter) {
341
            mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
342
        }
343
    }
344
345
    private void removeNonDecorViews() {
346
        for (int i = 0; i < getChildCount(); i++) {
347
            final View child = getChildAt(i);
348
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
349
            if (!lp.isDecor) {
350
                removeViewAt(i);
351
                i--;
352
            }
353
        }
354
    }
355
356
    /**
357
     * Retrieve the current adapter supplying pages.
358
     *
359
     * @return The currently registered PagerAdapter
360
     */
361
    public PagerAdapter getAdapter() {
362
        return mAdapter;
363
    }
364
365
    void setOnAdapterChangeListener(OnAdapterChangeListener listener) {
366
        mAdapterChangeListener = listener;
367
    }
368
369
//    private int getClientWidth() {
370
//        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
371
//    }
372
373
    private int getClientHeight() {
374
        return getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
375
    }
376
377
378
    /**
379
     * Set the currently selected page. If the ViewPager has already been through its first
380
     * layout with its current adapter there will be a smooth animated transition between
381
     * the current item and the specified item.
382
     *
383
     * @param item Item index to select
384
     */
385
    public void setCurrentItem(int item) {
386
        mPopulatePending = false;
387
        setCurrentItemInternal(item, !mFirstLayout, false);
388
    }
389
390
    /**
391
     * Set the currently selected page.
392
     *
393
     * @param item         Item index to select
394
     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
395
     */
396
    public void setCurrentItem(int item, boolean smoothScroll) {
397
        mPopulatePending = false;
398
        setCurrentItemInternal(item, smoothScroll, false);
399
    }
400
401
    public int getCurrentItem() {
402
        return mCurItem;
403
    }
404
405
    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
406
        setCurrentItemInternal(item, smoothScroll, always, 0);
407
    }
408
409
    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
410
        if (mAdapter == null || mAdapter.getCount() <= 0) {
411
            setScrollingCacheEnabled(false);
412
            return;
413
        }
414
        if (!always && mCurItem == item && mItems.size() != 0) {
415
            setScrollingCacheEnabled(false);
416
            return;
417
        }
418
419
        if (item < 0) {
420
            item = 0;
421
        } else if (item >= mAdapter.getCount()) {
422
            item = mAdapter.getCount() - 1;
423
        }
424
        final int pageLimit = mOffscreenPageLimit;
425
        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
426
            // We are doing a jump by more than one page.  To avoid
427
            // glitches, we want to keep all current pages in the view
428
            // until the scroll ends.
429
            for (int i = 0; i < mItems.size(); i++) {
430
                mItems.get(i).scrolling = true;
431
            }
432
        }
433
        final boolean dispatchSelected = mCurItem != item;
434
435
        if (mFirstLayout) {
436
            // We don't have any idea how big we are yet and shouldn't have any pages either.
437
            // Just set things up and let the pending layout handle things.
438
            mCurItem = item;
439
            if (dispatchSelected && mOnPageChangeListener != null) {
440
                mOnPageChangeListener.onPageSelected(item);
441
            }
442
            if (dispatchSelected && mInternalPageChangeListener != null) {
443
                mInternalPageChangeListener.onPageSelected(item);
444
            }
445
            requestLayout();
446
        } else {
447
            populate(item);
448
            scrollToItem(item, smoothScroll, velocity, dispatchSelected);
449
        }
450
    }
451
452
    private void scrollToItem(int item, boolean smoothScroll, int velocity,
453
                              boolean dispatchSelected) {
454
        final ItemInfo curInfo = infoForPosition(item);
455
        int destY = 0;
456
        if (curInfo != null) {
457
            final int height = getClientHeight();
458
            destY = (int) (height * Math.max(mFirstOffset,
459
                    Math.min(curInfo.offset, mLastOffset)));
460
        }
461
        if (smoothScroll) {
462
            smoothScrollTo(0, destY, velocity);
463
            if (dispatchSelected && mOnPageChangeListener != null) {
464
                mOnPageChangeListener.onPageSelected(item);
465
            }
466
            if (dispatchSelected && mInternalPageChangeListener != null) {
467
                mInternalPageChangeListener.onPageSelected(item);
468
            }
469
        } else {
470
            if (dispatchSelected && mOnPageChangeListener != null) {
471
                mOnPageChangeListener.onPageSelected(item);
472
            }
473
            if (dispatchSelected && mInternalPageChangeListener != null) {
474
                mInternalPageChangeListener.onPageSelected(item);
475
            }
476
            completeScroll(false);
477
            scrollTo(0, destY);
478
            pageScrolled(destY);
479
        }
480
    }
481
482
    /**
483
     * Set a listener that will be invoked whenever the page changes or is incrementally
484
     * scrolled. See {@link ViewPager.OnPageChangeListener}.
485
     *
486
     * @param listener Listener to set
487
     */
488
    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
489
        mOnPageChangeListener = listener;
490
    }
491
492
    /**
493
     * Set a {@link ViewPager.PageTransformer} that will be called for each attached page whenever
494
     * the scroll position is changed. This allows the application to apply custom property
495
     * transformations to each page, overriding the default sliding look and feel.
496
     * <p/>
497
     * <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did not exist.
498
     * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.</p>
499
     *
500
     * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
501
     *                            to be drawn from last to first instead of first to last.
502
     * @param transformer         PageTransformer that will modify each page's animation properties
503
     */
504
    public void setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer) {
505
        if (Build.VERSION.SDK_INT >= 11) {
506
            final boolean hasTransformer = transformer != null;
507
            final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
508
            mPageTransformer = transformer;
509
            setChildrenDrawingOrderEnabledCompat(hasTransformer);
510
            if (hasTransformer) {
511
                mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
512
            } else {
513
                mDrawingOrder = DRAW_ORDER_DEFAULT;
514
            }
515
            if (needsPopulate) populate();
516
        }
517
    }
518
519
    void setChildrenDrawingOrderEnabledCompat(boolean enable) {
520
        if (Build.VERSION.SDK_INT >= 7) {
521
            if (mSetChildrenDrawingOrderEnabled == null) {
522
                try {
523
                    mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(
524
                            "setChildrenDrawingOrderEnabled", new Class[]{Boolean.TYPE});
525
                } catch (NoSuchMethodException e) {
526
                    Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e);
527
                }
528
            }
529
            try {
530
                mSetChildrenDrawingOrderEnabled.invoke(this, enable);
531
            } catch (Exception e) {
532
                Log.e(TAG, "Error changing children drawing order", e);
533
            }
534
        }
535
    }
536
537
    @Override
538
    protected int getChildDrawingOrder(int childCount, int i) {
539
        final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;
540
        final int result = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;
541
        return result;
542
    }
543
544
    /**
545
     * Set a separate OnPageChangeListener for internal use by the support library.
546
     *
547
     * @param listener Listener to set
548
     * @return The old listener that was set, if any.
549
     */
550
    ViewPager.OnPageChangeListener setInternalPageChangeListener(ViewPager.OnPageChangeListener listener) {
551
        ViewPager.OnPageChangeListener oldListener = mInternalPageChangeListener;
552
        mInternalPageChangeListener = listener;
553
        return oldListener;
554
    }
555
556
    /**
557
     * Returns the number of pages that will be retained to either side of the
558
     * current page in the view hierarchy in an idle state. Defaults to 1.
559
     *
560
     * @return How many pages will be kept offscreen on either side
561
     * @see #setOffscreenPageLimit(int)
562
     */
563
    public int getOffscreenPageLimit() {
564
        return mOffscreenPageLimit;
565
    }
566
567
    /**
568
     * Set the number of pages that should be retained to either side of the
569
     * current page in the view hierarchy in an idle state. Pages beyond this
570
     * limit will be recreated from the adapter when needed.
571
     * <p/>
572
     * <p>This is offered as an optimization. If you know in advance the number
573
     * of pages you will need to support or have lazy-loading mechanisms in place
574
     * on your pages, tweaking this setting can have benefits in perceived smoothness
575
     * of paging animations and interaction. If you have a small number of pages (3-4)
576
     * that you can keep active all at once, less time will be spent in layout for
577
     * newly created view subtrees as the user pages back and forth.</p>
578
     * <p/>
579
     * <p>You should keep this limit low, especially if your pages have complex layouts.
580
     * This setting defaults to 1.</p>
581
     *
582
     * @param limit How many pages will be kept offscreen in an idle state.
583
     */
584
    public void setOffscreenPageLimit(int limit) {
585
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
586
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
587
                    DEFAULT_OFFSCREEN_PAGES);
588
            limit = DEFAULT_OFFSCREEN_PAGES;
589
        }
590
        if (limit != mOffscreenPageLimit) {
591
            mOffscreenPageLimit = limit;
592
            populate();
593
        }
594
    }
595
596
    /**
597
     * Set the margin between pages.
598
     *
599
     * @param marginPixels Distance between adjacent pages in pixels
600
     * @see #getPageMargin()
601
     * @see #setPageMarginDrawable(Drawable)
602
     * @see #setPageMarginDrawable(int)
603
     */
604
    public void setPageMargin(int marginPixels) {
605
        final int oldMargin = mPageMargin;
606
        mPageMargin = marginPixels;
607
608
        final int height = getHeight();
609
        recomputeScrollPosition(height, height, marginPixels, oldMargin);
610
611
        requestLayout();
612
    }
613
614
    /**
615
     * Return the margin between pages.
616
     *
617
     * @return The size of the margin in pixels
618
     */
619
    public int getPageMargin() {
620
        return mPageMargin;
621
    }
622
623
    /**
624
     * Set a drawable that will be used to fill the margin between pages.
625
     *
626
     * @param d Drawable to display between pages
627
     */
628
    public void setPageMarginDrawable(Drawable d) {
629
        mMarginDrawable = d;
630
        if (d != null) refreshDrawableState();
631
        setWillNotDraw(d == null);
632
        invalidate();
633
    }
634
635
    /**
636
     * Set a drawable that will be used to fill the margin between pages.
637
     *
638
     * @param resId Resource ID of a drawable to display between pages
639
     */
640
    public void setPageMarginDrawable(int resId) {
641
        setPageMarginDrawable(getContext().getResources().getDrawable(resId));
642
    }
643
644
    @Override
645
    protected boolean verifyDrawable(Drawable who) {
646
        return super.verifyDrawable(who) || who == mMarginDrawable;
647
    }
648
649
    @Override
650
    protected void drawableStateChanged() {
651
        super.drawableStateChanged();
652
        final Drawable d = mMarginDrawable;
653
        if (d != null && d.isStateful()) {
654
            d.setState(getDrawableState());
655
        }
656
    }
657
658
    // We want the duration of the page snap animation to be influenced by the distance that
659
    // the screen has to travel, however, we don't want this duration to be effected in a
660
    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
661
    // of travel has on the overall snap duration.
662
    float distanceInfluenceForSnapDuration(float f) {
663
        f -= 0.5f; // center the values about 0.
664
        f *= 0.3f * Math.PI / 2.0f;
665
        return (float) Math.sin(f);
666
    }
667
668
    /**
669
     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
670
     *
671
     * @param x the number of pixels to scroll by on the X axis
672
     * @param y the number of pixels to scroll by on the Y axis
673
     */
674
    void smoothScrollTo(int x, int y) {
675
        smoothScrollTo(x, y, 0);
676
    }
677
678
    /**
679
     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
680
     *
681
     * @param x        the number of pixels to scroll by on the X axis
682
     * @param y        the number of pixels to scroll by on the Y axis
683
     * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)
684
     */
685
    void smoothScrollTo(int x, int y, int velocity) {
686
        if (getChildCount() == 0) {
687
            // Nothing to do.
688
            setScrollingCacheEnabled(false);
689
            return;
690
        }
691
        int sx = getScrollX();
692
        int sy = getScrollY();
693
        int dx = x - sx;
694
        int dy = y - sy;
695
        if (dx == 0 && dy == 0) {
696
            completeScroll(false);
697
            populate();
698
            setScrollState(SCROLL_STATE_IDLE);
699
            return;
700
        }
701
702
        setScrollingCacheEnabled(true);
703
        setScrollState(SCROLL_STATE_SETTLING);
704
705
        final int height = getClientHeight();
706
        final int halfHeight = height / 2;
707
        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / height);
708
        final float distance = halfHeight + halfHeight *
709
                distanceInfluenceForSnapDuration(distanceRatio);
710
711
        int duration = 0;
712
        velocity = Math.abs(velocity);
713
        if (velocity > 0) {
714
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
715
        } else {
716
            final float pageHeight = height * mAdapter.getPageWidth(mCurItem);
717
            final float pageDelta = (float) Math.abs(dx) / (pageHeight + mPageMargin);
718
            duration = (int) ((pageDelta + 1) * 100);
719
        }
720
        duration = Math.min(duration, MAX_SETTLE_DURATION);
721
722
        mScroller.startScroll(sx, sy, dx, dy, duration);
723
        ViewCompat.postInvalidateOnAnimation(this);
724
    }
725
726
    ItemInfo addNewItem(int position, int index) {
727
        ItemInfo ii = new ItemInfo();
728
        ii.position = position;
729
        ii.object = mAdapter.instantiateItem(this, position);
730
        ii.heightFactor = mAdapter.getPageWidth(position);
731
        if (index < 0 || index >= mItems.size()) {
732
            mItems.add(ii);
733
        } else {
734
            mItems.add(index, ii);
735
        }
736
        return ii;
737
    }
738
739
    void dataSetChanged() {
740
        // This method only gets called if our observer is attached, so mAdapter is non-null.
741
742
        final int adapterCount = mAdapter.getCount();
743
        mExpectedAdapterCount = adapterCount;
744
        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 &&
745
                mItems.size() < adapterCount;
746
        int newCurrItem = mCurItem;
747
748
        boolean isUpdating = false;
749
        for (int i = 0; i < mItems.size(); i++) {
750
            final ItemInfo ii = mItems.get(i);
751
            final int newPos = mAdapter.getItemPosition(ii.object);
752
753
            if (newPos == PagerAdapter.POSITION_UNCHANGED) {
754
                continue;
755
            }
756
757
            if (newPos == PagerAdapter.POSITION_NONE) {
758
                mItems.remove(i);
759
                i--;
760
761
                if (!isUpdating) {
762
                    mAdapter.startUpdate(this);
763
                    isUpdating = true;
764
                }
765
766
                mAdapter.destroyItem(this, ii.position, ii.object);
767
                needPopulate = true;
768
769
                if (mCurItem == ii.position) {
770
                    // Keep the current item in the valid range
771
                    newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
772
                    needPopulate = true;
773
                }
774
                continue;
775
            }
776
777
            if (ii.position != newPos) {
778
                if (ii.position == mCurItem) {
779
                    // Our current item changed position. Follow it.
780
                    newCurrItem = newPos;
781
                }
782
783
                ii.position = newPos;
784
                needPopulate = true;
785
            }
786
        }
787
788
        if (isUpdating) {
789
            mAdapter.finishUpdate(this);
790
        }
791
792
        Collections.sort(mItems, COMPARATOR);
793
794
        if (needPopulate) {
795
            // Reset our known page widths; populate will recompute them.
796
            final int childCount = getChildCount();
797
            for (int i = 0; i < childCount; i++) {
798
                final View child = getChildAt(i);
799
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
800
                if (!lp.isDecor) {
801
                    lp.heightFactor = 0.f;
802
                }
803
            }
804
805
            setCurrentItemInternal(newCurrItem, false, true);
806
            requestLayout();
807
        }
808
    }
809
810
    void populate() {
811
        populate(mCurItem);
812
    }
813
814
    void populate(int newCurrentItem) {
815
        ItemInfo oldCurInfo = null;
816
        int focusDirection = View.FOCUS_FORWARD;
817
        if (mCurItem != newCurrentItem) {
818
            focusDirection = mCurItem < newCurrentItem ? View.FOCUS_DOWN : View.FOCUS_UP;
819
            oldCurInfo = infoForPosition(mCurItem);
820
            mCurItem = newCurrentItem;
821
        }
822
823
        if (mAdapter == null) {
824
            sortChildDrawingOrder();
825
            return;
826
        }
827
828
        // Bail now if we are waiting to populate.  This is to hold off
829
        // on creating views from the time the user releases their finger to
830
        // fling to a new position until we have finished the scroll to
831
        // that position, avoiding glitches from happening at that point.
832
        if (mPopulatePending) {
833
            if (DEBUG) {
834
                Log.i(TAG, "populate is pending, skipping for now...");
835
            }
836
            sortChildDrawingOrder();
837
            return;
838
        }
839
840
        // Also, don't populate until we are attached to a window.  This is to
841
        // avoid trying to populate before we have restored our view hierarchy
842
        // state and conflicting with what is restored.
843
        if (getWindowToken() == null) {
844
            return;
845
        }
846
847
        mAdapter.startUpdate(this);
848
849
        final int pageLimit = mOffscreenPageLimit;
850
        final int startPos = Math.max(0, mCurItem - pageLimit);
851
        final int N = mAdapter.getCount();
852
        final int endPos = Math.min(N - 1, mCurItem + pageLimit);
853
854
        if (N != mExpectedAdapterCount) {
855
            String resName;
856
            try {
857
                resName = getResources().getResourceName(getId());
858
            } catch (Resources.NotFoundException e) {
859
                resName = Integer.toHexString(getId());
860
            }
861
            throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +
862
                    " contents without calling PagerAdapter#notifyDataSetChanged!" +
863
                    " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +
864
                    " Pager id: " + resName +
865
                    " Pager class: " + getClass() +
866
                    " Problematic adapter: " + mAdapter.getClass());
867
        }
868
869
        // Locate the currently focused item or add it if needed.
870
        int curIndex = -1;
871
        ItemInfo curItem = null;
872
        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
873
            final ItemInfo ii = mItems.get(curIndex);
874
            if (ii.position >= mCurItem) {
875
                if (ii.position == mCurItem) curItem = ii;
876
                break;
877
            }
878
        }
879
880
        if (curItem == null && N > 0) {
881
            curItem = addNewItem(mCurItem, curIndex);
882
        }
883
884
        // Fill 3x the available width or up to the number of offscreen
885
        // pages requested to either side, whichever is larger.
886
        // If we have no current item we have no work to do.
887
        if (curItem != null) {
888
            float extraHeightTop = 0.f;
889
            int itemIndex = curIndex - 1;
890
            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
891
            final int clientHeight = getClientHeight();
892
            final float topHeightNeeded = clientHeight <= 0 ? 0 :
893
                    2.f - curItem.heightFactor + (float) getPaddingLeft() / (float) clientHeight;
894
            for (int pos = mCurItem - 1; pos >= 0; pos--) {
895
                if (extraHeightTop >= topHeightNeeded && pos < startPos) {
896
                    if (ii == null) {
897
                        break;
898
                    }
899
                    if (pos == ii.position && !ii.scrolling) {
900
                        mItems.remove(itemIndex);
901
                        mAdapter.destroyItem(this, pos, ii.object);
902
                        if (DEBUG) {
903
                            Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
904
                                    " view: " + ((View) ii.object));
905
                        }
906
                        itemIndex--;
907
                        curIndex--;
908
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
909
                    }
910
                } else if (ii != null && pos == ii.position) {
911
                    extraHeightTop += ii.heightFactor;
912
                    itemIndex--;
913
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
914
                } else {
915
                    ii = addNewItem(pos, itemIndex + 1);
916
                    extraHeightTop += ii.heightFactor;
917
                    curIndex++;
918
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
919
                }
920
            }
921
922
            float extraHeightBottom = curItem.heightFactor;
923
            itemIndex = curIndex + 1;
924
            if (extraHeightBottom < 2.f) {
925
                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
926
                final float bottomHeightNeeded = clientHeight <= 0 ? 0 :
927
                        (float) getPaddingRight() / (float) clientHeight + 2.f;
928
                for (int pos = mCurItem + 1; pos < N; pos++) {
929
                    if (extraHeightBottom >= bottomHeightNeeded && pos > endPos) {
930
                        if (ii == null) {
931
                            break;
932
                        }
933
                        if (pos == ii.position && !ii.scrolling) {
934
                            mItems.remove(itemIndex);
935
                            mAdapter.destroyItem(this, pos, ii.object);
936
                            if (DEBUG) {
937
                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
938
                                        " view: " + ((View) ii.object));
939
                            }
940
                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
941
                        }
942
                    } else if (ii != null && pos == ii.position) {
943
                        extraHeightBottom += ii.heightFactor;
944
                        itemIndex++;
945
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
946
                    } else {
947
                        ii = addNewItem(pos, itemIndex);
948
                        itemIndex++;
949
                        extraHeightBottom += ii.heightFactor;
950
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
951
                    }
952
                }
953
            }
954
955
            calculatePageOffsets(curItem, curIndex, oldCurInfo);
956
        }
957
958
        if (DEBUG) {
959
            Log.i(TAG, "Current page list:");
960
            for (int i = 0; i < mItems.size(); i++) {
961
                Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
962
            }
963
        }
964
965
        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
966
967
        mAdapter.finishUpdate(this);
968
969
        // Check width measurement of current pages and drawing sort order.
970
        // Update LayoutParams as needed.
971
        final int childCount = getChildCount();
972
        for (int i = 0; i < childCount; i++) {
973
            final View child = getChildAt(i);
974
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
975
            lp.childIndex = i;
976
            if (!lp.isDecor && lp.heightFactor == 0.f) {
977
                // 0 means requery the adapter for this, it doesn't have a valid width.
978
                final ItemInfo ii = infoForChild(child);
979
                if (ii != null) {
980
                    lp.heightFactor = ii.heightFactor;
981
                    lp.position = ii.position;
982
                }
983
            }
984
        }
985
        sortChildDrawingOrder();
986
987
        if (hasFocus()) {
988
            View currentFocused = findFocus();
989
            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
990
            if (ii == null || ii.position != mCurItem) {
991
                for (int i = 0; i < getChildCount(); i++) {
992
                    View child = getChildAt(i);
993
                    ii = infoForChild(child);
994
                    if (ii != null && ii.position == mCurItem) {
995
                        if (child.requestFocus(focusDirection)) {
996
                            break;
997
                        }
998
                    }
999
                }
1000
            }
1001
        }
1002
    }
1003
1004
    private void sortChildDrawingOrder() {
1005
        if (mDrawingOrder != DRAW_ORDER_DEFAULT) {
1006
            if (mDrawingOrderedChildren == null) {
1007
                mDrawingOrderedChildren = new ArrayList<View>();
1008
            } else {
1009
                mDrawingOrderedChildren.clear();
1010
            }
1011
            final int childCount = getChildCount();
1012
            for (int i = 0; i < childCount; i++) {
1013
                final View child = getChildAt(i);
1014
                mDrawingOrderedChildren.add(child);
1015
            }
1016
            Collections.sort(mDrawingOrderedChildren, sPositionComparator);
1017
        }
1018
    }
1019
1020
    private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
1021
        final int N = mAdapter.getCount();
1022
        final int height = getClientHeight();
1023
        final float marginOffset = height > 0 ? (float) mPageMargin / height : 0;
1024
        // Fix up offsets for later layout.
1025
        if (oldCurInfo != null) {
1026
            final int oldCurPosition = oldCurInfo.position;
1027
            // Base offsets off of oldCurInfo.
1028
            if (oldCurPosition < curItem.position) {
1029
                int itemIndex = 0;
1030
                ItemInfo ii = null;
1031
                float offset = oldCurInfo.offset + oldCurInfo.heightFactor + marginOffset;
1032
                for (int pos = oldCurPosition + 1;
1033
                     pos <= curItem.position && itemIndex < mItems.size(); pos++) {
1034
                    ii = mItems.get(itemIndex);
1035
                    while (pos > ii.position && itemIndex < mItems.size() - 1) {
1036
                        itemIndex++;
1037
                        ii = mItems.get(itemIndex);
1038
                    }
1039
                    while (pos < ii.position) {
1040
                        // We don't have an item populated for this,
1041
                        // ask the adapter for an offset.
1042
                        offset += mAdapter.getPageWidth(pos) + marginOffset;
1043
                        pos++;
1044
                    }
1045
                    ii.offset = offset;
1046
                    offset += ii.heightFactor + marginOffset;
1047
                }
1048
            } else if (oldCurPosition > curItem.position) {
1049
                int itemIndex = mItems.size() - 1;
1050
                ItemInfo ii = null;
1051
                float offset = oldCurInfo.offset;
1052
                for (int pos = oldCurPosition - 1;
1053
                     pos >= curItem.position && itemIndex >= 0; pos--) {
1054
                    ii = mItems.get(itemIndex);
1055
                    while (pos < ii.position && itemIndex > 0) {
1056
                        itemIndex--;
1057
                        ii = mItems.get(itemIndex);
1058
                    }
1059
                    while (pos > ii.position) {
1060
                        // We don't have an item populated for this,
1061
                        // ask the adapter for an offset.
1062
                        offset -= mAdapter.getPageWidth(pos) + marginOffset;
1063
                        pos--;
1064
                    }
1065
                    offset -= ii.heightFactor + marginOffset;
1066
                    ii.offset = offset;
1067
                }
1068
            }
1069
        }
1070
1071
        // Base all offsets off of curItem.
1072
        final int itemCount = mItems.size();
1073
        float offset = curItem.offset;
1074
        int pos = curItem.position - 1;
1075
        mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
1076
        mLastOffset = curItem.position == N - 1 ?
1077
                curItem.offset + curItem.heightFactor - 1 : Float.MAX_VALUE;
1078
        // Previous pages
1079
        for (int i = curIndex - 1; i >= 0; i--, pos--) {
1080
            final ItemInfo ii = mItems.get(i);
1081
            while (pos > ii.position) {
1082
                offset -= mAdapter.getPageWidth(pos--) + marginOffset;
1083
            }
1084
            offset -= ii.heightFactor + marginOffset;
1085
            ii.offset = offset;
1086
            if (ii.position == 0) {
1087
                mFirstOffset = offset;
1088
            }
1089
        }
1090
        offset = curItem.offset + curItem.heightFactor + marginOffset;
1091
        pos = curItem.position + 1;
1092
        // Next pages
1093
        for (int i = curIndex + 1; i < itemCount; i++, pos++) {
1094
            final ItemInfo ii = mItems.get(i);
1095
            while (pos < ii.position) {
1096
                offset += mAdapter.getPageWidth(pos++) + marginOffset;
1097
            }
1098
            if (ii.position == N - 1) {
1099
                mLastOffset = offset + ii.heightFactor - 1;
1100
            }
1101
            ii.offset = offset;
1102
            offset += ii.heightFactor + marginOffset;
1103
        }
1104
1105
        mNeedCalculatePageOffsets = false;
1106
    }
1107
1108
    /**
1109
     * This is the persistent state that is saved by ViewPager.  Only needed
1110
     * if you are creating a sublass of ViewPager that must save its own
1111
     * state, in which case it should implement a subclass of this which
1112
     * contains that state.
1113
     */
1114
    public static class SavedState extends BaseSavedState {
1115
        int position;
1116
        Parcelable adapterState;
1117
        ClassLoader loader;
1118
1119
        public SavedState(Parcelable superState) {
1120
            super(superState);
1121
        }
1122
1123
        @Override
1124
        public void writeToParcel(Parcel out, int flags) {
1125
            super.writeToParcel(out, flags);
1126
            out.writeInt(position);
1127
            out.writeParcelable(adapterState, flags);
1128
        }
1129
1130
        @Override
1131
        public String toString() {
1132
            return "FragmentPager.SavedState{"
1133
                    + Integer.toHexString(System.identityHashCode(this))
1134
                    + " position=" + position + "}";
1135
        }
1136
1137
        public static final Creator<SavedState> CREATOR
1138
                = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
1139
            @Override
1140
            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
1141
                return new SavedState(in, loader);
1142
            }
1143
1144
            @Override
1145
            public SavedState[] newArray(int size) {
1146
                return new SavedState[size];
1147
            }
1148
        });
1149
1150
        SavedState(Parcel in, ClassLoader loader) {
1151
            super(in);
1152
            if (loader == null) {
1153
                loader = getClass().getClassLoader();
1154
            }
1155
            position = in.readInt();
1156
            adapterState = in.readParcelable(loader);
1157
            this.loader = loader;
1158
        }
1159
    }
1160
1161
    @Override
1162
    public Parcelable onSaveInstanceState() {
1163
        Parcelable superState = super.onSaveInstanceState();
1164
        SavedState ss = new SavedState(superState);
1165
        ss.position = mCurItem;
1166
        if (mAdapter != null) {
1167
            ss.adapterState = mAdapter.saveState();
1168
        }
1169
        return ss;
1170
    }
1171
1172
    @Override
1173
    public void onRestoreInstanceState(Parcelable state) {
1174
        if (!(state instanceof SavedState)) {
1175
            super.onRestoreInstanceState(state);
1176
            return;
1177
        }
1178
1179
        SavedState ss = (SavedState) state;
1180
        super.onRestoreInstanceState(ss.getSuperState());
1181
1182
        if (mAdapter != null) {
1183
            mAdapter.restoreState(ss.adapterState, ss.loader);
1184
            setCurrentItemInternal(ss.position, false, true);
1185
        } else {
1186
            mRestoredCurItem = ss.position;
1187
            mRestoredAdapterState = ss.adapterState;
1188
            mRestoredClassLoader = ss.loader;
1189
        }
1190
    }
1191
1192
    @Override
1193
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
1194
        if (!checkLayoutParams(params)) {
1195
            params = generateLayoutParams(params);
1196
        }
1197
        final LayoutParams lp = (LayoutParams) params;
1198
        lp.isDecor |= child instanceof Decor;
1199
        if (mInLayout) {
1200
            if (lp != null && lp.isDecor) {
1201
                throw new IllegalStateException("Cannot add pager decor view during layout");
1202
            }
1203
            lp.needsMeasure = true;
1204
            addViewInLayout(child, index, params);
1205
        } else {
1206
            super.addView(child, index, params);
1207
        }
1208
1209
        if (USE_CACHE) {
1210
            if (child.getVisibility() != GONE) {
1211
                child.setDrawingCacheEnabled(mScrollingCacheEnabled);
1212
            } else {
1213
                child.setDrawingCacheEnabled(false);
1214
            }
1215
        }
1216
    }
1217
1218
    @Override
1219
    public void removeView(View view) {
1220
        if (mInLayout) {
1221
            removeViewInLayout(view);
1222
        } else {
1223
            super.removeView(view);
1224
        }
1225
    }
1226
1227
    ItemInfo infoForChild(View child) {
1228
        for (int i = 0; i < mItems.size(); i++) {
1229
            ItemInfo ii = mItems.get(i);
1230
            if (mAdapter.isViewFromObject(child, ii.object)) {
1231
                return ii;
1232
            }
1233
        }
1234
        return null;
1235
    }
1236
1237
    ItemInfo infoForAnyChild(View child) {
1238
        ViewParent parent;
1239
        while ((parent = child.getParent()) != this) {
1240
            if (parent == null || !(parent instanceof View)) {
1241
                return null;
1242
            }
1243
            child = (View) parent;
1244
        }
1245
        return infoForChild(child);
1246
    }
1247
1248
    ItemInfo infoForPosition(int position) {
1249
        for (int i = 0; i < mItems.size(); i++) {
1250
            ItemInfo ii = mItems.get(i);
1251
            if (ii.position == position) {
1252
                return ii;
1253
            }
1254
        }
1255
        return null;
1256
    }
1257
1258
    @Override
1259
    protected void onAttachedToWindow() {
1260
        super.onAttachedToWindow();
1261
        mFirstLayout = true;
1262
    }
1263
1264
    @Override
1265
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1266
        // For simple implementation, our internal size is always 0.
1267
        // We depend on the container to specify the layout size of
1268
        // our view.  We can't really know what it is since we will be
1269
        // adding and removing different arbitrary views and do not
1270
        // want the layout to change as this happens.
1271
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
1272
                getDefaultSize(0, heightMeasureSpec));
1273
1274
        final int measuredHeight = getMeasuredHeight();
1275
        final int maxGutterSize = measuredHeight / 10;
1276
        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);
1277
1278
        // Children are just made to fill our space.
1279
        int childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
1280
        int childHeightSize = measuredHeight - getPaddingTop() - getPaddingBottom();
1281
1282
        /*
1283
         * Make sure all children have been properly measured. Decor views first.
1284
         * Right now we cheat and make this less complicated by assuming decor
1285
         * views won't intersect. We will pin to edges based on gravity.
1286
         */
1287
        int size = getChildCount();
1288
        for (int i = 0; i < size; ++i) {
1289
            final View child = getChildAt(i);
1290
            if (child.getVisibility() != GONE) {
1291
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1292
                if (lp != null && lp.isDecor) {
1293
                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1294
                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
1295
                    int widthMode = MeasureSpec.AT_MOST;
1296
                    int heightMode = MeasureSpec.AT_MOST;
1297
                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
1298
                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;
1299
1300
                    if (consumeVertical) {
1301
                        widthMode = MeasureSpec.EXACTLY;
1302
                    } else if (consumeHorizontal) {
1303
                        heightMode = MeasureSpec.EXACTLY;
1304
                    }
1305
1306
                    int widthSize = childWidthSize;
1307
                    int heightSize = childHeightSize;
1308
                    if (lp.width != LayoutParams.WRAP_CONTENT) {
1309
                        widthMode = MeasureSpec.EXACTLY;
1310
                        if (lp.width != LayoutParams.FILL_PARENT) {
1311
                            widthSize = lp.width;
1312
                        }
1313
                    }
1314
                    if (lp.height != LayoutParams.WRAP_CONTENT) {
1315
                        heightMode = MeasureSpec.EXACTLY;
1316
                        if (lp.height != LayoutParams.FILL_PARENT) {
1317
                            heightSize = lp.height;
1318
                        }
1319
                    }
1320
                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
1321
                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
1322
                    child.measure(widthSpec, heightSpec);
1323
1324
                    if (consumeVertical) {
1325
                        childHeightSize -= child.getMeasuredHeight();
1326
                    } else if (consumeHorizontal) {
1327
                        childWidthSize -= child.getMeasuredWidth();
1328
                    }
1329
                }
1330
            }
1331
        }
1332
1333
        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
1334
        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);
1335
1336
        // Make sure we have created all fragments that we need to have shown.
1337
        mInLayout = true;
1338
        populate();
1339
        mInLayout = false;
1340
1341
        // Page views next.
1342
        size = getChildCount();
1343
        for (int i = 0; i < size; ++i) {
1344
            final View child = getChildAt(i);
1345
            if (child.getVisibility() != GONE) {
1346
                if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child
1347
                        + ": " + mChildWidthMeasureSpec);
1348
1349
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1350
                if (lp == null || !lp.isDecor) {
1351
                    final int heightSpec = MeasureSpec.makeMeasureSpec(
1352
                            (int) (childHeightSize * lp.heightFactor), MeasureSpec.EXACTLY);
1353
                    child.measure(mChildWidthMeasureSpec, heightSpec);
1354
                }
1355
            }
1356
        }
1357
    }
1358
1359
    @Override
1360
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1361
        super.onSizeChanged(w, h, oldw, oldh);
1362
1363
        // Make sure scroll position is set correctly.
1364
        if (h != oldh) {
1365
            recomputeScrollPosition(h, oldh, mPageMargin, mPageMargin);
1366
        }
1367
    }
1368
1369
    private void recomputeScrollPosition(int height, int oldHeight, int margin, int oldMargin) {
1370
        if (oldHeight > 0 && !mItems.isEmpty()) {
1371
            final int heightWithMargin = height - getPaddingTop() - getPaddingBottom() + margin;
1372
            final int oldHeightWithMargin = oldHeight - getPaddingTop() - getPaddingBottom()
1373
                    + oldMargin;
1374
            final int ypos = getScrollY();
1375
            final float pageOffset = (float) ypos / oldHeightWithMargin;
1376
            final int newOffsetPixels = (int) (pageOffset * heightWithMargin);
1377
1378
            scrollTo(getScrollX(), newOffsetPixels);
1379
            if (!mScroller.isFinished()) {
1380
                // We now return to your regularly scheduled scroll, already in progress.
1381
                final int newDuration = mScroller.getDuration() - mScroller.timePassed();
1382
                ItemInfo targetInfo = infoForPosition(mCurItem);
1383
                mScroller.startScroll(0, newOffsetPixels,
1384
                        0, (int) (targetInfo.offset * height), newDuration);
1385
            }
1386
        } else {
1387
            final ItemInfo ii = infoForPosition(mCurItem);
1388
            final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;
1389
            final int scrollPos = (int) (scrollOffset *
1390
                    (height - getPaddingTop() - getPaddingBottom()));
1391
            if (scrollPos != getScrollY()) {
1392
                completeScroll(false);
1393
                scrollTo(getScrollX(), scrollPos);
1394
            }
1395
        }
1396
    }
1397
1398
    @Override
1399
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1400
        final int count = getChildCount();
1401
        int width = r - l;
1402
        int height = b - t;
1403
        int paddingLeft = getPaddingLeft();
1404
        int paddingTop = getPaddingTop();
1405
        int paddingRight = getPaddingRight();
1406
        int paddingBottom = getPaddingBottom();
1407
        final int scrollY = getScrollY();
1408
1409
        int decorCount = 0;
1410
1411
        // First pass - decor views. We need to do this in two passes so that
1412
        // we have the proper offsets for non-decor views later.
1413
        for (int i = 0; i < count; i++) {
1414
            final View child = getChildAt(i);
1415
            if (child.getVisibility() != GONE) {
1416
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1417
                int childLeft = 0;
1418
                int childTop = 0;
1419
                if (lp.isDecor) {
1420
                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1421
                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
1422
                    switch (hgrav) {
1423
                        default:
1424
                            childLeft = paddingLeft;
1425
                            break;
1426
                        case Gravity.LEFT:
1427
                            childLeft = paddingLeft;
1428
                            paddingLeft += child.getMeasuredWidth();
1429
                            break;
1430
                        case Gravity.CENTER_HORIZONTAL:
1431
                            childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
1432
                                    paddingLeft);
1433
                            break;
1434
                        case Gravity.RIGHT:
1435
                            childLeft = width - paddingRight - child.getMeasuredWidth();
1436
                            paddingRight += child.getMeasuredWidth();
1437
                            break;
1438
                    }
1439
                    switch (vgrav) {
1440
                        default:
1441
                            childTop = paddingTop;
1442
                            break;
1443
                        case Gravity.TOP:
1444
                            childTop = paddingTop;
1445
                            paddingTop += child.getMeasuredHeight();
1446
                            break;
1447
                        case Gravity.CENTER_VERTICAL:
1448
                            childTop = Math.max((height - child.getMeasuredHeight()) / 2,
1449
                                    paddingTop);
1450
                            break;
1451
                        case Gravity.BOTTOM:
1452
                            childTop = height - paddingBottom - child.getMeasuredHeight();
1453
                            paddingBottom += child.getMeasuredHeight();
1454
                            break;
1455
                    }
1456
                    childTop += scrollY;
1457
                    child.layout(childLeft, childTop,
1458
                            childLeft + child.getMeasuredWidth(),
1459
                            childTop + child.getMeasuredHeight());
1460
                    decorCount++;
1461
                }
1462
            }
1463
        }
1464
1465
        final int childHeight = height - paddingTop - paddingBottom;
1466
        // Page views. Do this once we have the right padding offsets from above.
1467
        for (int i = 0; i < count; i++) {
1468
            final View child = getChildAt(i);
1469
            if (child.getVisibility() != GONE) {
1470
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1471
                ItemInfo ii;
1472
                if (!lp.isDecor && (ii = infoForChild(child)) != null) {
1473
                    int toff = (int) (childHeight * ii.offset);
1474
                    int childLeft = paddingLeft;
1475
                    int childTop = paddingTop + toff;
1476
                    if (lp.needsMeasure) {
1477
                        // This was added during layout and needs measurement.
1478
                        // Do it now that we know what we're working with.
1479
                        lp.needsMeasure = false;
1480
                        final int widthSpec = MeasureSpec.makeMeasureSpec(
1481
                                (int) (width - paddingLeft - paddingRight),
1482
                                MeasureSpec.EXACTLY);
1483
                        final int heightSpec = MeasureSpec.makeMeasureSpec(
1484
                                (int) (childHeight * lp.heightFactor),
1485
                                MeasureSpec.EXACTLY);
1486
                        child.measure(widthSpec, heightSpec);
1487
                    }
1488
                    if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
1489
                            + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
1490
                            + "x" + child.getMeasuredHeight());
1491
                    child.layout(childLeft, childTop,
1492
                            childLeft + child.getMeasuredWidth(),
1493
                            childTop + child.getMeasuredHeight());
1494
                }
1495
            }
1496
        }
1497
        mLeftPageBounds = paddingLeft;
1498
        mRightPageBounds = width - paddingRight;
1499
        mDecorChildCount = decorCount;
1500
1501
        if (mFirstLayout) {
1502
            scrollToItem(mCurItem, false, 0, false);
1503
        }
1504
        mFirstLayout = false;
1505
    }
1506
1507
    @Override
1508
    public void computeScroll() {
1509
        if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
1510
            int oldX = getScrollX();
1511
            int oldY = getScrollY();
1512
            int x = mScroller.getCurrX();
1513
            int y = mScroller.getCurrY();
1514
1515
            if (oldX != x || oldY != y) {
1516
                scrollTo(x, y);
1517
                if (!pageScrolled(y)) {
1518
                    mScroller.abortAnimation();
1519
                    scrollTo(x, 0);
1520
                }
1521
            }
1522
1523
            // Keep on drawing until the animation has finished.
1524
            ViewCompat.postInvalidateOnAnimation(this);
1525
            return;
1526
        }
1527
1528
        // Done with scroll, clean up state.
1529
        completeScroll(true);
1530
    }
1531
1532
    private boolean pageScrolled(int ypos) {
1533
        if (mItems.size() == 0) {
1534
            mCalledSuper = false;
1535
            onPageScrolled(0, 0, 0);
1536
            if (!mCalledSuper) {
1537
                throw new IllegalStateException(
1538
                        "onPageScrolled did not call superclass implementation");
1539
            }
1540
            return false;
1541
        }
1542
        final ItemInfo ii = infoForCurrentScrollPosition();
1543
        final int height = getClientHeight();
1544
        final int heightWithMargin = height + mPageMargin;
1545
        final float marginOffset = (float) mPageMargin / height;
1546
        final int currentPage = ii.position;
1547
        final float pageOffset = (((float) ypos / height) - ii.offset) /
1548
                (ii.heightFactor + marginOffset);
1549
        final int offsetPixels = (int) (pageOffset * heightWithMargin);
1550
1551
        mCalledSuper = false;
1552
        onPageScrolled(currentPage, pageOffset, offsetPixels);
1553
        if (!mCalledSuper) {
1554
            throw new IllegalStateException(
1555
                    "onPageScrolled did not call superclass implementation");
1556
        }
1557
        return true;
1558
    }
1559
1560
    /**
1561
     * This method will be invoked when the current page is scrolled, either as part
1562
     * of a programmatically initiated smooth scroll or a user initiated touch scroll.
1563
     * If you override this method you must call through to the superclass implementation
1564
     * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
1565
     * returns.
1566
     *
1567
     * @param position     Position index of the first page currently being displayed.
1568
     *                     Page position+1 will be visible if positionOffset is nonzero.
1569
     * @param offset       Value from [0, 1) indicating the offset from the page at position.
1570
     * @param offsetPixels Value in pixels indicating the offset from position.
1571
     */
1572
    protected void onPageScrolled(int position, float offset, int offsetPixels) {
1573
        // Offset any decor views if needed - keep them on-screen at all times.
1574
        if (mDecorChildCount > 0) {
1575
            final int scrollY = getScrollY();
1576
            int paddingTop = getPaddingTop();
1577
            int paddingBottom = getPaddingBottom();
1578
            final int height = getHeight();
1579
            final int childCount = getChildCount();
1580
            for (int i = 0; i < childCount; i++) {
1581
                final View child = getChildAt(i);
1582
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1583
                if (!lp.isDecor) continue;
1584
1585
                final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
1586
                int childTop = 0;
1587
                switch (vgrav) {
1588
                    default:
1589
                        childTop = paddingTop;
1590
                        break;
1591
                    case Gravity.TOP:
1592
                        childTop = paddingTop;
1593
                        paddingTop += child.getHeight();
1594
                        break;
1595
                    case Gravity.CENTER_VERTICAL:
1596
                        childTop = Math.max((height - child.getMeasuredHeight()) / 2,
1597
                                paddingTop);
1598
                        break;
1599
                    case Gravity.BOTTOM:
1600
                        childTop = height - paddingBottom - child.getMeasuredHeight();
1601
                        paddingBottom += child.getMeasuredHeight();
1602
                        break;
1603
                }
1604
                childTop += scrollY;
1605
1606
                final int childOffset = childTop - child.getTop();
1607
                if (childOffset != 0) {
1608
                    child.offsetTopAndBottom(childOffset);
1609
                }
1610
            }
1611
        }
1612
1613
        if (mOnPageChangeListener != null) {
1614
            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
1615
        }
1616
        if (mInternalPageChangeListener != null) {
1617
            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
1618
        }
1619
1620
        if (mPageTransformer != null) {
1621
            final int scrollY = getScrollY();
1622
            final int childCount = getChildCount();
1623
            for (int i = 0; i < childCount; i++) {
1624
                final View child = getChildAt(i);
1625
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1626
1627
                if (lp.isDecor) continue;
1628
1629
                final float transformPos = (float) (child.getTop() - scrollY) / getClientHeight();
1630
                mPageTransformer.transformPage(child, transformPos);
1631
            }
1632
        }
1633
1634
        mCalledSuper = true;
1635
    }
1636
1637
    private void completeScroll(boolean postEvents) {
1638
        boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
1639
        if (needPopulate) {
1640
            // Done with scroll, no longer want to cache view drawing.
1641
            setScrollingCacheEnabled(false);
1642
            mScroller.abortAnimation();
1643
            int oldX = getScrollX();
1644
            int oldY = getScrollY();
1645
            int x = mScroller.getCurrX();
1646
            int y = mScroller.getCurrY();
1647
            if (oldX != x || oldY != y) {
1648
                scrollTo(x, y);
1649
            }
1650
        }
1651
        mPopulatePending = false;
1652
        for (int i = 0; i < mItems.size(); i++) {
1653
            ItemInfo ii = mItems.get(i);
1654
            if (ii.scrolling) {
1655
                needPopulate = true;
1656
                ii.scrolling = false;
1657
            }
1658
        }
1659
        if (needPopulate) {
1660
            if (postEvents) {
1661
                ViewCompat.postOnAnimation(this, mEndScrollRunnable);
1662
            } else {
1663
                mEndScrollRunnable.run();
1664
            }
1665
        }
1666
    }
1667
1668
    private boolean isGutterDrag(float y, float dy) {
1669
        return (y < mGutterSize && dy > 0) || (y > getHeight() - mGutterSize && dy < 0);
1670
    }
1671
1672
    private void enableLayers(boolean enable) {
1673
        final int childCount = getChildCount();
1674
        for (int i = 0; i < childCount; i++) {
1675
            final int layerType = enable ?
1676
                    ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;
1677
            ViewCompat.setLayerType(getChildAt(i), layerType, null);
1678
        }
1679
    }
1680
1681
    @Override
1682
    public boolean onInterceptTouchEvent(MotionEvent ev) {
1683
        /*
1684
         * This method JUST determines whether we want to intercept the motion.
1685
         * If we return true, onMotionEvent will be called and we do the actual
1686
         * scrolling there.
1687
         */
1688
1689
        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
1690
1691
        // Always take care of the touch gesture being complete.
1692
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
1693
            // Release the drag.
1694
            if (DEBUG) Log.v(TAG, "Intercept done!");
1695
            mIsBeingDragged = false;
1696
            mIsUnableToDrag = false;
1697
            mActivePointerId = INVALID_POINTER;
1698
            if (mVelocityTracker != null) {
1699
                mVelocityTracker.recycle();
1700
                mVelocityTracker = null;
1701
            }
1702
            return false;
1703
        }
1704
1705
        // Nothing more to do here if we have decided whether or not we
1706
        // are dragging.
1707
        if (action != MotionEvent.ACTION_DOWN) {
1708
            if (mIsBeingDragged) {
1709
                if (DEBUG) Log.v(TAG, "Intercept returning true!");
1710
                return true;
1711
            }
1712
            if (mIsUnableToDrag) {
1713
                if (DEBUG) Log.v(TAG, "Intercept returning false!");
1714
                return false;
1715
            }
1716
        }
1717
1718
        switch (action) {
1719
            case MotionEvent.ACTION_MOVE: {
1720
                /*
1721
                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
1722
                 * whether the user has moved far enough from his original down touch.
1723
                 */
1724
1725
                /*
1726
                 * Locally do absolute value. mLastMotionY is set to the y value
1727
                 * of the down event.
1728
                 */
1729
                final int activePointerId = mActivePointerId;
1730
                if (activePointerId == INVALID_POINTER) {
1731
                    // If we don't have a valid id, the touch down wasn't on content.
1732
                    break;
1733
                }
1734
1735
                final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
1736
                final float y = MotionEventCompat.getY(ev, pointerIndex);
1737
                final float dy = y - mLastMotionY;
1738
                final float yDiff = Math.abs(dy);
1739
                final float x = MotionEventCompat.getX(ev, pointerIndex);
1740
                final float xDiff = Math.abs(x - mInitialMotionX);
1741
                if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
1742
1743
                if (dy != 0 && !isGutterDrag(mLastMotionY, dy) &&
1744
                        canScroll(this, false, (int) dy, (int) x, (int) y)) {
1745
                    // Nested view has scrollable area under this point. Let it be handled there.
1746
                    mLastMotionX = x;
1747
                    mLastMotionY = y;
1748
                    mIsUnableToDrag = true;
1749
                    return false;
1750
                }
1751
                if (yDiff > mTouchSlop && yDiff * 0.5f > xDiff) {
1752
                    if (DEBUG) Log.v(TAG, "Starting drag!");
1753
                    mIsBeingDragged = true;
1754
                    requestParentDisallowInterceptTouchEvent(true);
1755
                    setScrollState(SCROLL_STATE_DRAGGING);
1756
                    mLastMotionY = dy > 0 ? mInitialMotionY + mTouchSlop :
1757
                            mInitialMotionY - mTouchSlop;
1758
                    mLastMotionX = x;
1759
                    setScrollingCacheEnabled(true);
1760
                } else if (xDiff > mTouchSlop) {
1761
                    // The finger has moved enough in the vertical
1762
                    // direction to be counted as a drag...  abort
1763
                    // any attempt to drag horizontally, to work correctly
1764
                    // with children that have scrolling containers.
1765
                    if (DEBUG) Log.v(TAG, "Starting unable to drag!");
1766
                    mIsUnableToDrag = true;
1767
                }
1768
                if (mIsBeingDragged) {
1769
                    // Scroll to follow the motion event
1770
                    if (performDrag(y)) {
1771
                        ViewCompat.postInvalidateOnAnimation(this);
1772
                    }
1773
                }
1774
                break;
1775
            }
1776
1777
            case MotionEvent.ACTION_DOWN: {
1778
                /*
1779
                 * Remember location of down touch.
1780
                 * ACTION_DOWN always refers to pointer index 0.
1781
                 */
1782
                mLastMotionX = mInitialMotionX = ev.getX();
1783
                mLastMotionY = mInitialMotionY = ev.getY();
1784
                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
1785
                mIsUnableToDrag = false;
1786
1787
                mScroller.computeScrollOffset();
1788
                if (mScrollState == SCROLL_STATE_SETTLING &&
1789
                        Math.abs(mScroller.getFinalY() - mScroller.getCurrY()) > mCloseEnough) {
1790
                    // Let the user 'catch' the pager as it animates.
1791
                    mScroller.abortAnimation();
1792
                    mPopulatePending = false;
1793
                    populate();
1794
                    mIsBeingDragged = true;
1795
                    requestParentDisallowInterceptTouchEvent(true);
1796
                    setScrollState(SCROLL_STATE_DRAGGING);
1797
                } else {
1798
                    completeScroll(false);
1799
                    mIsBeingDragged = false;
1800
                }
1801
1802
                if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
1803
                        + " mIsBeingDragged=" + mIsBeingDragged
1804
                        + "mIsUnableToDrag=" + mIsUnableToDrag);
1805
                break;
1806
            }
1807
1808
            case MotionEventCompat.ACTION_POINTER_UP:
1809
                onSecondaryPointerUp(ev);
1810
                break;
1811
        }
1812
1813
        if (mVelocityTracker == null) {
1814
            mVelocityTracker = VelocityTracker.obtain();
1815
        }
1816
        mVelocityTracker.addMovement(ev);
1817
1818
        /*
1819
         * The only time we want to intercept motion events is if we are in the
1820
         * drag mode.
1821
         */
1822
        return mIsBeingDragged;
1823
    }
1824
1825
    @Override
1826
    public boolean onTouchEvent(MotionEvent ev) {
1827
        if (mFakeDragging) {
1828
            // A fake drag is in progress already, ignore this real one
1829
            // but still eat the touch events.
1830
            // (It is likely that the user is multi-touching the screen.)
1831
            return true;
1832
        }
1833
1834
        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
1835
            // Don't handle edge touches immediately -- they may actually belong to one of our
1836
            // descendants.
1837
            return false;
1838
        }
1839
1840
        if (mAdapter == null || mAdapter.getCount() == 0) {
1841
            // Nothing to present or scroll; nothing to touch.
1842
            return false;
1843
        }
1844
1845
        if (mVelocityTracker == null) {
1846
            mVelocityTracker = VelocityTracker.obtain();
1847
        }
1848
        mVelocityTracker.addMovement(ev);
1849
1850
        final int action = ev.getAction();
1851
        boolean needsInvalidate = false;
1852
1853
        switch (action & MotionEventCompat.ACTION_MASK) {
1854
            case MotionEvent.ACTION_DOWN: {
1855
                mScroller.abortAnimation();
1856
                mPopulatePending = false;
1857
                populate();
1858
1859
                // Remember where the motion event started
1860
                mLastMotionX = mInitialMotionX = ev.getX();
1861
                mLastMotionY = mInitialMotionY = ev.getY();
1862
                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
1863
                break;
1864
            }
1865
            case MotionEvent.ACTION_MOVE:
1866
                if (!mIsBeingDragged) {
1867
                    final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
1868
                    final float y = MotionEventCompat.getY(ev, pointerIndex);
1869
                    final float yDiff = Math.abs(y - mLastMotionY);
1870
                    final float x = MotionEventCompat.getX(ev, pointerIndex);
1871
                    final float xDiff = Math.abs(x - mLastMotionX);
1872
                    if (DEBUG)
1873
                        Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
1874
                    if (yDiff > mTouchSlop && yDiff > xDiff) {
1875
                        if (DEBUG) Log.v(TAG, "Starting drag!");
1876
                        mIsBeingDragged = true;
1877
                        requestParentDisallowInterceptTouchEvent(true);
1878
                        mLastMotionY = y - mInitialMotionY > 0 ? mInitialMotionY + mTouchSlop :
1879
                                mInitialMotionY - mTouchSlop;
1880
                        mLastMotionX = x;
1881
                        setScrollState(SCROLL_STATE_DRAGGING);
1882
                        setScrollingCacheEnabled(true);
1883
1884
                        // Disallow Parent Intercept, just in case
1885
                        ViewParent parent = getParent();
1886
                        if (parent != null) {
1887
                            parent.requestDisallowInterceptTouchEvent(true);
1888
                        }
1889
                    }
1890
                }
1891
                // Not else! Note that mIsBeingDragged can be set above.
1892
                if (mIsBeingDragged) {
1893
                    // Scroll to follow the motion event
1894
                    final int activePointerIndex = MotionEventCompat.findPointerIndex(
1895
                            ev, mActivePointerId);
1896
                    final float y = MotionEventCompat.getY(ev, activePointerIndex);
1897
                    needsInvalidate |= performDrag(y);
1898
                }
1899
                break;
1900
            case MotionEvent.ACTION_UP:
1901
                if (mIsBeingDragged) {
1902
                    final VelocityTracker velocityTracker = mVelocityTracker;
1903
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1904
                    int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
1905
                            velocityTracker, mActivePointerId);
1906
                    mPopulatePending = true;
1907
                    final int height = getClientHeight();
1908
                    final int scrollY = getScrollY();
1909
                    final ItemInfo ii = infoForCurrentScrollPosition();
1910
                    final int currentPage = ii.position;
1911
                    final float pageOffset = (((float) scrollY / height) - ii.offset) / ii.heightFactor;
1912
                    final int activePointerIndex =
1913
                            MotionEventCompat.findPointerIndex(ev, mActivePointerId);
1914
                    final float y = MotionEventCompat.getY(ev, activePointerIndex);
1915
                    final int totalDelta = (int) (y - mInitialMotionY);
1916
                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
1917
                            totalDelta);
1918
                    setCurrentItemInternal(nextPage, true, true, initialVelocity);
1919
1920
                    mActivePointerId = INVALID_POINTER;
1921
                    endDrag();
1922
                    needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();
1923
                }
1924
                break;
1925
            case MotionEvent.ACTION_CANCEL:
1926
                if (mIsBeingDragged) {
1927
                    scrollToItem(mCurItem, true, 0, false);
1928
                    mActivePointerId = INVALID_POINTER;
1929
                    endDrag();
1930
                    needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();
1931
                }
1932
                break;
1933
            case MotionEventCompat.ACTION_POINTER_DOWN: {
1934
                final int index = MotionEventCompat.getActionIndex(ev);
1935
                final float y = MotionEventCompat.getY(ev, index);
1936
                mLastMotionY = y;
1937
                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
1938
                break;
1939
            }
1940
            case MotionEventCompat.ACTION_POINTER_UP:
1941
                onSecondaryPointerUp(ev);
1942
                mLastMotionY = MotionEventCompat.getY(ev,
1943
                        MotionEventCompat.findPointerIndex(ev, mActivePointerId));
1944
                break;
1945
        }
1946
        if (needsInvalidate) {
1947
            ViewCompat.postInvalidateOnAnimation(this);
1948
        }
1949
        return true;
1950
    }
1951
1952
    private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {
1953
        final ViewParent parent = getParent();
1954
        if (parent != null) {
1955
            parent.requestDisallowInterceptTouchEvent(disallowIntercept);
1956
        }
1957
    }
1958
1959
    private boolean performDrag(float y) {
1960
        boolean needsInvalidate = false;
1961
1962
        final float deltaY = mLastMotionY - y;
1963
        mLastMotionY = y;
1964
1965
        float oldScrollY = getScrollY();
1966
        float scrollY = oldScrollY + deltaY;
1967
        final int height = getClientHeight();
1968
1969
        float topBound = height * mFirstOffset;
1970
        float bottomBound = height * mLastOffset;
1971
        boolean topAbsolute = true;
1972
        boolean bottomAbsolute = true;
1973
1974
        final ItemInfo firstItem = mItems.get(0);
1975
        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
1976
        if (firstItem.position != 0) {
1977
            topAbsolute = false;
1978
            topBound = firstItem.offset * height;
1979
        }
1980
        if (lastItem.position != mAdapter.getCount() - 1) {
1981
            bottomAbsolute = false;
1982
            bottomBound = lastItem.offset * height;
1983
        }
1984
1985
        if (scrollY < topBound) {
1986
            if (topAbsolute) {
1987
                float over = topBound - scrollY;
1988
                needsInvalidate = mTopEdge.onPull(Math.abs(over) / height);
1989
            }
1990
            scrollY = topBound;
1991
        } else if (scrollY > bottomBound) {
1992
            if (bottomAbsolute) {
1993
                float over = scrollY - bottomBound;
1994
                needsInvalidate = mBottomEdge.onPull(Math.abs(over) / height);
1995
            }
1996
            scrollY = bottomBound;
1997
        }
1998
        // Don't lose the rounded component
1999
        mLastMotionX += scrollY - (int) scrollY;
2000
        scrollTo(getScrollX(), (int) scrollY);
2001
        pageScrolled((int) scrollY);
2002
2003
        return needsInvalidate;
2004
    }
2005
2006
    /**
2007
     * @return Info about the page at the current scroll position.
2008
     * This can be synthetic for a missing middle page; the 'object' field can be null.
2009
     */
2010
    private ItemInfo infoForCurrentScrollPosition() {
2011
        final int height = getClientHeight();
2012
        final float scrollOffset = height > 0 ? (float) getScrollY() / height : 0;
2013
        final float marginOffset = height > 0 ? (float) mPageMargin / height : 0;
2014
        int lastPos = -1;
2015
        float lastOffset = 0.f;
2016
        float lastHeight = 0.f;
2017
        boolean first = true;
2018
2019
        ItemInfo lastItem = null;
2020
        for (int i = 0; i < mItems.size(); i++) {
2021
            ItemInfo ii = mItems.get(i);
2022
            float offset;
2023
            if (!first && ii.position != lastPos + 1) {
2024
                // Create a synthetic item for a missing page.
2025
                ii = mTempItem;
2026
                ii.offset = lastOffset + lastHeight + marginOffset;
2027
                ii.position = lastPos + 1;
2028
                ii.heightFactor = mAdapter.getPageWidth(ii.position);
2029
                i--;
2030
            }
2031
            offset = ii.offset;
2032
2033
            final float topBound = offset;
2034
            final float bottomBound = offset + ii.heightFactor + marginOffset;
2035
            if (first || scrollOffset >= topBound) {
2036
                if (scrollOffset < bottomBound || i == mItems.size() - 1) {
2037
                    return ii;
2038
                }
2039
            } else {
2040
                return lastItem;
2041
            }
2042
            first = false;
2043
            lastPos = ii.position;
2044
            lastOffset = offset;
2045
            lastHeight = ii.heightFactor;
2046
            lastItem = ii;
2047
        }
2048
2049
        return lastItem;
2050
    }
2051
2052
    private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaY) {
2053
        int targetPage;
2054
        if (Math.abs(deltaY) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
2055
            targetPage = velocity > 0 ? currentPage : currentPage + 1;
2056
        } else {
2057
            final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
2058
            targetPage = (int) (currentPage + pageOffset + truncator);
2059
        }
2060
2061
        if (mItems.size() > 0) {
2062
            final ItemInfo firstItem = mItems.get(0);
2063
            final ItemInfo lastItem = mItems.get(mItems.size() - 1);
2064
2065
            // Only let the user target pages we have items for
2066
            targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
2067
        }
2068
2069
        return targetPage;
2070
    }
2071
2072
    @Override
2073
    public void draw(Canvas canvas) {
2074
        super.draw(canvas);
2075
        boolean needsInvalidate = false;
2076
2077
        final int overScrollMode = ViewCompat.getOverScrollMode(this);
2078
        if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
2079
                (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&
2080
                        mAdapter != null && mAdapter.getCount() > 1)) {
2081
            if (!mTopEdge.isFinished()) {
2082
                final int restoreCount = canvas.save();
2083
                final int height = getHeight();
2084
                final int width = getWidth() - getPaddingLeft() - getPaddingRight();
2085
2086
                canvas.translate(getPaddingLeft(), mFirstOffset * height);
2087
                mTopEdge.setSize(width, height);
2088
                needsInvalidate |= mTopEdge.draw(canvas);
2089
                canvas.restoreToCount(restoreCount);
2090
            }
2091
            if (!mBottomEdge.isFinished()) {
2092
                final int restoreCount = canvas.save();
2093
                final int height = getHeight();
2094
                final int width = getWidth() - getPaddingLeft() - getPaddingRight();
2095
2096
                canvas.rotate(180);
2097
                canvas.translate(-width - getPaddingLeft(), -(mLastOffset + 1) * height);
2098
                mBottomEdge.setSize(width, height);
2099
                needsInvalidate |= mBottomEdge.draw(canvas);
2100
                canvas.restoreToCount(restoreCount);
2101
            }
2102
        } else {
2103
            mTopEdge.finish();
2104
            mBottomEdge.finish();
2105
        }
2106
2107
        if (needsInvalidate) {
2108
            // Keep animating
2109
            ViewCompat.postInvalidateOnAnimation(this);
2110
        }
2111
    }
2112
2113
    @Override
2114
    protected void onDraw(Canvas canvas) {
2115
        super.onDraw(canvas);
2116
2117
        // Draw the margin drawable between pages if needed.
2118
        if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {
2119
            final int scrollY = getScrollY();
2120
            final int height = getHeight();
2121
2122
            final float marginOffset = (float) mPageMargin / height;
2123
            int itemIndex = 0;
2124
            ItemInfo ii = mItems.get(0);
2125
            float offset = ii.offset;
2126
            final int itemCount = mItems.size();
2127
            final int firstPos = ii.position;
2128
            final int lastPos = mItems.get(itemCount - 1).position;
2129
            for (int pos = firstPos; pos < lastPos; pos++) {
2130
                while (pos > ii.position && itemIndex < itemCount) {
2131
                    ii = mItems.get(++itemIndex);
2132
                }
2133
2134
                float drawAt;
2135
                if (pos == ii.position) {
2136
                    drawAt = (ii.offset + ii.heightFactor) * height;
2137
                    offset = ii.offset + ii.heightFactor + marginOffset;
2138
                } else {
2139
                    float heightFactor = mAdapter.getPageWidth(pos);
2140
                    drawAt = (offset + heightFactor) * height;
2141
                    offset += heightFactor + marginOffset;
2142
                }
2143
2144
                if (drawAt + mPageMargin > scrollY) {
2145
                    mMarginDrawable.setBounds(mLeftPageBounds, (int) drawAt,
2146
                            mRightPageBounds, (int) (drawAt + mPageMargin + 0.5f));
2147
                    mMarginDrawable.draw(canvas);
2148
                }
2149
2150
                if (drawAt > scrollY + height) {
2151
                    break; // No more visible, no sense in continuing
2152
                }
2153
            }
2154
        }
2155
    }
2156
2157
    /**
2158
     * Start a fake drag of the pager.
2159
     * <p/>
2160
     * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
2161
     * with the touch scrolling of another view, while still letting the ViewPager
2162
     * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
2163
     * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
2164
     * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
2165
     * <p/>
2166
     * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
2167
     * is already in progress, this method will return false.
2168
     *
2169
     * @return true if the fake drag began successfully, false if it could not be started.
2170
     * @see #fakeDragBy(float)
2171
     * @see #endFakeDrag()
2172
     */
2173
    public boolean beginFakeDrag() {
2174
        if (mIsBeingDragged) {
2175
            return false;
2176
        }
2177
        mFakeDragging = true;
2178
        setScrollState(SCROLL_STATE_DRAGGING);
2179
        mInitialMotionY = mLastMotionY = 0;
2180
        if (mVelocityTracker == null) {
2181
            mVelocityTracker = VelocityTracker.obtain();
2182
        } else {
2183
            mVelocityTracker.clear();
2184
        }
2185
        final long time = SystemClock.uptimeMillis();
2186
        final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
2187
        mVelocityTracker.addMovement(ev);
2188
        ev.recycle();
2189
        mFakeDragBeginTime = time;
2190
        return true;
2191
    }
2192
2193
    /**
2194
     * End a fake drag of the pager.
2195
     *
2196
     * @see #beginFakeDrag()
2197
     * @see #fakeDragBy(float)
2198
     */
2199
    public void endFakeDrag() {
2200
        if (!mFakeDragging) {
2201
            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
2202
        }
2203
2204
        final VelocityTracker velocityTracker = mVelocityTracker;
2205
        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
2206
        int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
2207
                velocityTracker, mActivePointerId);
2208
        mPopulatePending = true;
2209
        final int height = getClientHeight();
2210
        final int scrollY = getScrollY();
2211
        final ItemInfo ii = infoForCurrentScrollPosition();
2212
        final int currentPage = ii.position;
2213
        final float pageOffset = (((float) scrollY / height) - ii.offset) / ii.heightFactor;
2214
        final int totalDelta = (int) (mLastMotionY - mInitialMotionY);
2215
        int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
2216
                totalDelta);
2217
        setCurrentItemInternal(nextPage, true, true, initialVelocity);
2218
        endDrag();
2219
2220
        mFakeDragging = false;
2221
    }
2222
2223
    /**
2224
     * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
2225
     *
2226
     * @param yOffset Offset in pixels to drag by.
2227
     * @see #beginFakeDrag()
2228
     * @see #endFakeDrag()
2229
     */
2230
    public void fakeDragBy(float yOffset) {
2231
        if (!mFakeDragging) {
2232
            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
2233
        }
2234
2235
        mLastMotionY += yOffset;
2236
2237
        float oldScrollY = getScrollY();
2238
        float scrollY = oldScrollY - yOffset;
2239
        final int height = getClientHeight();
2240
2241
        float topBound = height * mFirstOffset;
2242
        float bottomBound = height * mLastOffset;
2243
2244
        final ItemInfo firstItem = mItems.get(0);
2245
        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
2246
        if (firstItem.position != 0) {
2247
            topBound = firstItem.offset * height;
2248
        }
2249
        if (lastItem.position != mAdapter.getCount() - 1) {
2250
            bottomBound = lastItem.offset * height;
2251
        }
2252
2253
        if (scrollY < topBound) {
2254
            scrollY = topBound;
2255
        } else if (scrollY > bottomBound) {
2256
            scrollY = bottomBound;
2257
        }
2258
        // Don't lose the rounded component
2259
        mLastMotionY += scrollY - (int) scrollY;
2260
        scrollTo(getScrollX(), (int) scrollY);
2261
        pageScrolled((int) scrollY);
2262
2263
        // Synthesize an event for the VelocityTracker.
2264
        final long time = SystemClock.uptimeMillis();
2265
        final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
2266
                0, mLastMotionY, 0);
2267
        mVelocityTracker.addMovement(ev);
2268
        ev.recycle();
2269
    }
2270
2271
    /**
2272
     * Returns true if a fake drag is in progress.
2273
     *
2274
     * @return true if currently in a fake drag, false otherwise.
2275
     * @see #beginFakeDrag()
2276
     * @see #fakeDragBy(float)
2277
     * @see #endFakeDrag()
2278
     */
2279
    public boolean isFakeDragging() {
2280
        return mFakeDragging;
2281
    }
2282
2283
    private void onSecondaryPointerUp(MotionEvent ev) {
2284
        final int pointerIndex = MotionEventCompat.getActionIndex(ev);
2285
        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
2286
        if (pointerId == mActivePointerId) {
2287
            // This was our active pointer going up. Choose a new
2288
            // active pointer and adjust accordingly.
2289
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
2290
            mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);
2291
            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
2292
            if (mVelocityTracker != null) {
2293
                mVelocityTracker.clear();
2294
            }
2295
        }
2296
    }
2297
2298
    private void endDrag() {
2299
        mIsBeingDragged = false;
2300
        mIsUnableToDrag = false;
2301
2302
        if (mVelocityTracker != null) {
2303
            mVelocityTracker.recycle();
2304
            mVelocityTracker = null;
2305
        }
2306
    }
2307
2308
    private void setScrollingCacheEnabled(boolean enabled) {
2309
        if (mScrollingCacheEnabled != enabled) {
2310
            mScrollingCacheEnabled = enabled;
2311
            if (USE_CACHE) {
2312
                final int size = getChildCount();
2313
                for (int i = 0; i < size; ++i) {
2314
                    final View child = getChildAt(i);
2315
                    if (child.getVisibility() != GONE) {
2316
                        child.setDrawingCacheEnabled(enabled);
2317
                    }
2318
                }
2319
            }
2320
        }
2321
    }
2322
2323
    public boolean internalCanScrollVertically(int direction) {
2324
        if (mAdapter == null) {
2325
            return false;
2326
        }
2327
2328
        final int height = getClientHeight();
2329
        final int scrollY = getScrollY();
2330
        if (direction < 0) {
2331
            return (scrollY > (int) (height * mFirstOffset));
2332
        } else if (direction > 0) {
2333
            return (scrollY < (int) (height * mLastOffset));
2334
        } else {
2335
            return false;
2336
        }
2337
    }
2338
2339
    /**
2340
     * Tests scrollability within child views of v given a delta of dx.
2341
     *
2342
     * @param v      View to test for horizontal scrollability
2343
     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
2344
     *               or just its children (false).
2345
     * @param dy     Delta scrolled in pixels
2346
     * @param x      X coordinate of the active touch point
2347
     * @param y      Y coordinate of the active touch point
2348
     * @return true if child views of v can be scrolled by delta of dx.
2349
     */
2350
    protected boolean canScroll(View v, boolean checkV, int dy, int x, int y) {
2351
        if (v instanceof ViewGroup) {
2352
            final ViewGroup group = (ViewGroup) v;
2353
            final int scrollX = v.getScrollX();
2354
            final int scrollY = v.getScrollY();
2355
            final int count = group.getChildCount();
2356
            // Count backwards - let topmost views consume scroll distance first.
2357
            for (int i = count - 1; i >= 0; i--) {
2358
                // TODO: Add versioned support here for transformed views.
2359
                // This will not work for transformed views in Honeycomb+
2360
                final View child = group.getChildAt(i);
2361
                if (y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
2362
                        x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
2363
                        canScroll(child, true, dy, x + scrollX - child.getLeft(),
2364
                                y + scrollY - child.getTop())) {
2365
                    return true;
2366
                }
2367
            }
2368
        }
2369
2370
        return checkV && ViewCompat.canScrollVertically(v, -dy);
2371
    }
2372
2373
    @Override
2374
    public boolean dispatchKeyEvent(KeyEvent event) {
2375
        // Let the focused view and/or our descendants get the key first
2376
        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
2377
    }
2378
2379
    /**
2380
     * You can call this function yourself to have the scroll view perform
2381
     * scrolling from a key event, just as if the event had been dispatched to
2382
     * it by the view hierarchy.
2383
     *
2384
     * @param event The key event to execute.
2385
     * @return Return true if the event was handled, else false.
2386
     */
2387
    public boolean executeKeyEvent(KeyEvent event) {
2388
        boolean handled = false;
2389
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
2390
            switch (event.getKeyCode()) {
2391
                case KeyEvent.KEYCODE_DPAD_LEFT:
2392
                    handled = arrowScroll(FOCUS_LEFT);
2393
                    break;
2394
                case KeyEvent.KEYCODE_DPAD_RIGHT:
2395
                    handled = arrowScroll(FOCUS_RIGHT);
2396
                    break;
2397
                case KeyEvent.KEYCODE_TAB:
2398
                    if (Build.VERSION.SDK_INT >= 11) {
2399
                        if (event.hasNoModifiers()) {
2400
                            handled = arrowScroll(FOCUS_FORWARD);
2401
                        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
2402
                            handled = arrowScroll(FOCUS_BACKWARD);
2403
                        }
2404
                    }
2405
                    break;
2406
            }
2407
        }
2408
        return handled;
2409
    }
2410
2411
    public boolean arrowScroll(int direction) {
2412
        View currentFocused = findFocus();
2413
        if (currentFocused == this) {
2414
            currentFocused = null;
2415
        } else if (currentFocused != null) {
2416
            boolean isChild = false;
2417
            for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
2418
                 parent = parent.getParent()) {
2419
                if (parent == this) {
2420
                    isChild = true;
2421
                    break;
2422
                }
2423
            }
2424
            if (!isChild) {
2425
                // This would cause the focus search down below to fail in fun ways.
2426
                final StringBuilder sb = new StringBuilder();
2427
                sb.append(currentFocused.getClass().getSimpleName());
2428
                for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
2429
                     parent = parent.getParent()) {
2430
                    sb.append(" => ").append(parent.getClass().getSimpleName());
2431
                }
2432
                Log.e(TAG, "arrowScroll tried to find focus based on non-child " +
2433
                        "current focused view " + sb.toString());
2434
                currentFocused = null;
2435
            }
2436
        }
2437
2438
        boolean handled = false;
2439
2440
        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
2441
                direction);
2442
        if (nextFocused != null && nextFocused != currentFocused) {
2443
            if (direction == View.FOCUS_UP) {
2444
                // If there is nothing to the left, or this is causing us to
2445
                // jump to the right, then what we really want to do is page left.
2446
                final int nextTop = getChildRectInPagerCoordinates(mTempRect, nextFocused).top;
2447
                final int currTop = getChildRectInPagerCoordinates(mTempRect, currentFocused).top;
2448
                if (currentFocused != null && nextTop >= currTop) {
2449
                    handled = pageUp();
2450
                } else {
2451
                    handled = nextFocused.requestFocus();
2452
                }
2453
            } else if (direction == View.FOCUS_DOWN) {
2454
                // If there is nothing to the right, or this is causing us to
2455
                // jump to the left, then what we really want to do is page right.
2456
                final int nextDown = getChildRectInPagerCoordinates(mTempRect, nextFocused).bottom;
2457
                final int currDown = getChildRectInPagerCoordinates(mTempRect, currentFocused).bottom;
2458
                if (currentFocused != null && nextDown <= currDown) {
2459
                    handled = pageDown();
2460
                } else {
2461
                    handled = nextFocused.requestFocus();
2462
                }
2463
            }
2464
        } else if (direction == FOCUS_UP || direction == FOCUS_BACKWARD) {
2465
            // Trying to move left and nothing there; try to page.
2466
            handled = pageUp();
2467
        } else if (direction == FOCUS_DOWN || direction == FOCUS_FORWARD) {
2468
            // Trying to move right and nothing there; try to page.
2469
            handled = pageDown();
2470
        }
2471
        if (handled) {
2472
            playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
2473
        }
2474
        return handled;
2475
    }
2476
2477
    private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {
2478
        if (outRect == null) {
2479
            outRect = new Rect();
2480
        }
2481
        if (child == null) {
2482
            outRect.set(0, 0, 0, 0);
2483
            return outRect;
2484
        }
2485
        outRect.left = child.getLeft();
2486
        outRect.right = child.getRight();
2487
        outRect.top = child.getTop();
2488
        outRect.bottom = child.getBottom();
2489
2490
        ViewParent parent = child.getParent();
2491
        while (parent instanceof ViewGroup && parent != this) {
2492
            final ViewGroup group = (ViewGroup) parent;
2493
            outRect.left += group.getLeft();
2494
            outRect.right += group.getRight();
2495
            outRect.top += group.getTop();
2496
            outRect.bottom += group.getBottom();
2497
2498
            parent = group.getParent();
2499
        }
2500
        return outRect;
2501
    }
2502
2503
    boolean pageUp() {
2504
        if (mCurItem > 0) {
2505
            setCurrentItem(mCurItem - 1, true);
2506
            return true;
2507
        }
2508
        return false;
2509
    }
2510
2511
    boolean pageDown() {
2512
        if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {
2513
            setCurrentItem(mCurItem + 1, true);
2514
            return true;
2515
        }
2516
        return false;
2517
    }
2518
2519
    /**
2520
     * We only want the current page that is being shown to be focusable.
2521
     */
2522
    @Override
2523
    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2524
        final int focusableCount = views.size();
2525
2526
        final int descendantFocusability = getDescendantFocusability();
2527
2528
        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
2529
            for (int i = 0; i < getChildCount(); i++) {
2530
                final View child = getChildAt(i);
2531
                if (child.getVisibility() == VISIBLE) {
2532
                    ItemInfo ii = infoForChild(child);
2533
                    if (ii != null && ii.position == mCurItem) {
2534
                        child.addFocusables(views, direction, focusableMode);
2535
                    }
2536
                }
2537
            }
2538
        }
2539
2540
        // we add ourselves (if focusable) in all cases except for when we are
2541
        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
2542
        // to avoid the focus search finding layouts when a more precise search
2543
        // among the focusable children would be more interesting.
2544
        if (
2545
                descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
2546
                        // No focusable descendants
2547
                        (focusableCount == views.size())) {
2548
            // Note that we can't call the superclass here, because it will
2549
            // add all views in.  So we need to do the same thing View does.
2550
            if (!isFocusable()) {
2551
                return;
2552
            }
2553
            if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
2554
                    isInTouchMode() && !isFocusableInTouchMode()) {
2555
                return;
2556
            }
2557
            if (views != null) {
2558
                views.add(this);
2559
            }
2560
        }
2561
    }
2562
2563
    /**
2564
     * We only want the current page that is being shown to be touchable.
2565
     */
2566
    @Override
2567
    public void addTouchables(ArrayList<View> views) {
2568
        // Note that we don't call super.addTouchables(), which means that
2569
        // we don't call View.addTouchables().  This is okay because a ViewPager
2570
        // is itself not touchable.
2571
        for (int i = 0; i < getChildCount(); i++) {
2572
            final View child = getChildAt(i);
2573
            if (child.getVisibility() == VISIBLE) {
2574
                ItemInfo ii = infoForChild(child);
2575
                if (ii != null && ii.position == mCurItem) {
2576
                    child.addTouchables(views);
2577
                }
2578
            }
2579
        }
2580
    }
2581
2582
    /**
2583
     * We only want the current page that is being shown to be focusable.
2584
     */
2585
    @Override
2586
    protected boolean onRequestFocusInDescendants(int direction,
2587
                                                  Rect previouslyFocusedRect) {
2588
        int index;
2589
        int increment;
2590
        int end;
2591
        int count = getChildCount();
2592
        if ((direction & FOCUS_FORWARD) != 0) {
2593
            index = 0;
2594
            increment = 1;
2595
            end = count;
2596
        } else {
2597
            index = count - 1;
2598
            increment = -1;
2599
            end = -1;
2600
        }
2601
        for (int i = index; i != end; i += increment) {
2602
            View child = getChildAt(i);
2603
            if (child.getVisibility() == VISIBLE) {
2604
                ItemInfo ii = infoForChild(child);
2605
                if (ii != null && ii.position == mCurItem) {
2606
                    if (child.requestFocus(direction, previouslyFocusedRect)) {
2607
                        return true;
2608
                    }
2609
                }
2610
            }
2611
        }
2612
        return false;
2613
    }
2614
2615
    @Override
2616
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
2617
        // Dispatch scroll events from this ViewPager.
2618
        if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {
2619
            return super.dispatchPopulateAccessibilityEvent(event);
2620
        }
2621
2622
        // Dispatch all other accessibility events from the current page.
2623
        final int childCount = getChildCount();
2624
        for (int i = 0; i < childCount; i++) {
2625
            final View child = getChildAt(i);
2626
            if (child.getVisibility() == VISIBLE) {
2627
                final ItemInfo ii = infoForChild(child);
2628
                if (ii != null && ii.position == mCurItem &&
2629
                        child.dispatchPopulateAccessibilityEvent(event)) {
2630
                    return true;
2631
                }
2632
            }
2633
        }
2634
2635
        return false;
2636
    }
2637
2638
    @Override
2639
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
2640
        return new LayoutParams();
2641
    }
2642
2643
    @Override
2644
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
2645
        return generateDefaultLayoutParams();
2646
    }
2647
2648
    @Override
2649
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2650
        return p instanceof LayoutParams && super.checkLayoutParams(p);
2651
    }
2652
2653
    @Override
2654
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
2655
        return new LayoutParams(getContext(), attrs);
2656
    }
2657
2658
    class MyAccessibilityDelegate extends AccessibilityDelegateCompat {
2659
2660
        @Override
2661
        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
2662
            super.onInitializeAccessibilityEvent(host, event);
2663
            event.setClassName(ViewPager.class.getName());
2664
            final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain();
2665
            recordCompat.setScrollable(canScroll());
2666
            if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED
2667
                    && mAdapter != null) {
2668
                recordCompat.setItemCount(mAdapter.getCount());
2669
                recordCompat.setFromIndex(mCurItem);
2670
                recordCompat.setToIndex(mCurItem);
2671
            }
2672
        }
2673
2674
        @Override
2675
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
2676
            super.onInitializeAccessibilityNodeInfo(host, info);
2677
            info.setClassName(ViewPager.class.getName());
2678
            info.setScrollable(canScroll());
2679
            if (internalCanScrollVertically(1)) {
2680
                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
2681
            }
2682
            if (internalCanScrollVertically(-1)) {
2683
                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
2684
            }
2685
        }
2686
2687
        @Override
2688
        public boolean performAccessibilityAction(View host, int action, Bundle args) {
2689
            if (super.performAccessibilityAction(host, action, args)) {
2690
                return true;
2691
            }
2692
            switch (action) {
2693
                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
2694
                    if (internalCanScrollVertically(1)) {
2695
                        setCurrentItem(mCurItem + 1);
2696
                        return true;
2697
                    }
2698
                }
2699
                return false;
2700
                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
2701
                    if (internalCanScrollVertically(-1)) {
2702
                        setCurrentItem(mCurItem - 1);
2703
                        return true;
2704
                    }
2705
                }
2706
                return false;
2707
            }
2708
            return false;
2709
        }
2710
2711
        private boolean canScroll() {
2712
            return (mAdapter != null) && (mAdapter.getCount() > 1);
2713
        }
2714
    }
2715
2716
    private class PagerObserver extends DataSetObserver {
2717
        @Override
2718
        public void onChanged() {
2719
            dataSetChanged();
2720
        }
2721
2722
        @Override
2723
        public void onInvalidated() {
2724
            dataSetChanged();
2725
        }
2726
    }
2727
2728
    /**
2729
     * Layout parameters that should be supplied for views added to a
2730
     * ViewPager.
2731
     */
2732
    public static class LayoutParams extends ViewGroup.LayoutParams {
2733
        /**
2734
         * true if this view is a decoration on the pager itself and not
2735
         * a view supplied by the adapter.
2736
         */
2737
        public boolean isDecor;
2738
2739
        /**
2740
         * Gravity setting for use on decor views only:
2741
         * Where to position the view page within the overall ViewPager
2742
         * container; constants are defined in {@link Gravity}.
2743
         */
2744
        public int gravity;
2745
2746
        /**
2747
         * Width as a 0-1 multiplier of the measured pager width
2748
         */
2749
        float heightFactor = 0.f;
2750
2751
        /**
2752
         * true if this view was added during layout and needs to be measured
2753
         * before being positioned.
2754
         */
2755
        boolean needsMeasure;
2756
2757
        /**
2758
         * Adapter position this view is for if !isDecor
2759
         */
2760
        int position;
2761
2762
        /**
2763
         * Current child index within the ViewPager that this view occupies
2764
         */
2765
        int childIndex;
2766
2767
        public LayoutParams() {
2768
            super(FILL_PARENT, FILL_PARENT);
2769
        }
2770
2771
        public LayoutParams(Context context, AttributeSet attrs) {
2772
            super(context, attrs);
2773
2774
            final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
2775
            gravity = a.getInteger(0, Gravity.TOP);
2776
            a.recycle();
2777
        }
2778
    }
2779
2780
    static class ViewPositionComparator implements Comparator<View> {
2781
        @Override
2782
        public int compare(View lhs, View rhs) {
2783
            final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();
2784
            final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();
2785
            if (llp.isDecor != rlp.isDecor) {
2786
                return llp.isDecor ? 1 : -1;
2787
            }
2788
            return llp.position - rlp.position;
2789
        }
2790
    }
2791
}

+ 7 - 0
app/src/main/res/anim/bottom_in_anim.xml

@ -0,0 +1,7 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<set xmlns:android="http://schemas.android.com/apk/res/android">
3
    <translate
4
        android:duration="300"
5
        android:fromYDelta="100%p"
6
        android:toYDelta="0" />
7
</set>

+ 7 - 0
app/src/main/res/anim/bottom_out_anim.xml

@ -0,0 +1,7 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<set xmlns:android="http://schemas.android.com/apk/res/android">
3
    <translate
4
        android:duration="300"
5
        android:fromYDelta="0"
6
        android:toYDelta="100%p" />
7
</set>

app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/mima.png → app/src/main/res/drawable-xxhdpi/mima.png


app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/rentou.png → app/src/main/res/drawable-xxhdpi/rentou.png


app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/shanche.png → app/src/main/res/drawable-xxhdpi/shanche.png


app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/top.png → app/src/main/res/drawable-xxhdpi/top.png


app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/tubiao1.png → app/src/main/res/drawable-xxhdpi/tubiao1.png


app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/zhuangtailan.png → app/src/main/res/drawable-xxhdpi/zhuangtailan.png


app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/zhucaozuolan.png → app/src/main/res/drawable-xxhdpi/zhucaozuolan.png


+ 12 - 0
app/src/main/res/drawable/bg_bottom_dialog.xml

@ -0,0 +1,12 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<shape xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:shape="rectangle">
4
5
    <corners
6
        android:topLeftRadius="6dp"
7
        android:topRightRadius="6dp" />
8
9
    <solid android:color="#424242" />
10
11
12
</shape>

+ 10 - 0
app/src/main/res/drawable/bg_comment_report.xml

@ -0,0 +1,10 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<shape xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:shape="rectangle">
4
5
    <corners android:radius="15dp" />
6
7
    <solid android:color="#606060" />
8
9
10
</shape>

+ 10 - 0
app/src/main/res/drawable/bg_topic.xml

@ -0,0 +1,10 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<shape xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:shape="rectangle">
4
5
    <corners android:radius="6dp" />
6
7
    <solid android:color="#b3303030" />
8
9
10
</shape>

+ 48 - 0
app/src/main/res/layout/activity_videodetails.xml

@ -0,0 +1,48 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="match_parent"
5
    android:background="#424242">
6
7
8
    <com.electric.chargingpile.view.sview.VerticalViewPager
9
        android:id="@+id/pager_video"
10
        android:layout_width="match_parent"
11
        android:layout_height="match_parent" />
12
13
14
    <RelativeLayout
15
        android:layout_width="match_parent"
16
        android:layout_height="48dp">
17
18
19
        <TextView
20
            android:id="@+id/vd_title_tv"
21
            android:layout_width="wrap_content"
22
            android:layout_height="wrap_content"
23
            android:layout_centerInParent="true"
24
            android:text="待问答问题"
25
            android:textColor="#ffffff"
26
            android:textSize="18sp" />
27
28
29
        <ImageView
30
            android:id="@+id/vd_title_back"
31
            android:layout_width="wrap_content"
32
            android:layout_height="match_parent"
33
            android:layout_alignParentLeft="true"
34
            android:paddingLeft="15dp"
35
            android:paddingRight="15dp"
36
            android:src="@drawable/tubiao1" />
37
38
        <ImageView
39
            android:id="@+id/vd_title_dot"
40
            android:layout_width="wrap_content"
41
            android:layout_height="match_parent"
42
            android:layout_alignParentRight="true"
43
            android:paddingLeft="15dp"
44
            android:paddingRight="15dp"
45
            android:src="@drawable/tubiao1" />
46
47
    </RelativeLayout>
48
</RelativeLayout>

+ 11 - 0
app/src/main/res/layout/fragment_shortvideo.xml

@ -0,0 +1,11 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="match_parent">
5
6
    <com.electric.chargingpile.view.sview.VerticalViewPager
7
        android:id="@+id/pager_video"
8
        android:layout_width="match_parent"
9
        android:layout_height="match_parent" />
10
11
</android.support.constraint.ConstraintLayout>

+ 130 - 0
app/src/main/res/layout/item_show_comment.xml

@ -0,0 +1,130 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="wrap_content"
5
    android:layout_marginTop="20dp">
6
7
    <LinearLayout
8
        android:id="@+id/item_user_comment_like"
9
        android:layout_width="wrap_content"
10
        android:layout_height="18dp"
11
        android:layout_alignParentEnd="true"
12
        android:layout_marginTop="3dp"
13
        android:layout_marginEnd="19dp"
14
        android:gravity="center_vertical">
15
16
        <ImageView
17
            android:id="@+id/item_user_like_img"
18
            android:layout_width="18dp"
19
            android:layout_height="18dp"
20
            android:background="#fff" />
21
22
        <TextView
23
            android:id="@+id/item_user_like_count"
24
            android:layout_width="wrap_content"
25
            android:layout_height="wrap_content"
26
            android:layout_marginStart="4dp"
27
            android:text="1000"
28
            android:textColor="#888"
29
            android:textSize="12sp" />
30
    </LinearLayout>
31
32
33
    <ImageView
34
        android:id="@+id/item_user_avatar"
35
        android:layout_width="20dp"
36
        android:layout_height="20dp"
37
        android:layout_marginStart="15dp"
38
        android:layout_marginTop="6dp"
39
        android:background="#3df" />
40
41
42
    <TextView
43
        android:id="@+id/item_user_name"
44
        android:layout_width="wrap_content"
45
        android:layout_height="wrap_content"
46
        android:layout_marginStart="5dp"
47
        android:layout_toEndOf="@+id/item_user_avatar"
48
        android:text="12sp12sp12sp"
49
        android:textColor="#fff"
50
        android:textSize="12sp" />
51
52
    <TextView
53
        android:id="@+id/item_user_time"
54
        android:layout_width="wrap_content"
55
        android:layout_height="wrap_content"
56
        android:layout_below="@+id/item_user_name"
57
        android:layout_alignStart="@+id/item_user_name"
58
        android:layout_marginStart="5dp"
59
        android:text="12sp12sp12sp"
60
        android:textColor="#888"
61
        android:textSize="12sp" />
62
63
64
    <TextView
65
        android:id="@+id/item_user_con"
66
        android:layout_width="wrap_content"
67
        android:layout_height="wrap_content"
68
        android:layout_below="@+id/item_user_time"
69
        android:layout_alignStart="@+id/item_user_name"
70
        android:layout_marginStart="5dp"
71
        android:layout_marginTop="10dp"
72
        android:text="12sp12sp12sp"
73
        android:textColor="#fff"
74
        android:textSize="14sp" />
75
76
    <LinearLayout
77
        android:id="@+id/item_user_operation"
78
        android:layout_width="wrap_content"
79
        android:layout_height="22dp"
80
        android:layout_below="@+id/item_user_con"
81
        android:layout_alignStart="@+id/item_user_name"
82
        android:layout_marginTop="10dp"
83
        android:layout_marginBottom="22dp"
84
        android:gravity="center_vertical">
85
86
        <LinearLayout
87
            android:id="@+id/item_user_report_ll"
88
            android:layout_width="wrap_content"
89
            android:layout_height="match_parent"
90
            android:layout_marginEnd="10dp"
91
            android:background="@drawable/bg_comment_report"
92
            android:gravity="center_vertical">
93
94
            <ImageView
95
                android:layout_width="16dp"
96
                android:layout_height="16dp"
97
                android:layout_marginStart="9dp"
98
                android:background="#fff" />
99
100
            <TextView
101
                android:id="@+id/item_user_report_count"
102
                android:layout_width="wrap_content"
103
                android:layout_height="wrap_content"
104
                android:layout_marginStart="5dp"
105
                android:layout_marginEnd="9dp"
106
                android:text="1000回复"
107
                android:textColor="#c2c2c2"
108
                android:textSize="12sp" />
109
        </LinearLayout>
110
111
112
        <TextView
113
            android:id="@+id/item_user_delete"
114
            android:layout_width="wrap_content"
115
            android:layout_height="22dp"
116
            android:gravity="center"
117
            android:text="删除"
118
            android:textColor="#c2c2c2"
119
            android:textSize="12sp" />
120
    </LinearLayout>
121
122
    <View
123
        android:layout_width="match_parent"
124
        android:layout_height="1dp"
125
        android:layout_below="@+id/item_user_operation"
126
        android:layout_marginStart="16dp"
127
        android:background="#505050" />
128
129
130
</RelativeLayout>

+ 85 - 0
app/src/main/res/layout/sv_video_publish_info.xml

@ -0,0 +1,85 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="wrap_content"
5
    android:background="@drawable/bg_bottom_dialog">
6
7
8
    <View
9
        android:id="@+id/video_publish_close"
10
        android:layout_width="22dp"
11
        android:layout_height="22dp"
12
        android:layout_alignParentEnd="true"
13
        android:layout_marginTop="14dp"
14
        android:layout_marginEnd="14dp"
15
        android:background="#fff" />
16
17
18
    <LinearLayout
19
        android:id="@+id/video_publish_topic"
20
        android:layout_width="wrap_content"
21
        android:layout_height="wrap_content"
22
        android:layout_marginStart="15dp"
23
        android:layout_marginTop="15dp"
24
        android:layout_marginEnd="40dp"
25
        android:layout_marginBottom="10dp"
26
        android:background="@drawable/bg_topic"
27
        android:gravity="center_vertical"
28
        android:minHeight="30dp">
29
30
        <View
31
            android:layout_width="12dp"
32
            android:layout_height="12dp"
33
            android:layout_marginStart="12dp"
34
            android:layout_marginEnd="4dp"
35
            android:background="#fff" />
36
37
        <TextView
38
            android:id="@+id/video_publish_topic_con"
39
            android:layout_width="wrap_content"
40
            android:layout_height="wrap_content"
41
            android:layout_margin="5dp"
42
            android:text="显示出所有文字内容显示出所有文字内容显示出所有文字内容"
43
            android:textColor="#fff"
44
            android:textSize="11sp" />
45
    </LinearLayout>
46
47
    <LinearLayout
48
        android:id="@+id/video_publish_user_ll"
49
        android:layout_width="wrap_content"
50
        android:layout_height="20dp"
51
        android:layout_below="@+id/video_publish_topic"
52
        android:layout_marginStart="15dp"
53
        android:gravity="center_vertical"
54
        android:orientation="horizontal">
55
56
        <ImageView
57
            android:id="@+id/video_publish_user_avatar"
58
            android:layout_width="20dp"
59
            android:layout_height="20dp"
60
            android:background="#3df" />
61
62
        <TextView
63
            android:id="@+id/video_publish_user_name"
64
            android:layout_width="wrap_content"
65
            android:layout_height="wrap_content"
66
            android:layout_marginStart="6dp"
67
            android:text="12sp12sp12sp"
68
            android:textColor="#fff"
69
            android:textSize="12sp" />
70
    </LinearLayout>
71
72
    <TextView
73
        android:layout_width="match_parent"
74
        android:layout_height="wrap_content"
75
        android:layout_below="@+id/video_publish_user_ll"
76
        android:layout_marginStart="30dp"
77
        android:layout_marginTop="15dp"
78
        android:layout_marginEnd="30dp"
79
        android:layout_marginBottom="35dp"
80
        android:maxHeight="320dp"
81
        android:text="asfsfsfsfsfsafsafsfsfsaf"
82
        android:textColor="#fff"
83
        android:textSize="14sp" />
84
85
</RelativeLayout>

+ 204 - 0
app/src/main/res/layout/view_show_bottom.xml

@ -0,0 +1,204 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<merge xmlns:android="http://schemas.android.com/apk/res/android">
3
4
    <RelativeLayout
5
        android:id="@+id/sv_show_bottom"
6
        android:layout_width="match_parent"
7
        android:layout_height="50dp"
8
        android:layout_alignParentBottom="true">
9
10
        <LinearLayout
11
            android:id="@+id/sv_show_forward"
12
            android:layout_width="wrap_content"
13
            android:layout_height="30dp"
14
            android:layout_alignParentRight="true"
15
            android:layout_centerVertical="true"
16
            android:layout_marginRight="15dp"
17
            android:gravity="center_vertical">
18
19
            <ImageView
20
                android:layout_width="30dp"
21
                android:layout_height="30dp"
22
                android:layout_centerVertical="true"
23
                android:background="#fff" />
24
25
            <TextView
26
                android:layout_width="wrap_content"
27
                android:layout_height="wrap_content"
28
                android:text="转发"
29
                android:textSize="12sp" />
30
31
        </LinearLayout>
32
33
        <LinearLayout
34
            android:id="@+id/sv_show_comment_count_ll"
35
            android:layout_width="wrap_content"
36
            android:layout_height="30dp"
37
            android:layout_centerVertical="true"
38
            android:layout_marginRight="15dp"
39
            android:layout_toStartOf="@+id/sv_show_forward"
40
            android:gravity="center_vertical">
41
42
            <ImageView
43
                android:layout_width="30dp"
44
                android:layout_height="30dp"
45
                android:layout_centerVertical="true"
46
                android:background="#fff" />
47
48
            <TextView
49
                android:id="@+id/sv_show_comment_count_tv"
50
                android:layout_width="wrap_content"
51
                android:layout_height="wrap_content"
52
                android:text="1230w"
53
                android:textSize="12sp" />
54
55
        </LinearLayout>
56
57
        <LinearLayout
58
            android:id="@+id/sv_show_like_ll"
59
            android:layout_width="wrap_content"
60
            android:layout_height="30dp"
61
            android:layout_centerVertical="true"
62
            android:layout_marginRight="15dp"
63
            android:layout_toStartOf="@+id/sv_show_comment_count_ll"
64
            android:gravity="center_vertical">
65
66
            <ImageView
67
                android:id="@+id/sv_show_like_img"
68
                android:layout_width="30dp"
69
                android:layout_height="30dp"
70
                android:layout_centerVertical="true"
71
                android:background="#fff" />
72
73
            <TextView
74
                android:id="@+id/sv_show_like_tv"
75
                android:layout_width="wrap_content"
76
                android:layout_height="wrap_content"
77
                android:text="12我30w"
78
                android:textSize="12sp" />
79
80
        </LinearLayout>
81
82
        <LinearLayout
83
            android:id="@+id/sv_show_comment_ll"
84
            android:layout_width="match_parent"
85
            android:layout_height="match_parent"
86
            android:layout_centerVertical="true"
87
            android:layout_marginRight="15dp"
88
            android:layout_toStartOf="@+id/sv_show_like_ll"
89
            android:gravity="center_vertical">
90
91
            <ImageView
92
                android:layout_width="30dp"
93
                android:layout_height="30dp"
94
                android:layout_centerVertical="true"
95
                android:layout_marginLeft="15dp"
96
                android:background="#fff" />
97
98
            <TextView
99
                android:layout_width="wrap_content"
100
                android:layout_height="wrap_content"
101
                android:text="写评论"
102
                android:textSize="12sp" />
103
104
        </LinearLayout>
105
106
    </RelativeLayout>
107
108
    <View
109
        android:layout_width="match_parent"
110
        android:layout_height="1dp"
111
        android:layout_above="@+id/sv_show_bottom"
112
        android:background="#505050" />
113
114
    <TextView
115
        android:id="@+id/sv_show_tvcon"
116
        android:layout_width="match_parent"
117
        android:layout_height="wrap_content"
118
        android:layout_above="@+id/sv_show_bottom"
119
        android:layout_marginLeft="39dp"
120
        android:layout_marginRight="39dp"
121
        android:layout_marginBottom="23dp"
122
        android:ellipsize="end"
123
        android:maxLines="3"
124
        android:text="3-超出三行的,点击文字,上滑弹窗,显示出所有文字内容,根据内容自适应高度,最高为一半屏幕高度,字数超高,以滚动条滑动显示。3-超出三行的,点击文字,上滑弹窗,显示出所有文字内容,根据内容自适应高度,最高为一半屏幕高度,字数超高,以滚动条滑动显示。"
125
        android:textSize="14sp" />
126
127
    <View
128
        android:id="@+id/sv_show_tvcon_more"
129
        android:layout_width="30dp"
130
        android:layout_height="30dp"
131
        android:layout_above="@+id/sv_show_bottom"
132
        android:layout_alignParentEnd="true"
133
        android:layout_marginBottom="28dp"
134
        android:background="@color/__picker_black_40" />
135
136
    <RelativeLayout
137
        android:id="@+id/sv_show_user_info"
138
        android:layout_width="wrap_content"
139
        android:layout_height="34dp"
140
        android:layout_above="@+id/sv_show_tvcon"
141
        android:layout_marginLeft="15dp"
142
        android:layout_marginBottom="5dp"
143
        android:background="#2ff">
144
145
        <ImageView
146
            android:id="@+id/sv_show_user_avatar"
147
            android:layout_width="20dp"
148
            android:layout_height="20dp"
149
            android:layout_centerVertical="true"
150
            android:background="#fff" />
151
152
        <TextView
153
            android:id="@+id/sv_show_user_name"
154
            android:layout_width="wrap_content"
155
            android:layout_height="wrap_content"
156
            android:layout_marginLeft="5dp"
157
            android:layout_toEndOf="@+id/sv_show_user_avatar"
158
            android:text="adsdasasdad"
159
            android:textColor="#fff"
160
            android:textSize="12sp" />
161
162
        <TextView
163
            android:id="@+id/sv_show_user_time"
164
            android:layout_width="wrap_content"
165
            android:layout_height="wrap_content"
166
            android:layout_below="@+id/sv_show_user_name"
167
            android:layout_alignLeft="@+id/sv_show_user_name"
168
            android:text="adsdasasdad"
169
            android:textColor="#fff"
170
            android:textSize="12sp" />
171
    </RelativeLayout>
172
173
174
    <LinearLayout
175
        android:id="@+id/sv_show_topic"
176
        android:layout_width="wrap_content"
177
        android:layout_height="30dp"
178
        android:layout_above="@+id/sv_show_user_info"
179
        android:layout_marginStart="15dp"
180
        android:layout_marginEnd="15dp"
181
        android:layout_marginBottom="10dp"
182
        android:background="#b3303030"
183
        android:gravity="center_vertical">
184
185
        <View
186
            android:layout_width="12dp"
187
            android:layout_height="12dp"
188
            android:layout_marginStart="12dp"
189
            android:layout_marginEnd="4dp"
190
            android:background="#fff" />
191
192
        <TextView
193
            android:id="@+id/sv_show_topic_con"
194
            android:layout_width="wrap_content"
195
            android:layout_height="wrap_content"
196
            android:layout_marginRight="12dp"
197
            android:ellipsize="end"
198
            android:maxLines="1"
199
            android:text="显示出所有文字内容显示出所有文字内容显示出所有文字内容显示出所有文字内容显示出所有文字内容显示出所有文字内容显示出所有文字内容"
200
            android:textColor="#fff"
201
            android:textSize="11sp" />
202
    </LinearLayout>
203
204
</merge>

+ 74 - 0
app/src/main/res/layout/view_show_comment.xml

@ -0,0 +1,74 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="wrap_content"
5
    android:background="@drawable/bg_bottom_dialog">
6
7
    <TextView
8
        android:id="@+id/show_comment_title"
9
        android:layout_width="wrap_content"
10
        android:layout_height="wrap_content"
11
        android:layout_centerHorizontal="true"
12
        android:layout_marginTop="10dp"
13
        android:text="12sp12sp12sp"
14
        android:textColor="#c2c2c2"
15
        android:textSize="14sp" />
16
17
18
    <View
19
        android:id="@+id/show_comment_close"
20
        android:layout_width="22dp"
21
        android:layout_height="22dp"
22
        android:layout_alignParentEnd="true"
23
        android:layout_marginTop="9dp"
24
        android:layout_marginEnd="14dp"
25
        android:background="#fff" />
26
27
    <View
28
        android:layout_width="match_parent"
29
        android:layout_height="1dp"
30
        android:layout_marginTop="40dp"
31
        android:background="#505050" />
32
33
    <android.support.v7.widget.RecyclerView
34
        android:id="@+id/show_comment_lv"
35
        android:layout_width="wrap_content"
36
        android:layout_height="320dp"
37
        android:layout_marginBottom="54dp"
38
        android:layout_marginTop="40dp" />
39
40
    <LinearLayout
41
        android:layout_width="match_parent"
42
        android:layout_height="wrap_content"
43
        android:layout_below="@+id/show_comment_lv"
44
        android:background="#535353"
45
        android:layout_marginTop="-54dp"
46
        android:gravity="center_vertical"
47
        android:minHeight="54dp">
48
49
        <EditText
50
            android:id="@+id/show_comment_ed"
51
            android:layout_width="0dp"
52
            android:layout_height="wrap_content"
53
            android:layout_marginStart="14dp"
54
            android:layout_weight="1"
55
            android:background="@drawable/bg_comment_report"
56
            android:hint="写评论"
57
            android:minHeight="30dp"
58
            android:paddingStart="14dp"
59
            android:paddingEnd="14dp"
60
            android:textColorHint="#7e7e7e" />
61
62
        <TextView
63
            android:id="@+id/show_comment_publish"
64
            android:layout_width="wrap_content"
65
            android:layout_height="match_parent"
66
            android:gravity="center"
67
            android:paddingStart="13dp"
68
            android:paddingEnd="13dp"
69
            android:text="发布"
70
            android:textColor="#7e7e7e"
71
            android:textSize="14sp" />
72
    </LinearLayout>
73
74
</RelativeLayout>

+ 13 - 0
app/src/main/res/layout/view_show_view.xml

@ -0,0 +1,13 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="match_parent">
5
6
    <ImageView
7
        android:layout_width="match_parent"
8
        android:layout_height="match_parent"
9
        android:background="#fff0" />
10
11
    <include layout="@layout/view_show_bottom" />
12
13
</RelativeLayout>

+ 15 - 0
app/src/main/res/layout/view_show_zoom.xml

@ -0,0 +1,15 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="match_parent">
5
6
7
    <com.electric.chargingpile.widge.photoview.ZoomingViewpager
8
        android:id="@+id/view_show_zoomingphoto"
9
        android:layout_width="match_parent"
10
        android:layout_height="match_parent" />
11
12
13
    <include layout="@layout/view_show_bottom" />
14
15
</RelativeLayout>

+ 2 - 0
app/src/main/res/values/color.xml

@ -170,6 +170,8 @@
170 170
    <color name="color_888888">#888888</color>
171 171
    <color name="color_fb9349">#fb9349</color>
172 172

173
    <color name="sv_black">#FF000000</color>
174
    <color name="sv_white">#FFFFFF</color>
173 175

174 176

175 177


+ 12 - 0
app/src/main/res/values/strings.xml

@ -192,4 +192,16 @@
192 192
193 193
    <string name="refund_tip">退款说明:\n1、可退款金额为实际充值金额,不含充值赠送、添加站点赠送和活动赠送金额。申请退款时,赠送金额将清零,请慎重选择;\n2、退款过程中,账户将被冻结,无法充值,使用支付宝免密支付充电不受影响;\n3、微信充值订单6个月内,支付宝充值订单3个月内将原路返回,超出时间我们将联系您取得新的退款方式;\n4、退款将于3-7个工作日内完成,请耐心等待。</string>
194 194
195
196
    <string name="refresh_pull_to_refresh">Slide down to refresh</string>
197
    <string name="refresh_release_to_refresh">Release to refresh</string>
198
    <string name="refresh_refreshing">Refreshing...</string>
199
    <string name="refresh_succeed">Refreshed</string>
200
    <string name="refresh_fail">Refreshing failed</string>
201
    <string name="refresh_pullup_to_load">Slide up to upload more</string>
202
    <string name="refresh_release_to_load">Release to upload</string>
203
    <string name="refresh_loading">Loading...</string>
204
    <string name="refresh_load_succeed">Loading succeeded</string>
205
    <string name="refresh_load_succeed_nomore">That\'s All!</string>
206
    <string name="refresh_load_fail">Loading failed</string>
195 207
</resources>

+ 30 - 9
app/src/main/res/values/styles.xml

@ -1,4 +1,5 @@
1 1
<resources>
2

2 3
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
3 4
        <item name="colorPrimary">@color/colorPrimary</item>
4 5
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
@ -55,18 +56,18 @@
55 56
    <style name="Widget.ProgressBar.RegularProgressBar">
56 57
        <item name="android:indeterminateOnly">false</item>
57 58
        <item name="android:progressDrawable">@drawable/progressbar</item>
58
        <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
59
        <item name="android:indeterminateDrawable">
60
            @android:drawable/progress_indeterminate_horizontal
61
        </item>
59 62
        <item name="android:minHeight">1dip</item>
60 63
        <item name="android:maxHeight">10dip</item>
61 64
    </style>
62 65

63 66

64
        <!--<style name="MyRatingBar" parent="@android:style/Widget.RatingBar">-->
65
            <!--<item name="android:progressDrawable">@drawable/circle_rating_bar_full</item>-->
66

67
        <!--</style>-->
68

67
    <!--<style name="MyRatingBar" parent="@android:style/Widget.RatingBar">-->
68
    <!--<item name="android:progressDrawable">@drawable/circle_rating_bar_full</item>-->
69 69

70
    <!--</style>-->
70 71

71 72

72 73
    <style name="roomRatingBar" parent="@android:style/Widget.RatingBar">
@ -82,8 +83,6 @@
82 83
    </style>
83 84

84 85

85

86

87 86
    <style name="appointent_time_item_style">
88 87
        <item name="android:textColor">@drawable/appointent_item_tv_color_selector</item>
89 88
        <item name="android:layout_width">fill_parent</item>
@ -191,16 +190,19 @@
191 190
        <item name="android:windowBackground">@android:color/transparent</item>
192 191
        <item name="android:windowContentOverlay">@null</item>
193 192
    </style>
194
    <style name="dialogAnim" parent="android:Animation" >
193

194
    <style name="dialogAnim" parent="android:Animation">
195 195
        <item name="android:windowEnterAnimation">@anim/dialog_show</item>
196 196
        <item name="android:windowExitAnimation">@anim/dialog_dismiss</item>
197 197
    </style>
198

198 199
    <style name="divider_horizontal"><!-- 水平分割线 -->
199 200
        <item name="android:layout_width">fill_parent</item>
200 201
        <item name="android:layout_height">0.01dp</item>
201 202
        <item name="android:background">@android:color/darker_gray</item>
202 203
        <item name="android:orientation">vertical</item>
203 204
    </style>
205

204 206
    <style name="divider_vertical"><!-- 垂直分割线 -->
205 207
        <item name="android:layout_width">0.01dp</item>
206 208
        <item name="android:layout_height">fill_parent</item>
@ -220,6 +222,25 @@
220 222
        <item name="android:windowCloseOnTouchOutside">false</item>
221 223
    </style>
222 224

225

226
    <style name="bottomDialogStyle">
227
        <item name="android:windowBackground">@android:color/transparent</item>
228
        <item name="android:windowContentOverlay">@null</item>
229
        <item name="android:windowIsFloating">true</item>
230
        <item name="android:windowFrame">@null</item>
231
        <item name="android:backgroundDimEnabled">false</item>
232
        <item name="android:windowNoTitle">true</item>
233
        <item name="android:windowIsTranslucent">true</item>
234
        <item name="android:windowCloseOnTouchOutside">true</item>
235
        <item name="android:windowAnimationStyle">@style/BottomDialogAnimation</item>
236
    </style>
237

238
    <style name="BottomDialogAnimation">
239
        <item name="android:windowEnterAnimation">@anim/bottom_in_anim</item>
240
        <item name="android:windowExitAnimation">@anim/bottom_out_anim</item>
241
    </style>
242

243

223 244
    <style name="AnimationPreview">
224 245
        <item name="android:windowEnterAnimation">@anim/fade_in</item>
225 246
        <item name="android:windowExitAnimation">@anim/fade_out</item>

Gogs: Go Git Service

Gogs - Go Git Service

A painless self-hosted Git service

Easy to install

Simply run the binary for your platform. Or ship Gogs with Docker or Vagrant, or get it packaged.

Cross-platform

Gogs runs anywhere Go can compile for: Windows, Mac OS X, Linux, ARM, etc. Choose the one you love!

Lightweight

Gogs has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!

Open Source

It's all on GitHub! Join us by contributing to make this project even better. Don't be shy to be a contributor!

cdzApp - Gogs: Go Git Service

充电桩app代码

dxh e85505f328 首页面跳转更改 %!s(int64=7) %!d(string=hace) años
XRefreshView f780ba992e remove %!s(int64=7) %!d(string=hace) años
app e85505f328 首页面跳转更改 %!s(int64=7) %!d(string=hace) años
autolayout e1cf244fe0 remove Useless dependence %!s(int64=7) %!d(string=hace) años
gradle e1cf244fe0 remove Useless dependence %!s(int64=7) %!d(string=hace) años
ijkplayer-java 7082cc0853 remove %!s(int64=7) %!d(string=hace) años
keystore 06bd894653 init %!s(int64=7) %!d(string=hace) años
library 7082cc0853 remove %!s(int64=7) %!d(string=hace) años
zxing f780ba992e remove %!s(int64=7) %!d(string=hace) años
.gitignore 2fa5ae59bf remove %!s(int64=7) %!d(string=hace) años
build.gradle 2fa5ae59bf remove %!s(int64=7) %!d(string=hace) años
gradlew e1cf244fe0 remove Useless dependence %!s(int64=7) %!d(string=hace) años
gradlew.bat e1cf244fe0 remove Useless dependence %!s(int64=7) %!d(string=hace) años
icon_sgreen.png e1cf244fe0 remove Useless dependence %!s(int64=7) %!d(string=hace) años
settings.gradle 2fa5ae59bf remove %!s(int64=7) %!d(string=hace) años