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