Home | History | Annotate | Download | only in photo
      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 
     18 package com.android.ex.photo;
     19 
     20 import android.content.Context;
     21 import android.support.v4.view.MotionEventCompat;
     22 import android.support.v4.view.ViewPager;
     23 import android.util.AttributeSet;
     24 import android.view.MotionEvent;
     25 import android.view.View;
     26 
     27 /**
     28  * View pager for photo view fragments. Define our own class so we can specify the
     29  * view pager in XML.
     30  */
     31 public class PhotoViewPager extends ViewPager {
     32     /**
     33      * A type of intercept that should be performed
     34      */
     35     public static enum InterceptType { NONE, LEFT, RIGHT, BOTH }
     36 
     37     /**
     38      * Provides an ability to intercept touch events.
     39      * <p>
     40      * {@link ViewPager} intercepts all touch events and we need to be able to override this
     41      * behavior. Instead, we could perform a similar function by declaring a custom
     42      * {@link android.view.ViewGroup} to contain the pager and intercept touch events at a higher
     43      * level.
     44      */
     45     public static interface OnInterceptTouchListener {
     46         /**
     47          * Called when a touch intercept is about to occur.
     48          *
     49          * @param origX the raw x coordinate of the initial touch
     50          * @param origY the raw y coordinate of the initial touch
     51          * @return Which type of touch, if any, should should be intercepted.
     52          */
     53         public InterceptType onTouchIntercept(float origX, float origY);
     54     }
     55 
     56     private static final int INVALID_POINTER = -1;
     57 
     58     private float mLastMotionX;
     59     private int mActivePointerId;
     60     /** The x coordinate where the touch originated */
     61     private float mActivatedX;
     62     /** The y coordinate where the touch originated */
     63     private float mActivatedY;
     64     private OnInterceptTouchListener mListener;
     65 
     66     public PhotoViewPager(Context context) {
     67         super(context);
     68         initialize();
     69     }
     70 
     71     public PhotoViewPager(Context context, AttributeSet attrs) {
     72         super(context, attrs);
     73         initialize();
     74     }
     75 
     76     private void initialize() {
     77         // Set the page transformer to perform the transition animation
     78         // for each page in the view.
     79         setPageTransformer(true, new PageTransformer() {
     80             @Override
     81             public void transformPage(View page, float position) {
     82 
     83                 // The >= 1 is needed so that the page
     84                 // (page A) that transitions behind the newly visible
     85                 // page (page B) that comes in from the left does not
     86                 // get the touch events because it is still on screen
     87                 // (page A is still technically on screen despite being
     88                 // invisible). This makes sure that when the transition
     89                 // has completely finished, we revert it to its default
     90                 // behavior and move it off of the screen.
     91                 if (position < 0 || position >= 1.f) {
     92                     page.setTranslationX(0);
     93                     page.setAlpha(1.f);
     94                     page.setScaleX(1);
     95                     page.setScaleY(1);
     96                 } else {
     97                     page.setTranslationX(-position * page.getWidth());
     98                     page.setAlpha(Math.max(0,1.f - position));
     99                     final float scale = Math.max(0, 1.f - position * 0.3f);
    100                     page.setScaleX(scale);
    101                     page.setScaleY(scale);
    102                 }
    103             }
    104         });
    105     }
    106 
    107     /**
    108      * {@inheritDoc}
    109      * <p>
    110      * We intercept touch event intercepts so we can prevent switching views when the
    111      * current view is internally scrollable.
    112      */
    113     @Override
    114     public boolean onInterceptTouchEvent(MotionEvent ev) {
    115         final InterceptType intercept = (mListener != null)
    116                 ? mListener.onTouchIntercept(mActivatedX, mActivatedY)
    117                 : InterceptType.NONE;
    118         final boolean ignoreScrollLeft =
    119                 (intercept == InterceptType.BOTH || intercept == InterceptType.LEFT);
    120         final boolean ignoreScrollRight =
    121                 (intercept == InterceptType.BOTH || intercept == InterceptType.RIGHT);
    122 
    123         // Only check ability to page if we can't scroll in one / both directions
    124         final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
    125 
    126         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
    127             mActivePointerId = INVALID_POINTER;
    128         }
    129 
    130         switch (action) {
    131             case MotionEvent.ACTION_MOVE: {
    132                 if (ignoreScrollLeft || ignoreScrollRight) {
    133                     final int activePointerId = mActivePointerId;
    134                     if (activePointerId == INVALID_POINTER) {
    135                         // If we don't have a valid id, the touch down wasn't on content.
    136                         break;
    137                     }
    138 
    139                     final int pointerIndex =
    140                             MotionEventCompat.findPointerIndex(ev, activePointerId);
    141                     final float x = MotionEventCompat.getX(ev, pointerIndex);
    142 
    143                     if (ignoreScrollLeft && ignoreScrollRight) {
    144                         mLastMotionX = x;
    145                         return false;
    146                     } else if (ignoreScrollLeft && (x > mLastMotionX)) {
    147                         mLastMotionX = x;
    148                         return false;
    149                     } else if (ignoreScrollRight && (x < mLastMotionX)) {
    150                         mLastMotionX = x;
    151                         return false;
    152                     }
    153                 }
    154                 break;
    155             }
    156 
    157             case MotionEvent.ACTION_DOWN: {
    158                 mLastMotionX = ev.getX();
    159                 // Use the raw x/y as the children can be located anywhere and there isn't a
    160                 // single offset that would be meaningful
    161                 mActivatedX = ev.getRawX();
    162                 mActivatedY = ev.getRawY();
    163                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
    164                 break;
    165             }
    166 
    167             case MotionEventCompat.ACTION_POINTER_UP: {
    168                 final int pointerIndex = MotionEventCompat.getActionIndex(ev);
    169                 final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
    170                 if (pointerId == mActivePointerId) {
    171                     // Our active pointer going up; select a new active pointer
    172                     final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
    173                     mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
    174                     mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
    175                 }
    176                 break;
    177             }
    178         }
    179 
    180         return super.onInterceptTouchEvent(ev);
    181     }
    182 
    183     /**
    184      * sets the intercept touch listener.
    185      */
    186     public void setOnInterceptTouchListener(OnInterceptTouchListener l) {
    187         mListener = l;
    188     }
    189 }
    190