() {
- @Override
- public SavedState createFromParcel(Parcel in, ClassLoader loader) {
- return new SavedState(in, loader);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- });
-
- SavedState(Parcel in, ClassLoader loader) {
- super(in);
- if (loader == null) {
- loader = getClass().getClassLoader();
- }
- position = in.readInt();
- adapterState = in.readParcelable(loader);
- this.loader = loader;
- }
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
- ss.position = mCurItem;
- if (mAdapter != null) {
- ss.adapterState = mAdapter.saveState();
- }
- return ss;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
-
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(ss.getSuperState());
-
- if (mAdapter != null) {
- mAdapter.restoreState(ss.adapterState, ss.loader);
- setCurrentItemInternal(ss.position, false, true);
- } else {
- mRestoredCurItem = ss.position;
- mRestoredAdapterState = ss.adapterState;
- mRestoredClassLoader = ss.loader;
- }
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- if (!checkLayoutParams(params)) {
- params = generateLayoutParams(params);
- }
- final LayoutParams lp = (LayoutParams) params;
- lp.isDecor |= child instanceof Decor;
- if (mInLayout) {
- if (lp != null && lp.isDecor) {
- throw new IllegalStateException("Cannot add pager decor view during layout");
- }
- lp.needsMeasure = true;
- addViewInLayout(child, index, params);
- } else {
- super.addView(child, index, params);
- }
-
- if (USE_CACHE) {
- if (child.getVisibility() != GONE) {
- child.setDrawingCacheEnabled(mScrollingCacheEnabled);
- } else {
- child.setDrawingCacheEnabled(false);
- }
- }
- }
-
- @Override
- public void removeView(View view) {
- if (mInLayout) {
- removeViewInLayout(view);
- } else {
- super.removeView(view);
- }
- }
-
- ItemInfo infoForChild(View child) {
- for (int i = 0; i < mItems.size(); i++) {
- ItemInfo ii = mItems.get(i);
- if (mAdapter.isViewFromObject(child, ii.object)) {
- return ii;
- }
- }
- return null;
- }
-
- ItemInfo infoForAnyChild(View child) {
- ViewParent parent;
- while ((parent = child.getParent()) != this) {
- if (parent == null || !(parent instanceof View)) {
- return null;
- }
- child = (View) parent;
- }
- return infoForChild(child);
- }
-
- ItemInfo infoForPosition(int position) {
- for (int i = 0; i < mItems.size(); i++) {
- ItemInfo ii = mItems.get(i);
- if (ii.position == position) {
- return ii;
- }
- }
- return null;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mFirstLayout = true;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // For simple implementation, our internal size is always 0.
- // We depend on the container to specify the layout size of
- // our view. We can't really know what it is since we will be
- // adding and removing different arbitrary views and do not
- // want the layout to change as this happens.
- setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
- getDefaultSize(0, heightMeasureSpec));
-
- final int measuredHeight = getMeasuredHeight();
- final int maxGutterSize = measuredHeight / 10;
- mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);
-
- // Children are just made to fill our space.
- int childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
- int childHeightSize = measuredHeight - getPaddingTop() - getPaddingBottom();
-
- /*
- * Make sure all children have been properly measured. Decor views first.
- * Right now we cheat and make this less complicated by assuming decor
- * views won't intersect. We will pin to edges based on gravity.
- */
- int size = getChildCount();
- for (int i = 0; i < size; ++i) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp != null && lp.isDecor) {
- final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- int widthMode = MeasureSpec.AT_MOST;
- int heightMode = MeasureSpec.AT_MOST;
- boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
- boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;
-
- if (consumeVertical) {
- widthMode = MeasureSpec.EXACTLY;
- } else if (consumeHorizontal) {
- heightMode = MeasureSpec.EXACTLY;
- }
-
- int widthSize = childWidthSize;
- int heightSize = childHeightSize;
- if (lp.width != LayoutParams.WRAP_CONTENT) {
- widthMode = MeasureSpec.EXACTLY;
- if (lp.width != LayoutParams.FILL_PARENT) {
- widthSize = lp.width;
- }
- }
- if (lp.height != LayoutParams.WRAP_CONTENT) {
- heightMode = MeasureSpec.EXACTLY;
- if (lp.height != LayoutParams.FILL_PARENT) {
- heightSize = lp.height;
- }
- }
- final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
- final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
- child.measure(widthSpec, heightSpec);
-
- if (consumeVertical) {
- childHeightSize -= child.getMeasuredHeight();
- } else if (consumeHorizontal) {
- childWidthSize -= child.getMeasuredWidth();
- }
- }
- }
- }
-
- mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
- mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);
-
- // Make sure we have created all fragments that we need to have shown.
- mInLayout = true;
- populate();
- mInLayout = false;
-
- // Page views next.
- size = getChildCount();
- for (int i = 0; i < size; ++i) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child
- + ": " + mChildWidthMeasureSpec);
-
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp == null || !lp.isDecor) {
- final int heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (childHeightSize * lp.heightFactor), MeasureSpec.EXACTLY);
- child.measure(mChildWidthMeasureSpec, heightSpec);
- }
- }
- }
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- // Make sure scroll position is set correctly.
- if (h != oldh) {
- recomputeScrollPosition(h, oldh, mPageMargin, mPageMargin);
- }
- }
-
- private void recomputeScrollPosition(int height, int oldHeight, int margin, int oldMargin) {
- if (oldHeight > 0 && !mItems.isEmpty()) {
- final int heightWithMargin = height - getPaddingTop() - getPaddingBottom() + margin;
- final int oldHeightWithMargin = oldHeight - getPaddingTop() - getPaddingBottom()
- + oldMargin;
- final int ypos = getScrollY();
- final float pageOffset = (float) ypos / oldHeightWithMargin;
- final int newOffsetPixels = (int) (pageOffset * heightWithMargin);
-
- scrollTo(getScrollX(), newOffsetPixels);
- if (!mScroller.isFinished()) {
- // We now return to your regularly scheduled scroll, already in progress.
- final int newDuration = mScroller.getDuration() - mScroller.timePassed();
- ItemInfo targetInfo = infoForPosition(mCurItem);
- mScroller.startScroll(0, newOffsetPixels,
- 0, (int) (targetInfo.offset * height), newDuration);
- }
- } else {
- final ItemInfo ii = infoForPosition(mCurItem);
- final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;
- final int scrollPos = (int) (scrollOffset *
- (height - getPaddingTop() - getPaddingBottom()));
- if (scrollPos != getScrollY()) {
- completeScroll(false);
- scrollTo(getScrollX(), scrollPos);
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int count = getChildCount();
- int width = r - l;
- int height = b - t;
- int paddingLeft = getPaddingLeft();
- int paddingTop = getPaddingTop();
- int paddingRight = getPaddingRight();
- int paddingBottom = getPaddingBottom();
- final int scrollY = getScrollY();
-
- int decorCount = 0;
-
- // First pass - decor views. We need to do this in two passes so that
- // we have the proper offsets for non-decor views later.
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- int childLeft = 0;
- int childTop = 0;
- if (lp.isDecor) {
- final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- switch (hgrav) {
- default:
- childLeft = paddingLeft;
- break;
- case Gravity.LEFT:
- childLeft = paddingLeft;
- paddingLeft += child.getMeasuredWidth();
- break;
- case Gravity.CENTER_HORIZONTAL:
- childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
- paddingLeft);
- break;
- case Gravity.RIGHT:
- childLeft = width - paddingRight - child.getMeasuredWidth();
- paddingRight += child.getMeasuredWidth();
- break;
- }
- switch (vgrav) {
- default:
- childTop = paddingTop;
- break;
- case Gravity.TOP:
- childTop = paddingTop;
- paddingTop += child.getMeasuredHeight();
- break;
- case Gravity.CENTER_VERTICAL:
- childTop = Math.max((height - child.getMeasuredHeight()) / 2,
- paddingTop);
- break;
- case Gravity.BOTTOM:
- childTop = height - paddingBottom - child.getMeasuredHeight();
- paddingBottom += child.getMeasuredHeight();
- break;
- }
- childTop += scrollY;
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- decorCount++;
- }
- }
- }
-
- final int childHeight = height - paddingTop - paddingBottom;
- // Page views. Do this once we have the right padding offsets from above.
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- ItemInfo ii;
- if (!lp.isDecor && (ii = infoForChild(child)) != null) {
- int toff = (int) (childHeight * ii.offset);
- int childLeft = paddingLeft;
- int childTop = paddingTop + toff;
- if (lp.needsMeasure) {
- // This was added during layout and needs measurement.
- // Do it now that we know what we're working with.
- lp.needsMeasure = false;
- final int widthSpec = MeasureSpec.makeMeasureSpec(
- (int) (width - paddingLeft - paddingRight),
- MeasureSpec.EXACTLY);
- final int heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (childHeight * lp.heightFactor),
- MeasureSpec.EXACTLY);
- child.measure(widthSpec, heightSpec);
- }
- if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
- + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
- + "x" + child.getMeasuredHeight());
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- }
- }
- }
- mLeftPageBounds = paddingLeft;
- mRightPageBounds = width - paddingRight;
- mDecorChildCount = decorCount;
-
- if (mFirstLayout) {
- scrollToItem(mCurItem, false, 0, false);
- }
- mFirstLayout = false;
- }
-
- @Override
- public void computeScroll() {
- if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
-
- if (oldX != x || oldY != y) {
- scrollTo(x, y);
- if (!pageScrolled(y)) {
- mScroller.abortAnimation();
- scrollTo(x, 0);
- }
- }
-
- // Keep on drawing until the animation has finished.
- ViewCompat.postInvalidateOnAnimation(this);
- return;
- }
-
- // Done with scroll, clean up state.
- completeScroll(true);
- }
-
- private boolean pageScrolled(int ypos) {
- if (mItems.size() == 0) {
- mCalledSuper = false;
- onPageScrolled(0, 0, 0);
- if (!mCalledSuper) {
- throw new IllegalStateException(
- "onPageScrolled did not call superclass implementation");
- }
- return false;
- }
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int height = getClientHeight();
- final int heightWithMargin = height + mPageMargin;
- final float marginOffset = (float) mPageMargin / height;
- final int currentPage = ii.position;
- final float pageOffset = (((float) ypos / height) - ii.offset) /
- (ii.heightFactor + marginOffset);
- final int offsetPixels = (int) (pageOffset * heightWithMargin);
-
- mCalledSuper = false;
- onPageScrolled(currentPage, pageOffset, offsetPixels);
- if (!mCalledSuper) {
- throw new IllegalStateException(
- "onPageScrolled did not call superclass implementation");
- }
- return true;
- }
-
- /**
- * This method will be invoked when the current page is scrolled, either as part
- * of a programmatically initiated smooth scroll or a user initiated touch scroll.
- * If you override this method you must call through to the superclass implementation
- * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
- * returns.
- *
- * @param position Position index of the first page currently being displayed.
- * Page position+1 will be visible if positionOffset is nonzero.
- * @param offset Value from [0, 1) indicating the offset from the page at position.
- * @param offsetPixels Value in pixels indicating the offset from position.
- */
- protected void onPageScrolled(int position, float offset, int offsetPixels) {
- // Offset any decor views if needed - keep them on-screen at all times.
- if (mDecorChildCount > 0) {
- final int scrollY = getScrollY();
- int paddingTop = getPaddingTop();
- int paddingBottom = getPaddingBottom();
- final int height = getHeight();
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.isDecor) continue;
-
- final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- int childTop = 0;
- switch (vgrav) {
- default:
- childTop = paddingTop;
- break;
- case Gravity.TOP:
- childTop = paddingTop;
- paddingTop += child.getHeight();
- break;
- case Gravity.CENTER_VERTICAL:
- childTop = Math.max((height - child.getMeasuredHeight()) / 2,
- paddingTop);
- break;
- case Gravity.BOTTOM:
- childTop = height - paddingBottom - child.getMeasuredHeight();
- paddingBottom += child.getMeasuredHeight();
- break;
- }
- childTop += scrollY;
-
- final int childOffset = childTop - child.getTop();
- if (childOffset != 0) {
- child.offsetTopAndBottom(childOffset);
- }
- }
- }
-
- if (mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
- }
- if (mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
- }
-
- if (mPageTransformer != null) {
- final int scrollY = getScrollY();
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.isDecor) continue;
-
- final float transformPos = (float) (child.getTop() - scrollY) / getClientHeight();
- mPageTransformer.transformPage(child, transformPos);
- }
- }
-
- mCalledSuper = true;
- }
-
- private void completeScroll(boolean postEvents) {
- boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
- if (needPopulate) {
- // Done with scroll, no longer want to cache view drawing.
- setScrollingCacheEnabled(false);
- mScroller.abortAnimation();
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
- if (oldX != x || oldY != y) {
- scrollTo(x, y);
- }
- }
- mPopulatePending = false;
- for (int i = 0; i < mItems.size(); i++) {
- ItemInfo ii = mItems.get(i);
- if (ii.scrolling) {
- needPopulate = true;
- ii.scrolling = false;
- }
- }
- if (needPopulate) {
- if (postEvents) {
- ViewCompat.postOnAnimation(this, mEndScrollRunnable);
- } else {
- mEndScrollRunnable.run();
- }
- }
- }
-
- private boolean isGutterDrag(float y, float dy) {
- return (y < mGutterSize && dy > 0) || (y > getHeight() - mGutterSize && dy < 0);
- }
-
- private void enableLayers(boolean enable) {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final int layerType = enable ?
- ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;
- ViewCompat.setLayerType(getChildAt(i), layerType, null);
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- /*
- * This method JUST determines whether we want to intercept the motion.
- * If we return true, onMotionEvent will be called and we do the actual
- * scrolling there.
- */
-
- final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
-
- // Always take care of the touch gesture being complete.
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- // Release the drag.
- if (DEBUG) Log.v(TAG, "Intercept done!");
- mIsBeingDragged = false;
- mIsUnableToDrag = false;
- mActivePointerId = INVALID_POINTER;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- return false;
- }
-
- // Nothing more to do here if we have decided whether or not we
- // are dragging.
- if (action != MotionEvent.ACTION_DOWN) {
- if (mIsBeingDragged) {
- if (DEBUG) Log.v(TAG, "Intercept returning true!");
- return true;
- }
- if (mIsUnableToDrag) {
- if (DEBUG) Log.v(TAG, "Intercept returning false!");
- return false;
- }
- }
-
- switch (action) {
- case MotionEvent.ACTION_MOVE: {
- /*
- * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
- */
-
- /*
- * Locally do absolute value. mLastMotionY is set to the y value
- * of the down event.
- */
- final int activePointerId = mActivePointerId;
- if (activePointerId == INVALID_POINTER) {
- // If we don't have a valid id, the touch down wasn't on content.
- break;
- }
-
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
- final float y = MotionEventCompat.getY(ev, pointerIndex);
- final float dy = y - mLastMotionY;
- final float yDiff = Math.abs(dy);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
- final float xDiff = Math.abs(x - mInitialMotionX);
- if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
-
- if (dy != 0 && !isGutterDrag(mLastMotionY, dy) &&
- canScroll(this, false, (int) dy, (int) x, (int) y)) {
- // Nested view has scrollable area under this point. Let it be handled there.
- mLastMotionX = x;
- mLastMotionY = y;
- mIsUnableToDrag = true;
- return false;
- }
- if (yDiff > mTouchSlop && yDiff * 0.5f > xDiff) {
- if (DEBUG) Log.v(TAG, "Starting drag!");
- mIsBeingDragged = true;
- requestParentDisallowInterceptTouchEvent(true);
- setScrollState(SCROLL_STATE_DRAGGING);
- mLastMotionY = dy > 0 ? mInitialMotionY + mTouchSlop :
- mInitialMotionY - mTouchSlop;
- mLastMotionX = x;
- setScrollingCacheEnabled(true);
- } else if (xDiff > mTouchSlop) {
- // The finger has moved enough in the vertical
- // direction to be counted as a drag... abort
- // any attempt to drag horizontally, to work correctly
- // with children that have scrolling containers.
- if (DEBUG) Log.v(TAG, "Starting unable to drag!");
- mIsUnableToDrag = true;
- }
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- if (performDrag(y)) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
- break;
- }
-
- case MotionEvent.ACTION_DOWN: {
- /*
- * Remember location of down touch.
- * ACTION_DOWN always refers to pointer index 0.
- */
- mLastMotionX = mInitialMotionX = ev.getX();
- mLastMotionY = mInitialMotionY = ev.getY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- mIsUnableToDrag = false;
-
- mScroller.computeScrollOffset();
- if (mScrollState == SCROLL_STATE_SETTLING &&
- Math.abs(mScroller.getFinalY() - mScroller.getCurrY()) > mCloseEnough) {
- // Let the user 'catch' the pager as it animates.
- mScroller.abortAnimation();
- mPopulatePending = false;
- populate();
- mIsBeingDragged = true;
- requestParentDisallowInterceptTouchEvent(true);
- setScrollState(SCROLL_STATE_DRAGGING);
- } else {
- completeScroll(false);
- mIsBeingDragged = false;
- }
-
- if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
- + " mIsBeingDragged=" + mIsBeingDragged
- + "mIsUnableToDrag=" + mIsUnableToDrag);
- break;
- }
-
- case MotionEventCompat.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- /*
- * The only time we want to intercept motion events is if we are in the
- * drag mode.
- */
- return mIsBeingDragged;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mFakeDragging) {
- // A fake drag is in progress already, ignore this real one
- // but still eat the touch events.
- // (It is likely that the user is multi-touching the screen.)
- return true;
- }
-
- if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
- // Don't handle edge touches immediately -- they may actually belong to one of our
- // descendants.
- return false;
- }
-
- if (mAdapter == null || mAdapter.getCount() == 0) {
- // Nothing to present or scroll; nothing to touch.
- return false;
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- final int action = ev.getAction();
- boolean needsInvalidate = false;
-
- switch (action & MotionEventCompat.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- mScroller.abortAnimation();
- mPopulatePending = false;
- populate();
-
- // Remember where the motion event started
- mLastMotionX = mInitialMotionX = ev.getX();
- mLastMotionY = mInitialMotionY = ev.getY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- break;
- }
- case MotionEvent.ACTION_MOVE:
- if (!mIsBeingDragged) {
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- final float y = MotionEventCompat.getY(ev, pointerIndex);
- final float yDiff = Math.abs(y - mLastMotionY);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
- final float xDiff = Math.abs(x - mLastMotionX);
- if (DEBUG)
- Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
- if (yDiff > mTouchSlop && yDiff > xDiff) {
- if (DEBUG) Log.v(TAG, "Starting drag!");
- mIsBeingDragged = true;
- requestParentDisallowInterceptTouchEvent(true);
- mLastMotionY = y - mInitialMotionY > 0 ? mInitialMotionY + mTouchSlop :
- mInitialMotionY - mTouchSlop;
- mLastMotionX = x;
- setScrollState(SCROLL_STATE_DRAGGING);
- setScrollingCacheEnabled(true);
-
- // Disallow Parent Intercept, just in case
- ViewParent parent = getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
- }
- // Not else! Note that mIsBeingDragged can be set above.
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- final int activePointerIndex = MotionEventCompat.findPointerIndex(
- ev, mActivePointerId);
- final float y = MotionEventCompat.getY(ev, activePointerIndex);
- needsInvalidate |= performDrag(y);
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mIsBeingDragged) {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
- velocityTracker, mActivePointerId);
- mPopulatePending = true;
- final int height = getClientHeight();
- final int scrollY = getScrollY();
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
- final float pageOffset = (((float) scrollY / height) - ii.offset) / ii.heightFactor;
- final int activePointerIndex =
- MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- final float y = MotionEventCompat.getY(ev, activePointerIndex);
- final int totalDelta = (int) (y - mInitialMotionY);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
-
- mActivePointerId = INVALID_POINTER;
- endDrag();
- needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- if (mIsBeingDragged) {
- scrollToItem(mCurItem, true, 0, false);
- mActivePointerId = INVALID_POINTER;
- endDrag();
- needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();
- }
- break;
- case MotionEventCompat.ACTION_POINTER_DOWN: {
- final int index = MotionEventCompat.getActionIndex(ev);
- final float y = MotionEventCompat.getY(ev, index);
- mLastMotionY = y;
- mActivePointerId = MotionEventCompat.getPointerId(ev, index);
- break;
- }
- case MotionEventCompat.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- mLastMotionY = MotionEventCompat.getY(ev,
- MotionEventCompat.findPointerIndex(ev, mActivePointerId));
- break;
- }
- if (needsInvalidate) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- return true;
- }
-
- private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {
- final ViewParent parent = getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
- }
-
- private boolean performDrag(float y) {
- boolean needsInvalidate = false;
-
- final float deltaY = mLastMotionY - y;
- mLastMotionY = y;
-
- float oldScrollY = getScrollY();
- float scrollY = oldScrollY + deltaY;
- final int height = getClientHeight();
-
- float topBound = height * mFirstOffset;
- float bottomBound = height * mLastOffset;
- boolean topAbsolute = true;
- boolean bottomAbsolute = true;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- topAbsolute = false;
- topBound = firstItem.offset * height;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- bottomAbsolute = false;
- bottomBound = lastItem.offset * height;
- }
-
- if (scrollY < topBound) {
- if (topAbsolute) {
- float over = topBound - scrollY;
- needsInvalidate = mTopEdge.onPull(Math.abs(over) / height);
- }
- scrollY = topBound;
- } else if (scrollY > bottomBound) {
- if (bottomAbsolute) {
- float over = scrollY - bottomBound;
- needsInvalidate = mBottomEdge.onPull(Math.abs(over) / height);
- }
- scrollY = bottomBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollY - (int) scrollY;
- scrollTo(getScrollX(), (int) scrollY);
- pageScrolled((int) scrollY);
-
- return needsInvalidate;
- }
-
- /**
- * @return Info about the page at the current scroll position.
- * This can be synthetic for a missing middle page; the 'object' field can be null.
- */
- private ItemInfo infoForCurrentScrollPosition() {
- final int height = getClientHeight();
- final float scrollOffset = height > 0 ? (float) getScrollY() / height : 0;
- final float marginOffset = height > 0 ? (float) mPageMargin / height : 0;
- int lastPos = -1;
- float lastOffset = 0.f;
- float lastHeight = 0.f;
- boolean first = true;
-
- ItemInfo lastItem = null;
- for (int i = 0; i < mItems.size(); i++) {
- ItemInfo ii = mItems.get(i);
- float offset;
- if (!first && ii.position != lastPos + 1) {
- // Create a synthetic item for a missing page.
- ii = mTempItem;
- ii.offset = lastOffset + lastHeight + marginOffset;
- ii.position = lastPos + 1;
- ii.heightFactor = mAdapter.getPageWidth(ii.position);
- i--;
- }
- offset = ii.offset;
-
- final float topBound = offset;
- final float bottomBound = offset + ii.heightFactor + marginOffset;
- if (first || scrollOffset >= topBound) {
- if (scrollOffset < bottomBound || i == mItems.size() - 1) {
- return ii;
- }
- } else {
- return lastItem;
- }
- first = false;
- lastPos = ii.position;
- lastOffset = offset;
- lastHeight = ii.heightFactor;
- lastItem = ii;
- }
-
- return lastItem;
- }
-
- private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaY) {
- int targetPage;
- if (Math.abs(deltaY) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
- targetPage = velocity > 0 ? currentPage : currentPage + 1;
- } else {
- final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
- targetPage = (int) (currentPage + pageOffset + truncator);
- }
-
- if (mItems.size() > 0) {
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-
- // Only let the user target pages we have items for
- targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
- }
-
- return targetPage;
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- boolean needsInvalidate = false;
-
- final int overScrollMode = ViewCompat.getOverScrollMode(this);
- if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
- (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&
- mAdapter != null && mAdapter.getCount() > 1)) {
- if (!mTopEdge.isFinished()) {
- final int restoreCount = canvas.save();
- final int height = getHeight();
- final int width = getWidth() - getPaddingLeft() - getPaddingRight();
-
- canvas.translate(getPaddingLeft(), mFirstOffset * height);
- mTopEdge.setSize(width, height);
- needsInvalidate |= mTopEdge.draw(canvas);
- canvas.restoreToCount(restoreCount);
- }
- if (!mBottomEdge.isFinished()) {
- final int restoreCount = canvas.save();
- final int height = getHeight();
- final int width = getWidth() - getPaddingLeft() - getPaddingRight();
-
- canvas.rotate(180);
- canvas.translate(-width - getPaddingLeft(), -(mLastOffset + 1) * height);
- mBottomEdge.setSize(width, height);
- needsInvalidate |= mBottomEdge.draw(canvas);
- canvas.restoreToCount(restoreCount);
- }
- } else {
- mTopEdge.finish();
- mBottomEdge.finish();
- }
-
- if (needsInvalidate) {
- // Keep animating
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // Draw the margin drawable between pages if needed.
- if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {
- final int scrollY = getScrollY();
- final int height = getHeight();
-
- final float marginOffset = (float) mPageMargin / height;
- int itemIndex = 0;
- ItemInfo ii = mItems.get(0);
- float offset = ii.offset;
- final int itemCount = mItems.size();
- final int firstPos = ii.position;
- final int lastPos = mItems.get(itemCount - 1).position;
- for (int pos = firstPos; pos < lastPos; pos++) {
- while (pos > ii.position && itemIndex < itemCount) {
- ii = mItems.get(++itemIndex);
- }
-
- float drawAt;
- if (pos == ii.position) {
- drawAt = (ii.offset + ii.heightFactor) * height;
- offset = ii.offset + ii.heightFactor + marginOffset;
- } else {
- float heightFactor = mAdapter.getPageWidth(pos);
- drawAt = (offset + heightFactor) * height;
- offset += heightFactor + marginOffset;
- }
-
- if (drawAt + mPageMargin > scrollY) {
- mMarginDrawable.setBounds(mLeftPageBounds, (int) drawAt,
- mRightPageBounds, (int) (drawAt + mPageMargin + 0.5f));
- mMarginDrawable.draw(canvas);
- }
-
- if (drawAt > scrollY + height) {
- break; // No more visible, no sense in continuing
- }
- }
- }
- }
-
- /**
- * Start a fake drag of the pager.
- *
- * A fake drag can be useful if you want to synchronize the motion of the ViewPager
- * with the touch scrolling of another view, while still letting the ViewPager
- * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
- * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
- * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
- *
- * During a fake drag the ViewPager will ignore all touch events. If a real drag
- * is already in progress, this method will return false.
- *
- * @return true if the fake drag began successfully, false if it could not be started.
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean beginFakeDrag() {
- if (mIsBeingDragged) {
- return false;
- }
- mFakeDragging = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- mInitialMotionY = mLastMotionY = 0;
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- mFakeDragBeginTime = time;
- return true;
- }
-
- /**
- * End a fake drag of the pager.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
+public class VerticalViewPager extends PageSlider implements PageSlider.PageChangedListener {
+ private static final int SLID_LEFT = 0;
+ private static final int SLID_LEFT_ELASTIC = 1;
+ private static final int SLID_RIGHT = 2;
+ private static final int SLID_RIGHT_ELASTIC = 3;
+ private static final int SLID_NONE = 4;
+ private static final int SCROLL_RIGHT = -1;
+ private int mCurrentState = 0;
+ private int mCurrentItemPos = 0;
+ private int mItemPosOffsetPixels = 0;
+ private int mLastScrollState;
+ private float mItemPosOffset = 0;
+ private float mOriginalOffset = 0;
+ private List mComponents;
+ private VerticalViewPager.PageTransformer mPageTransformer;
+
+ /**
+ * 此类为了适配PageSlide滑动监听参数问题
+ *
+ * @param context context
*/
- public void endFakeDrag() {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
- velocityTracker, mActivePointerId);
- mPopulatePending = true;
- final int height = getClientHeight();
- final int scrollY = getScrollY();
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
- final float pageOffset = (((float) scrollY / height) - ii.offset) / ii.heightFactor;
- final int totalDelta = (int) (mLastMotionY - mInitialMotionY);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
- endDrag();
-
- mFakeDragging = false;
+ public VerticalViewPager(Context context) {
+ this(context, null);
}
/**
- * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
+ * 此类为了适配PageSlide滑动监听参数问题
*
- * @param yOffset Offset in pixels to drag by.
- * @see #beginFakeDrag()
- * @see #endFakeDrag()
+ * @param context context
+ * @param attrSet attrSet
*/
- public void fakeDragBy(float yOffset) {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- mLastMotionY += yOffset;
-
- float oldScrollY = getScrollY();
- float scrollY = oldScrollY - yOffset;
- final int height = getClientHeight();
-
- float topBound = height * mFirstOffset;
- float bottomBound = height * mLastOffset;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- topBound = firstItem.offset * height;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- bottomBound = lastItem.offset * height;
- }
-
- if (scrollY < topBound) {
- scrollY = topBound;
- } else if (scrollY > bottomBound) {
- scrollY = bottomBound;
- }
- // Don't lose the rounded component
- mLastMotionY += scrollY - (int) scrollY;
- scrollTo(getScrollX(), (int) scrollY);
- pageScrolled((int) scrollY);
-
- // Synthesize an event for the VelocityTracker.
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
- 0, mLastMotionY, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
+ public VerticalViewPager(Context context, AttrSet attrSet) {
+ this(context, attrSet, null);
+ addPageChangedListener(this);
}
/**
- * Returns true if a fake drag is in progress.
+ * 此类为了适配PageSlide滑动监听参数问题
*
- * @return true if currently in a fake drag, false otherwise.
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
+ * @param context context
+ * @param attrSet attrSet
+ * @param styleName style name
*/
- public boolean isFakeDragging() {
- return mFakeDragging;
- }
-
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);
- mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
- }
-
- private void endDrag() {
- mIsBeingDragged = false;
- mIsUnableToDrag = false;
-
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private void setScrollingCacheEnabled(boolean enabled) {
- if (mScrollingCacheEnabled != enabled) {
- mScrollingCacheEnabled = enabled;
- if (USE_CACHE) {
- final int size = getChildCount();
- for (int i = 0; i < size; ++i) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- child.setDrawingCacheEnabled(enabled);
- }
- }
- }
- }
+ public VerticalViewPager(Context context, AttrSet attrSet, String styleName) {
+ super(context, attrSet, styleName);
}
- public boolean internalCanScrollVertically(int direction) {
- if (mAdapter == null) {
- return false;
- }
-
- final int height = getClientHeight();
- final int scrollY = getScrollY();
- if (direction < 0) {
- return (scrollY > (int) (height * mFirstOffset));
- } else if (direction > 0) {
- return (scrollY < (int) (height * mLastOffset));
- } else {
- return false;
- }
+ @Override
+ public void onPageSliding(int position, float offset, int offsetPixels) {
+ onPageScrolled(position, offset, offsetPixels);
}
- /**
- * Tests scrollability within child views of v given a delta of dx.
- *
- * @param v View to test for horizontal scrollability
- * @param checkV Whether the view v passed should itself be checked for scrollability (true),
- * or just its children (false).
- * @param dy Delta scrolled in pixels
- * @param x X coordinate of the active touch point
- * @param y Y coordinate of the active touch point
- * @return true if child views of v can be scrolled by delta of dx.
- */
- protected boolean canScroll(View v, boolean checkV, int dy, int x, int y) {
- if (v instanceof ViewGroup) {
- final ViewGroup group = (ViewGroup) v;
- final int scrollX = v.getScrollX();
- final int scrollY = v.getScrollY();
- final int count = group.getChildCount();
- // Count backwards - let topmost views consume scroll distance first.
- for (int i = count - 1; i >= 0; i--) {
- // TODO: Add versioned support here for transformed views.
- // This will not work for transformed views in Honeycomb+
- final View child = group.getChildAt(i);
- if (y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
- x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
- canScroll(child, true, dy, x + scrollX - child.getLeft(),
- y + scrollY - child.getTop())) {
- return true;
- }
- }
- }
-
- return checkV && ViewCompat.canScrollVertically(v, -dy);
+ @Override
+ public void onPageSlideStateChanged(int state) {
+ mCurrentState = state;
}
@Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- // Let the focused view and/or our descendants get the key first
- return super.dispatchKeyEvent(event) || executeKeyEvent(event);
+ public void onPageChosen(int i) {
+ // NOTHING
}
/**
- * You can call this function yourself to have the scroll view perform
- * scrolling from a key event, just as if the event had been dispatched to
- * it by the view hierarchy.
+ * 设置PageSlide所有子组件
*
- * @param event The key event to execute.
- * @return Return true if the event was handled, else false.
+ * @param components 子组件集合
*/
- public boolean executeKeyEvent(KeyEvent event) {
- boolean handled = false;
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- handled = arrowScroll(FOCUS_LEFT);
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- handled = arrowScroll(FOCUS_RIGHT);
- break;
- case KeyEvent.KEYCODE_TAB:
- if (Build.VERSION.SDK_INT >= 11) {
- // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD
- // before Android 3.0. Ignore the tab key on those devices.
- if (KeyEventCompat.hasNoModifiers(event)) {
- handled = arrowScroll(FOCUS_FORWARD);
- } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
- handled = arrowScroll(FOCUS_BACKWARD);
- }
- }
- break;
- }
- }
- return handled;
- }
-
- public boolean arrowScroll(int direction) {
- View currentFocused = findFocus();
- if (currentFocused == this) {
- currentFocused = null;
- } else if (currentFocused != null) {
- boolean isChild = false;
- for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
- parent = parent.getParent()) {
- if (parent == this) {
- isChild = true;
- break;
- }
- }
- if (!isChild) {
- // This would cause the focus search down below to fail in fun ways.
- final StringBuilder sb = new StringBuilder();
- sb.append(currentFocused.getClass().getSimpleName());
- for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
- parent = parent.getParent()) {
- sb.append(" => ").append(parent.getClass().getSimpleName());
- }
- Log.e(TAG, "arrowScroll tried to find focus based on non-child " +
- "current focused view " + sb.toString());
- currentFocused = null;
- }
- }
-
- boolean handled = false;
-
- View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
- direction);
- if (nextFocused != null && nextFocused != currentFocused) {
- if (direction == View.FOCUS_UP) {
- // If there is nothing to the left, or this is causing us to
- // jump to the right, then what we really want to do is page left.
- final int nextTop = getChildRectInPagerCoordinates(mTempRect, nextFocused).top;
- final int currTop = getChildRectInPagerCoordinates(mTempRect, currentFocused).top;
- if (currentFocused != null && nextTop >= currTop) {
- handled = pageUp();
- } else {
- handled = nextFocused.requestFocus();
- }
- } else if (direction == View.FOCUS_DOWN) {
- // If there is nothing to the right, or this is causing us to
- // jump to the left, then what we really want to do is page right.
- final int nextDown = getChildRectInPagerCoordinates(mTempRect, nextFocused).bottom;
- final int currDown = getChildRectInPagerCoordinates(mTempRect, currentFocused).bottom;
- if (currentFocused != null && nextDown <= currDown) {
- handled = pageDown();
- } else {
- handled = nextFocused.requestFocus();
- }
- }
- } else if (direction == FOCUS_UP || direction == FOCUS_BACKWARD) {
- // Trying to move left and nothing there; try to page.
- handled = pageUp();
- } else if (direction == FOCUS_DOWN || direction == FOCUS_FORWARD) {
- // Trying to move right and nothing there; try to page.
- handled = pageDown();
- }
- if (handled) {
- playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
- }
- return handled;
- }
-
- private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {
- if (outRect == null) {
- outRect = new Rect();
- }
- if (child == null) {
- outRect.set(0, 0, 0, 0);
- return outRect;
- }
- outRect.left = child.getLeft();
- outRect.right = child.getRight();
- outRect.top = child.getTop();
- outRect.bottom = child.getBottom();
-
- ViewParent parent = child.getParent();
- while (parent instanceof ViewGroup && parent != this) {
- final ViewGroup group = (ViewGroup) parent;
- outRect.left += group.getLeft();
- outRect.right += group.getRight();
- outRect.top += group.getTop();
- outRect.bottom += group.getBottom();
-
- parent = group.getParent();
- }
- return outRect;
- }
-
- boolean pageUp() {
- if (mCurItem > 0) {
- setCurrentItem(mCurItem - 1, true);
- return true;
- }
- return false;
- }
-
- boolean pageDown() {
- if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {
- setCurrentItem(mCurItem + 1, true);
- return true;
- }
- return false;
+ public void setPages(List components) {
+ this.mComponents = components;
}
/**
- * We only want the current page that is being shown to be focusable.
+ * 设置动画接口
+ *
+ * @param pageTransformer 动画接口实现类
*/
- @Override
- public void addFocusables(ArrayList views, int direction, int focusableMode) {
- final int focusableCount = views.size();
-
- final int descendantFocusability = getDescendantFocusability();
-
- if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem) {
- child.addFocusables(views, direction, focusableMode);
- }
- }
- }
- }
-
- // we add ourselves (if focusable) in all cases except for when we are
- // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
- // to avoid the focus search finding layouts when a more precise search
- // among the focusable children would be more interesting.
- if (
- descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
- // No focusable descendants
- (focusableCount == views.size())) {
- // Note that we can't call the superclass here, because it will
- // add all views in. So we need to do the same thing View does.
- if (!isFocusable()) {
- return;
- }
- if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
- isInTouchMode() && !isFocusableInTouchMode()) {
- return;
- }
- if (views != null) {
- views.add(this);
- }
- }
+ public void setPageTransformer(VerticalViewPager.PageTransformer pageTransformer) {
+ this.mPageTransformer = pageTransformer;
}
- /**
- * We only want the current page that is being shown to be touchable.
- */
- @Override
- public void addTouchables(ArrayList views) {
- // Note that we don't call super.addTouchables(), which means that
- // we don't call View.addTouchables(). This is okay because a ViewPager
- // is itself not touchable.
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem) {
- child.addTouchables(views);
- }
- }
+ private void onPageScrolled(int position, float offset, int offsetPixels) {
+ if (mPageTransformer != null) {
+ onPageTransformer(position, offset, offsetPixels);
}
}
- /**
- * We only want the current page that is being shown to be focusable.
- */
- @Override
- protected boolean onRequestFocusInDescendants(int direction,
- Rect previouslyFocusedRect) {
- int index;
- int increment;
- int end;
- int count = getChildCount();
- if ((direction & FOCUS_FORWARD) != 0) {
- index = 0;
- increment = 1;
- end = count;
- } else {
- index = count - 1;
- increment = -1;
- end = -1;
- }
- for (int i = index; i != end; i += increment) {
- View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem) {
- if (child.requestFocus(direction, previouslyFocusedRect)) {
- return true;
- }
- }
- }
- }
- return false;
- }
+ private void onPageTransformer(int position, float positionOffset, int offsetPixels) {
+ mOriginalOffset = positionOffset;
+ this.mCurrentItemPos = position;
+ this.mItemPosOffset = positionOffset;
+ this.mItemPosOffsetPixels = offsetPixels;
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- // Dispatch scroll events from this ViewPager.
- if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {
- return super.dispatchPopulateAccessibilityEvent(event);
+ int realSlidState = getRealSlidState();
+ if (realSlidState != SLID_NONE) {
+ mLastScrollState = realSlidState;
}
-
- // Dispatch all other accessibility events from the current page.
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- final ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem &&
- child.dispatchPopulateAccessibilityEvent(event)) {
- return true;
- }
- }
+ switch (mLastScrollState) {
+ case SLID_LEFT:
+ scrollLeft(position, positionOffset);
+ break;
+ case SLID_RIGHT:
+ scrollRight(position, positionOffset);
+ break;
+ case SLID_LEFT_ELASTIC:
+ // 向左滑动但未达到临界回弹
+ elasticLeft(position, positionOffset);
+ break;
+ case SLID_RIGHT_ELASTIC:
+ // 向右滑动但未达到临界回弹
+ elasticRight(position, positionOffset);
+ break;
+ default:
+ break;
}
-
- return false;
}
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams();
+ private void elasticLeft(int position, float positionOffset) {
+ Component in = mComponents.get(position);
+ Component out = mComponents.get(position + 1);
+ mPageTransformer.transformPage(out, mOriginalOffset);
+ mPageTransformer.transformPage(in, MathUtils.floatToAdd(SCROLL_RIGHT, positionOffset));
}
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return generateDefaultLayoutParams();
+ private void elasticRight(int position, float positionOffset) {
+ Component out = mComponents.get(position - 1);
+ Component in = mComponents.get(position);
+ mPageTransformer.transformPage(in, MathUtils.floatToSubtract(1, positionOffset));
+ mPageTransformer.transformPage(out, MathUtils.floatToSubtract(0, positionOffset));
}
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams && super.checkLayoutParams(p);
+ private void scrollRight(int position, float positionOffset) {
+ // 从左往右滑动
+ Component in = mComponents.get(position - 1);
+ Component out = mComponents.get(position);
+ mPageTransformer.transformPage(out, mOriginalOffset);
+ mPageTransformer.transformPage(in, MathUtils.floatToAdd(SCROLL_RIGHT, positionOffset));
}
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
+ private void scrollLeft(int position, float positionOffset) {
+ Component in = mComponents.get(position + 1);
+ Component out = mComponents.get(position);
+ mPageTransformer.transformPage(in, MathUtils.floatToSubtract(1, positionOffset));
+ mPageTransformer.transformPage(out, MathUtils.floatToSubtract(0, positionOffset));
}
- class MyAccessibilityDelegate extends AccessibilityDelegateCompat {
-
- @Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(host, event);
- event.setClassName(ViewPager.class.getName());
- final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain();
- recordCompat.setScrollable(canScroll());
- if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED
- && mAdapter != null) {
- recordCompat.setItemCount(mAdapter.getCount());
- recordCompat.setFromIndex(mCurItem);
- recordCompat.setToIndex(mCurItem);
- }
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setClassName(ViewPager.class.getName());
- info.setScrollable(canScroll());
- if (internalCanScrollVertically(1)) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
- }
- if (internalCanScrollVertically(-1)) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
- }
+ private int getRealSlidState() {
+ if (mItemPosOffset == 1) {
+ return SLID_NONE;
}
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (super.performAccessibilityAction(host, action, args)) {
- return true;
+ if (mCurrentState == PageSlider.SLIDING_STATE_DRAGGING) {
+ if (mItemPosOffsetPixels > 0) {
+ return SLID_LEFT;
+ } else if (mItemPosOffsetPixels < 0) {
+ return SLID_RIGHT;
}
- switch (action) {
- case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
- if (internalCanScrollVertically(1)) {
- setCurrentItem(mCurItem + 1);
- return true;
- }
+ } else if (mCurrentState == PageSlider.SLIDING_STATE_SETTLING) {
+ if (mCurrentItemPos != getCurrentPage()) {
+ if (mItemPosOffsetPixels > 0) {
+ return SLID_RIGHT;
+ } else if (mItemPosOffsetPixels < 0) {
+ return SLID_LEFT;
}
- return false;
- case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
- if (internalCanScrollVertically(-1)) {
- setCurrentItem(mCurItem - 1);
- return true;
- }
+ } else {
+ if (mItemPosOffsetPixels > 0) {
+ return SLID_LEFT_ELASTIC;
+ } else if (mItemPosOffsetPixels < 0) {
+ return SLID_RIGHT_ELASTIC;
}
- return false;
}
- return false;
- }
-
- private boolean canScroll() {
- return (mAdapter != null) && (mAdapter.getCount() > 1);
- }
- }
-
- private class PagerObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- dataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- dataSetChanged();
}
+ return SLID_NONE;
}
/**
- * Layout parameters that should be supplied for views added to a
- * ViewPager.
+ * PageSlide的动画接口
+ *
+ * @since 2021-04-12
*/
- public static class LayoutParams extends ViewGroup.LayoutParams {
- /**
- * true if this view is a decoration on the pager itself and not
- * a view supplied by the adapter.
- */
- public boolean isDecor;
-
- /**
- * Gravity setting for use on decor views only:
- * Where to position the view page within the overall ViewPager
- * container; constants are defined in {@link android.view.Gravity}.
- */
- public int gravity;
-
- /**
- * Width as a 0-1 multiplier of the measured pager width
- */
- float heightFactor = 0.f;
-
- /**
- * true if this view was added during layout and needs to be measured
- * before being positioned.
- */
- boolean needsMeasure;
-
+ public interface PageTransformer {
/**
- * Adapter position this view is for if !isDecor
+ * 动画接口
+ *
+ * @param page 子组件
+ * @param position position
*/
- int position;
-
- /**
- * Current child index within the ViewPager that this view occupies
- */
- int childIndex;
-
- public LayoutParams() {
- super(FILL_PARENT, FILL_PARENT);
- }
-
- public LayoutParams(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
- gravity = a.getInteger(0, Gravity.TOP);
- a.recycle();
- }
- }
-
- static class ViewPositionComparator implements Comparator {
- @Override
- public int compare(View lhs, View rhs) {
- final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();
- final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();
- if (llp.isDecor != rlp.isDecor) {
- return llp.isDecor ? 1 : -1;
- }
- return llp.position - rlp.position;
- }
+ void transformPage(Component page, float position);
}
}
diff --git a/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/AccordionTransformer.java b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/AccordionTransformer.java
new file mode 100644
index 0000000000000000000000000000000000000000..4df8c0a6241f784887626c4d8a815daa582610d5
--- /dev/null
+++ b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/AccordionTransformer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fr.castorflex.android.verticalviewpager.transformer;
+
+import ohos.agp.components.Component;
+
+/**
+ * Accordion Transformer
+ *
+ * @author shetaotao
+ * @since 2021-04-26
+ */
+public class AccordionTransformer extends BaseTransformer {
+ @Override
+ public void onTransform(Component component, float position) {
+ component.setPivotX(position < (float) 0 ? 0.0F : (float) component.getWidth());
+ component.setScaleX(position < (float) 0 ? MathUtils.floatToAdd(1.0F, position)
+ : MathUtils.floatToSubtract(1.0F, position));
+ }
+}
diff --git a/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/BaseTransformer.java b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/BaseTransformer.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bf9ff5c60a8c066aca7f5b3864d797acb9de3d5
--- /dev/null
+++ b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/BaseTransformer.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fr.castorflex.android.verticalviewpager.transformer;
+
+import fr.castorflex.android.verticalviewpager.VerticalViewPager;
+import ohos.agp.components.Component;
+
+/**
+ * Base transformer
+ *
+ * @author shetaotao
+ * @since 2021-04-26
+ */
+public abstract class BaseTransformer implements VerticalViewPager.PageTransformer {
+ private static final float SCROLL_RIGHT = -1.0F;
+ private static final float SCROLL_LEFT = 1.0F;
+
+ @Override
+ public void transformPage(Component component, float position) {
+ float clampedPosition = clampPosition(position);
+ onPreTransform(component, clampedPosition);
+ onTransform(component, clampedPosition);
+ onPostTransform(component, clampedPosition);
+ }
+
+ /**
+ * PreTransform
+ *
+ * @param component component
+ * @param position position
+ */
+ protected void onPreTransform(Component component, float position) {
+ float width = (float) component.getWidth();
+ component.setRotation(0.0F);
+ component.setScaleX(1.0F);
+ component.setScaleY(1.0F);
+ component.setPivotX(0.0F);
+ component.setPivotY(0.0F);
+ component.setTranslationY(0.0F);
+ component.setTranslationX(isPagingEnabled() ? 0.0F : -width * position);
+ if (hideOffscreenPages()) {
+ if (position <= SCROLL_RIGHT || position >= SCROLL_LEFT) {
+ component.setAlpha(0f);
+ } else {
+ component.setAlpha(1f);
+ }
+ component.setEnabled(false);
+ } else {
+ component.setEnabled(true);
+ component.setAlpha(1.0F);
+ }
+ }
+
+ private float clampPosition(float position) {
+ float clampPosition;
+ if (position <= SCROLL_RIGHT) {
+ clampPosition = SCROLL_RIGHT;
+ } else if (position >= 1F) {
+ clampPosition = 1.0F;
+ } else if (Float.isNaN(position)) {
+ clampPosition = 0.0F;
+ } else {
+ clampPosition = position;
+ }
+ return clampPosition;
+ }
+
+ /**
+ * Is paging enabled
+ *
+ * @return false
+ */
+ public boolean isPagingEnabled() {
+ return false;
+ }
+
+ /**
+ * Hide Off screen Pages
+ *
+ * @return true
+ */
+ protected boolean hideOffscreenPages() {
+ return true;
+ }
+
+ /**
+ * PostTransform
+ *
+ * @param component component
+ * @param position position
+ */
+ protected void onPostTransform(Component component, float position) {
+ }
+
+ /**
+ * Abstract transform
+ *
+ * @param component component
+ * @param position position
+ */
+ protected abstract void onTransform(Component component, float position);
+}
diff --git a/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/MathUtils.java b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/MathUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..f889b9991594183e5f3ede9ac04ec25f9e07e3c9
--- /dev/null
+++ b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/MathUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fr.castorflex.android.verticalviewpager.transformer;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+
+/**
+ * 精确计算
+ *
+ * @author shetaotao
+ * @since 2021-04-27
+ */
+public class MathUtils {
+ private MathUtils() {
+ }
+
+ /**
+ * Subtract
+ *
+ * @param f1 float1
+ * @param f2 float2
+ * @return subtract value
+ */
+ public static float floatToSubtract(float f1, float f2) {
+ BigDecimal b1 = BigDecimal.valueOf(f1);
+ BigDecimal b2 = BigDecimal.valueOf(f2);
+ DecimalFormat df = new DecimalFormat("0.00000");
+ return Float.valueOf(df.format(b1.subtract(b2)));
+ }
+
+ /**
+ * Addition
+ *
+ * @param f1 float1
+ * @param f2 float2
+ * @return Addition value
+ */
+ public static float floatToAdd(float f1, float f2) {
+ BigDecimal b1 = BigDecimal.valueOf(f1);
+ BigDecimal b2 = BigDecimal.valueOf(f2);
+ DecimalFormat df = new DecimalFormat("0.00000");
+ return Float.valueOf(df.format(b1.add(b2)));
+ }
+}
diff --git a/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/TransformerItem.java b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/TransformerItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a66b2407c8dcb51f8132c6f42f0882cfd19da69
--- /dev/null
+++ b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/TransformerItem.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fr.castorflex.android.verticalviewpager.transformer;
+
+import fr.castorflex.android.verticalviewpager.VerticalViewPager;
+
+/**
+ * 扩展了PageSlide的动画接口
+ *
+ * @author shetaotao
+ * @since 2021-04-26
+ */
+public class TransformerItem {
+ private Class extends VerticalViewPager.PageTransformer> mClass;
+
+ /**
+ * Transformer item
+ *
+ * @param itemClass Item class
+ */
+ public TransformerItem(Class itemClass) {
+ this.mClass = itemClass;
+ }
+
+ /**
+ * Get page transformer
+ *
+ * @return Page transformer
+ */
+ public VerticalViewPager.PageTransformer getPageTransformer() {
+ try {
+ return mClass.newInstance();
+ } catch (IllegalAccessException e) {
+ return new AccordionTransformer();
+ } catch (InstantiationException e) {
+ return new AccordionTransformer();
+ }
+ }
+}
diff --git a/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/ZoomOutSlideTransformer.java b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/ZoomOutSlideTransformer.java
new file mode 100644
index 0000000000000000000000000000000000000000..bad55a96a5cabeea2ee2ffd5b30926694f2890c9
--- /dev/null
+++ b/library/src/main/java/fr/castorflex/android/verticalviewpager/transformer/ZoomOutSlideTransformer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fr.castorflex.android.verticalviewpager.transformer;
+
+import ohos.agp.components.Component;
+
+/**
+ * 页面缩放动画接口
+ *
+ * @author shetaotao
+ * @since 2021-04-26
+ */
+public class ZoomOutSlideTransformer extends BaseTransformer {
+ private static final float ALPHA = 0.14999998F;
+ private static final float ALPHA_MAX = 0.85F;
+ private static final float ALPHA_DEFAULT = 0.7F;
+ private static final float SCALE_MAX = 0.85F;
+ private static final float PIVOT_X = 0.5F;
+ private static final float PIVOT_Y = 0.5F;
+ private static final float HALF = 2.0F;
+ private static final float SCROLL_RIGHT = -1.0F;
+ private static final float SCROLL_LEFT = 1.0F;
+
+ @Override
+ protected void onTransform(Component component, float position) {
+ if (position >= SCROLL_RIGHT || position <= SCROLL_LEFT) {
+ float height = (float) component.getHeight();
+ float width = (float) component.getWidth();
+ float scaleFactor = Math.max(SCALE_MAX, MathUtils.floatToSubtract(SCROLL_LEFT, Math.abs(position)));
+ float vertMargin = height * MathUtils.floatToSubtract(SCROLL_LEFT, scaleFactor) / HALF;
+ float horzMargin = width * MathUtils.floatToSubtract(SCROLL_LEFT, scaleFactor) / HALF;
+ component.setPivotY(PIVOT_X * height);
+ component.setPivotX(PIVOT_Y * width);
+ component.setTranslationX(position < (float) 0
+ ? MathUtils.floatToSubtract(horzMargin, vertMargin / HALF)
+ : MathUtils.floatToAdd(-horzMargin, vertMargin / HALF));
+ component.setScaleX(scaleFactor);
+ component.setScaleY(scaleFactor);
+ component.setAlpha(MathUtils.floatToAdd(ALPHA_DEFAULT,
+ MathUtils.floatToSubtract(scaleFactor, ALPHA_MAX) / ALPHA / HALF));
+ }
+ }
+}
diff --git a/library/src/main/resources/base/element/string.json b/library/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..6c298286373e6266fd910aac81429792f08df437
--- /dev/null
+++ b/library/src/main/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "Library"
+ }
+ ]
+}
diff --git a/mvn_push.gradle b/mvn_push.gradle
deleted file mode 100644
index 92fdb4d8fec9c4747c9b6385b82c3cbd184eba0d..0000000000000000000000000000000000000000
--- a/mvn_push.gradle
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2013 Chris Banes
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-apply plugin: 'maven'
-apply plugin: 'signing'
-
-def isReleaseBuild() {
- return version.contains("SNAPSHOT") == false
-}
-
-def getReleaseRepositoryUrl() {
- return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
- : "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
-}
-
-def getSnapshotRepositoryUrl() {
- return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
- : "https://oss.sonatype.org/content/repositories/snapshots/"
-}
-
-def getRepositoryUsername() {
- return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
-}
-
-def getRepositoryPassword() {
- return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
-}
-
-afterEvaluate { project ->
- uploadArchives {
- repositories {
- mavenDeployer {
- beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
-
- pom.artifactId = POM_ARTIFACT_ID
-
- repository(url: getReleaseRepositoryUrl()) {
- authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
- }
- snapshotRepository(url: getSnapshotRepositoryUrl()) {
- authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
- }
-
- pom.project {
- name POM_NAME
- packaging POM_PACKAGING
- description POM_DESCRIPTION
- url POM_URL
-
- scm {
- url POM_SCM_URL
- connection POM_SCM_CONNECTION
- developerConnection POM_SCM_DEV_CONNECTION
- }
-
- licenses {
- license {
- name POM_LICENCE_NAME
- url POM_LICENCE_URL
- distribution POM_LICENCE_DIST
- }
- }
-
- developers {
- developer {
- id POM_DEVELOPER_ID
- name POM_DEVELOPER_NAME
- }
- }
- }
- }
- }
- }
-
- signing {
- required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
- sign configurations.archives
- }
-
- task androidJavadocs(type: Javadoc) {
- source = android.sourceSets.main.allJava
- }
-
- task androidJavadocsJar(type: Jar) {
- classifier = 'javadoc'
- from androidJavadocs.destinationDir
- }
-
- task androidSourcesJar(type: Jar) {
- classifier = 'sources'
- from android.sourceSets.main.allSource
- }
-
- artifacts {
- archives androidSourcesJar
- archives androidJavadocsJar
- }
-}
\ No newline at end of file
diff --git a/sample/build.gradle b/sample/build.gradle
deleted file mode 100644
index ef7c77369bd65d984ee64eeb90da03c2db032f48..0000000000000000000000000000000000000000
--- a/sample/build.gradle
+++ /dev/null
@@ -1,43 +0,0 @@
-apply plugin: 'android'
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- compile project(':library')
- compile 'com.android.support:support-v13:19.0.0'
-}
-
-android {
- compileSdkVersion 19
- buildToolsVersion "19.0.0"
-
- defaultConfig {
- minSdkVersion 14
- targetSdkVersion 19
- versionName project.VERSION_NAME
- versionCode Integer.parseInt(project.VERSION_CODE)
- }
-
- signingConfigs {
- release
- }
- buildTypes {
- release {
- signingConfig project.hasProperty('storeFile') ? signingConfigs.release : signingConfigs.debug
- }
- }
-}
-if (project.hasProperty('storeFile')) {
- android.signingConfigs.release.storeFile = file(storeFile)
-}
-if (project.hasProperty('keyAlias')) {
- android.signingConfigs.release.keyAlias = keyAlias
-}
-if (project.hasProperty('storePassword')) {
- android.signingConfigs.release.storePassword = storePassword
-}
-if (project.hasProperty('keyPassword')) {
- android.signingConfigs.release.keyPassword = keyPassword
-}
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
deleted file mode 100644
index 6d530ab6d1ac59b12371d0443814eba59e4088fd..0000000000000000000000000000000000000000
--- a/sample/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sample/src/main/java/fr/castorflex/android/verticalviewpager/sample/MainActivity.java b/sample/src/main/java/fr/castorflex/android/verticalviewpager/sample/MainActivity.java
deleted file mode 100644
index 648db98c3045d96e361ba8d072f4bce03d8d2a1c..0000000000000000000000000000000000000000
--- a/sample/src/main/java/fr/castorflex/android/verticalviewpager/sample/MainActivity.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package fr.castorflex.android.verticalviewpager.sample;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Bundle;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import java.util.Locale;
-
-import fr.castorflex.android.verticalviewpager.VerticalViewPager;
-
-public class MainActivity extends Activity {
-
- private static final float MIN_SCALE = 0.75f;
- private static final float MIN_ALPHA = 0.75f;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- VerticalViewPager verticalViewPager = (VerticalViewPager) findViewById(R.id.verticalviewpager);
-
- verticalViewPager.setAdapter(new DummyAdapter(getFragmentManager()));
- verticalViewPager.setPageMargin(getResources().getDimensionPixelSize(R.dimen.pagemargin));
- verticalViewPager.setPageMarginDrawable(new ColorDrawable(getResources().getColor(android.R.color.holo_green_dark)));
-
- verticalViewPager.setPageTransformer(true, new ViewPager.PageTransformer() {
- @Override
- public void transformPage(View view, float position) {
- int pageWidth = view.getWidth();
- int pageHeight = view.getHeight();
-
- if (position < -1) { // [-Infinity,-1)
- // This page is way off-screen to the left.
- view.setAlpha(0);
-
- } else if (position <= 1) { // [-1,1]
- // Modify the default slide transition to shrink the page as well
- float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
- float vertMargin = pageHeight * (1 - scaleFactor) / 2;
- float horzMargin = pageWidth * (1 - scaleFactor) / 2;
- if (position < 0) {
- view.setTranslationY(vertMargin - horzMargin / 2);
- } else {
- view.setTranslationY(-vertMargin + horzMargin / 2);
- }
-
- // Scale the page down (between MIN_SCALE and 1)
- view.setScaleX(scaleFactor);
- view.setScaleY(scaleFactor);
-
- // Fade the page relative to its size.
- view.setAlpha(MIN_ALPHA +
- (scaleFactor - MIN_SCALE) /
- (1 - MIN_SCALE) * (1 - MIN_ALPHA));
-
- } else { // (1,+Infinity]
- // This page is way off-screen to the right.
- view.setAlpha(0);
- }
- }
- });
- }
-
- public class DummyAdapter extends FragmentPagerAdapter {
-
- public DummyAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public Fragment getItem(int position) {
- // getItem is called to instantiate the fragment for the given page.
- // Return a PlaceholderFragment (defined as a static inner class below).
- return PlaceholderFragment.newInstance(position + 1);
- }
-
- @Override
- public int getCount() {
- // Show 3 total pages.
- return 3;
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- Locale l = Locale.getDefault();
- switch (position) {
- case 0:
- return "PAGE 1";
- case 1:
- return "PAGE 2";
- case 2:
- return "PAGE 3";
- }
- return null;
- }
-
- }
-
- /**
- * A placeholder fragment containing a simple view.
- */
- public static class PlaceholderFragment extends Fragment {
- /**
- * The fragment argument representing the section number for this
- * fragment.
- */
- private static final String ARG_SECTION_NUMBER = "section_number";
-
- /**
- * Returns a new instance of this fragment for the given section
- * number.
- */
- public static PlaceholderFragment newInstance(int sectionNumber) {
- PlaceholderFragment fragment = new PlaceholderFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_SECTION_NUMBER, sectionNumber);
- fragment.setArguments(args);
- return fragment;
- }
-
- public PlaceholderFragment() {
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_layout, container, false);
- TextView textView = (TextView) rootView.findViewById(R.id.textview);
- textView.setText(Integer.toString(getArguments().getInt(ARG_SECTION_NUMBER)));
- return rootView;
- }
-
-
- }
-
-}
diff --git a/sample/src/main/res/drawable-hdpi/ic_launcher.png b/sample/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 55621cc1074fe21c736a80abdd0837c4e8132bdf..0000000000000000000000000000000000000000
Binary files a/sample/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/sample/src/main/res/drawable-mdpi/ic_launcher.png b/sample/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 11ec2068be19999e4826062377ec8cd169c893bf..0000000000000000000000000000000000000000
Binary files a/sample/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/sample/src/main/res/drawable-xhdpi/ic_launcher.png b/sample/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 7c02b784aa5d7e2b7608ee8f96eb387ab3ab87d6..0000000000000000000000000000000000000000
Binary files a/sample/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/sample/src/main/res/drawable-xxhdpi/ic_launcher.png b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 915d9144134970b4366fac25f08ca53571c7dc9b..0000000000000000000000000000000000000000
Binary files a/sample/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 6e4b605242c789b51132b48b1e42c7a01c77884e..0000000000000000000000000000000000000000
--- a/sample/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/sample/src/main/res/layout/fragment_layout.xml b/sample/src/main/res/layout/fragment_layout.xml
deleted file mode 100644
index 1cf8f2c87abee81a11883782c14a644e0073552c..0000000000000000000000000000000000000000
--- a/sample/src/main/res/layout/fragment_layout.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/sample/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml
deleted file mode 100644
index d5fae0f72098079882b77f9f3af7bbfcc9e81456..0000000000000000000000000000000000000000
--- a/sample/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- 16dp
-
\ No newline at end of file
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
deleted file mode 100644
index 8ee06093ffa2035c1b6a70445cb606e3d541f8a2..0000000000000000000000000000000000000000
--- a/sample/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- VerticalViewPager
-
-
diff --git a/settings.gradle b/settings.gradle
index c504e934182ddc70f1df254ac96828fd208f54a2..0330f9c23909f6c01f3328c32bc99d015c4d9b3e 100755
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1 @@
-include ':sample'
-include ':library'
+include ':entry', ':Library'