/* * Copyright (C) 2011 Google Inc. * Licensed to The Android Open Source Project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ex.photo; import android.content.Context; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * View pager for photo view fragments. Define our own class so we can specify the * view pager in XML. */ public class PhotoViewPager extends ViewPager { /** * A type of intercept that should be performed */ public static enum InterceptType { NONE, LEFT, RIGHT, BOTH } /** * Provides an ability to intercept touch events. *

* {@link ViewPager} intercepts all touch events and we need to be able to override this * behavior. Instead, we could perform a similar function by declaring a custom * {@link ViewGroup} to contain the pager and intercept touch events at a higher level. */ public static interface OnInterceptTouchListener { /** * Called when a touch intercept is about to occur. * * @param origX the raw x coordinate of the initial touch * @param origY the raw y coordinate of the initial touch * @return Which type of touch, if any, should should be intercepted. */ public InterceptType onTouchIntercept(float origX, float origY); } private static final int INVALID_POINTER = -1; private float mLastMotionX; private int mActivePointerId; /** The x coordinate where the touch originated */ private float mActivatedX; /** The y coordinate where the touch originated */ private float mActivatedY; private OnInterceptTouchListener mListener; public PhotoViewPager(Context context) { super(context); initialize(); } public PhotoViewPager(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { // Set the page transformer to perform the transition animation // for each page in the view. setPageTransformer(true, new PageTransformer() { @Override public void transformPage(View page, float position) { // The >= 1 is needed so that the page // (page A) that transitions behind the newly visible // page (page B) that comes in from the left does not // get the touch events because it is still on screen // (page A is still technically on screen despite being // invisible). This makes sure that when the transition // has completely finished, we revert it to its default // behavior and move it off of the screen. if (position < 0 || position >= 1.f) { page.setTranslationX(0); page.setAlpha(1.f); page.setScaleX(1); page.setScaleY(1); } else { page.setTranslationX(-position * page.getWidth()); page.setAlpha(Math.max(0,1.f - position)); final float scale = Math.max(0, 1.f - position * 0.3f); page.setScaleX(scale); page.setScaleY(scale); } } }); } /** * {@inheritDoc} *

* We intercept touch event intercepts so we can prevent switching views when the * current view is internally scrollable. */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final InterceptType intercept = (mListener != null) ? mListener.onTouchIntercept(mActivatedX, mActivatedY) : InterceptType.NONE; final boolean ignoreScrollLeft = (intercept == InterceptType.BOTH || intercept == InterceptType.LEFT); final boolean ignoreScrollRight = (intercept == InterceptType.BOTH || intercept == InterceptType.RIGHT); // Only check ability to page if we can't scroll in one / both directions final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mActivePointerId = INVALID_POINTER; } switch (action) { case MotionEvent.ACTION_MOVE: { if (ignoreScrollLeft || ignoreScrollRight) { final int activePointerId = mActivePointerId; if (activePointerId == INVALID_POINTER) { // If we don't have a valid id, the touch down wasn't on content. break; } final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId); final float x = MotionEventCompat.getX(ev, pointerIndex); if (ignoreScrollLeft && ignoreScrollRight) { mLastMotionX = x; return false; } else if (ignoreScrollLeft && (x > mLastMotionX)) { mLastMotionX = x; return false; } else if (ignoreScrollRight && (x < mLastMotionX)) { mLastMotionX = x; return false; } } break; } case MotionEvent.ACTION_DOWN: { mLastMotionX = ev.getX(); // Use the raw x/y as the children can be located anywhere and there isn't a // single offset that would be meaningful mActivatedX = ev.getRawX(); mActivatedY = ev.getRawY(); mActivePointerId = MotionEventCompat.getPointerId(ev, 0); break; } case MotionEventCompat.ACTION_POINTER_UP: { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { // Our active pointer going up; select a new active pointer final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } break; } } return super.onInterceptTouchEvent(ev); } /** * sets the intercept touch listener. */ public void setOnInterceptTouchListener(OnInterceptTouchListener l) { mListener = l; } }