PhotoViewPager.java revision f77a7eb196d16110c7b1087352b423913821ff61
1f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/* 2f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Copyright (C) 2011 Google Inc. 3f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed to The Android Open Source Project. 4f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 5f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License"); 6f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * you may not use this file except in compliance with the License. 7f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * You may obtain a copy of the License at 8f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 9f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * http://www.apache.org/licenses/LICENSE-2.0 10f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 11f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Unless required by applicable law or agreed to in writing, software 12f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS, 13f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * See the License for the specific language governing permissions and 15f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * limitations under the License. 16f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 17f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 18f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpackage com.android.ex.photo; 19f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 20f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.content.Context; 21f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.view.MotionEventCompat; 22f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.support.v4.view.ViewPager; 23f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.util.AttributeSet; 24f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinimport android.view.MotionEvent; 25f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 26f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein/** 27f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * View pager for photo view fragments. Define our own class so we can specify the 28f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * view pager in XML. 29f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 30f77a7eb196d16110c7b1087352b423913821ff61Andrew Sappersteinpublic class PhotoViewPager extends ViewPager { 31f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 32f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * A type of intercept that should be performed 33f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 34f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public static enum InterceptType { NONE, LEFT, RIGHT, BOTH } 35f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 36f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 37f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Provides an ability to intercept touch events. 38f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <p> 39f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * {@link ViewPager} intercepts all touch events and we need to be able to override this 40f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * behaviour. Instead, we could perform a similar function by declaring a custom 41f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * {@link ViewGroup} to contain the pager and intercept touch events at a higher level. 42f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 43f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public static interface OnInterceptTouchListener { 44f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 45f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * Called when a touch intercept is about to occur. 46f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * 47f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * @param origX the raw x coordinate of the initial touch 48f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * @param origY the raw y coordinate of the initial touch 49f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * @return Which type of touch, if any, should should be intercepted. 50f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 51f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public InterceptType onTouchIntercept(float origX, float origY); 52f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 53f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 54f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private static final int INVALID_POINTER = -1; 55f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 56f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private float mLastMotionX; 57f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private int mActivePointerId; 58f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** The x coordinate where the touch originated */ 59f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private float mActivatedX; 60f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** The y coordinate where the touch originated */ 61f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private float mActivatedY; 62f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein private OnInterceptTouchListener mListener; 63f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 64f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public PhotoViewPager(Context context) { 65f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein super(context); 66f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 67f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 68f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public PhotoViewPager(Context context, AttributeSet attrs) { 69f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein super(context, attrs); 70f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 71f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 72f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 73f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * {@inheritDoc} 74f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * <p> 75f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * We intercept touch event intercepts so we can prevent switching views when the 76f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * current view is internally scrollable. 77f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 78f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein @Override 79f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public boolean onInterceptTouchEvent(MotionEvent ev) { 80f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final InterceptType intercept = (mListener != null) 81f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein ? mListener.onTouchIntercept(mActivatedX, mActivatedY) 82f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein : InterceptType.NONE; 83f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final boolean ignoreScrollLeft = 84f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein (intercept == InterceptType.BOTH || intercept == InterceptType.LEFT); 85f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final boolean ignoreScrollRight = 86f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein (intercept == InterceptType.BOTH || intercept == InterceptType.RIGHT); 87f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 88f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // Only check ability to page if we can't scroll in one / both directions 89f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; 90f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 91f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 92f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mActivePointerId = INVALID_POINTER; 93f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 94f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 95f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein switch (action) { 96f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein case MotionEvent.ACTION_MOVE: { 97f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (ignoreScrollLeft || ignoreScrollRight) { 98f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final int activePointerId = mActivePointerId; 99f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (activePointerId == INVALID_POINTER) { 100f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // If we don't have a valid id, the touch down wasn't on content. 101f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein break; 102f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 103f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 104f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final int pointerIndex = 105f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein MotionEventCompat.findPointerIndex(ev, activePointerId); 106f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final float x = MotionEventCompat.getX(ev, pointerIndex); 107f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 108f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (ignoreScrollLeft && ignoreScrollRight) { 109f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mLastMotionX = x; 110f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return false; 111f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } else if (ignoreScrollLeft && (x > mLastMotionX)) { 112f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mLastMotionX = x; 113f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return false; 114f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } else if (ignoreScrollRight && (x < mLastMotionX)) { 115f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mLastMotionX = x; 116f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return false; 117f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 118f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 119f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein break; 120f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 121f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 122f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein case MotionEvent.ACTION_DOWN: { 123f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mLastMotionX = ev.getX(); 124f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // Use the raw x/y as the children can be located anywhere and there isn't a 125f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // single offset that would be meaningful 126f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mActivatedX = ev.getRawX(); 127f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mActivatedY = ev.getRawY(); 128f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 129f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein break; 130f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 131f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 132f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein case MotionEventCompat.ACTION_POINTER_UP: { 133f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final int pointerIndex = MotionEventCompat.getActionIndex(ev); 134f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 135f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein if (pointerId == mActivePointerId) { 136f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein // Our active pointer going up; select a new active pointer 137f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 138f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); 139f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 140f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 141f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein break; 142f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 143f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 144f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 145f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein return super.onInterceptTouchEvent(ev); 146f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 147f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein 148f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein /** 149f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein * sets the intercept touch listener. 150f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein */ 151f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein public void setOnInterceptTouchListener(OnInterceptTouchListener l) { 152f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein mListener = l; 153f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein } 154f77a7eb196d16110c7b1087352b423913821ff61Andrew Sapperstein} 155