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