lmw
2024-05-21 0af0750101f969b6ff18d7ade37354b4bcdccd03
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package cn.sinata.xldutils.view.gesture;
 
import android.view.MotionEvent;
 
/**
 * Component that detects and tracks multiple pointers based on touch events.
 * <p>
 * Each time a pointer gets pressed or released, the current gesture (if any) will end, and a new
 * one will be started (if there are still pressed pointers left). It is guaranteed that the number
 * of pointers within the single gesture will remain the same during the whole gesture.
 */
public class MultiPointerGestureDetector {
 
  /** The listener for receiving notifications when gestures occur. */
  public interface Listener {
    /** Responds to the beginning of a gesture. */
    public void onGestureBegin(MultiPointerGestureDetector detector);
 
    /** Responds to the update of a gesture in progress. */
    public void onGestureUpdate(MultiPointerGestureDetector detector);
 
    /** Responds to the end of a gesture. */
    public void onGestureEnd(MultiPointerGestureDetector detector);
  }
 
  private static final int MAX_POINTERS = 2;
 
  private boolean mGestureInProgress;
  private int mCount;
  private final int mId[] = new int[MAX_POINTERS];
  private final float mStartX[] = new float[MAX_POINTERS];
  private final float mStartY[] = new float[MAX_POINTERS];
  private final float mCurrentX[] = new float[MAX_POINTERS];
  private final float mCurrentY[] = new float[MAX_POINTERS];
 
  private Listener mListener = null;
 
  public MultiPointerGestureDetector() {
    reset();
  }
 
  /** Factory method that creates a new instance of MultiPointerGestureDetector */
  public static MultiPointerGestureDetector newInstance() {
    return new MultiPointerGestureDetector();
  }
 
  /**
   * Sets the listener.
   * @param listener listener to set
   */
  public void setListener(Listener listener) {
    mListener = listener;
  }
 
  /**
   * Resets the component to the initial state.
   */
  public void reset() {
    mGestureInProgress = false;
    mCount = 0;
    for (int i = 0; i < MAX_POINTERS; i++) {
      mId[i] = MotionEvent.INVALID_POINTER_ID;
    }
  }
 
  /**
   * This method can be overridden in order to perform threshold check or something similar.
   * @return whether or not to start a new gesture
   */
  protected boolean shouldStartGesture() {
    return true;
  }
 
  private void startGesture() {
    if (!mGestureInProgress) {
      mGestureInProgress = true;
      if (mListener != null) {
        mListener.onGestureBegin(this);
      }
    }
  }
 
  private void stopGesture() {
    if (mGestureInProgress) {
      mGestureInProgress = false;
      if (mListener != null) {
        mListener.onGestureEnd(this);
      }
    }
  }
 
  /**
   * Gets the index of the i-th pressed pointer.
   * Normally, the index will be equal to i, except in the case when the pointer is released.
   * @return index of the specified pointer or -1 if not found (i.e. not enough pointers are down)
   */
  private int getPressedPointerIndex(MotionEvent event, int i) {
    final int count = event.getPointerCount();
    final int action = event.getActionMasked();
    final int index = event.getActionIndex();
    if (action == MotionEvent.ACTION_UP ||
        action == MotionEvent.ACTION_POINTER_UP) {
      if (i >= index) {
        i++;
      }
    }
    return (i < count) ? i : -1;
  }
 
  /**
   * Handles the given motion event.
   * @param event event to handle
   * @return whether or not the event was handled
   */
  public boolean onTouchEvent(final MotionEvent event) {
    switch (event.getActionMasked()) {
      case MotionEvent.ACTION_MOVE: {
        // update pointers
        for (int i = 0; i < MAX_POINTERS; i++) {
          int index = event.findPointerIndex(mId[i]);
          if (index != -1) {
            mCurrentX[i] = event.getX(index);
            mCurrentY[i] = event.getY(index);
          }
        }
        // start a new gesture if not already started
        if (!mGestureInProgress && shouldStartGesture()) {
          startGesture();
        }
        // notify listener
        if (mGestureInProgress && mListener != null) {
          mListener.onGestureUpdate(this);
        }
        break;
      }
 
      case MotionEvent.ACTION_DOWN:
      case MotionEvent.ACTION_POINTER_DOWN:
      case MotionEvent.ACTION_POINTER_UP:
      case MotionEvent.ACTION_UP: {
        // we'll restart the current gesture (if any) whenever the number of pointers changes
        // NOTE: we only restart existing gestures here, new gestures are started in ACTION_MOVE
        boolean wasGestureInProgress = mGestureInProgress;
        stopGesture();
        reset();
        // update pointers
        for (int i = 0; i < MAX_POINTERS; i++) {
          int index = getPressedPointerIndex(event, i);
          if (index == -1) {
            break;
          }
          mId[i] = event.getPointerId(index);
          mCurrentX[i] = mStartX[i] = event.getX(index);
          mCurrentY[i] = mStartY[i] = event.getY(index);
          mCount++;
        }
        // restart the gesture (if any) if there are still pointers left
        if (wasGestureInProgress && mCount > 0) {
          startGesture();
          return true;
        }else {
          //处理click事件
          if (!wasGestureInProgress ){
            return false;
          }
        }
 
//        break;
      }
 
      case MotionEvent.ACTION_CANCEL: {
        stopGesture();
        reset();
        break;
      }
    }
    return true;
  }
 
  /** Restarts the current gesture */
  public void restartGesture() {
    if (!mGestureInProgress) {
      return;
    }
    stopGesture();
    for (int i = 0; i < MAX_POINTERS; i++) {
      mStartX[i] = mCurrentX[i];
      mStartY[i] = mCurrentY[i];
    }
    startGesture();
  }
 
  /** Gets whether gesture is in progress or not */
  public boolean isGestureInProgress() {
    return mGestureInProgress;
  }
 
  /** Gets the number of pointers in the current gesture */
  public int getCount() {
    return mCount;
  }
 
  /**
   * Gets the start X coordinates for the all pointers
   * Mutable array is exposed for performance reasons and is not to be modified by the callers.
   */
  public float[] getStartX() {
    return mStartX;
  }
 
  /**
   * Gets the start Y coordinates for the all pointers
   * Mutable array is exposed for performance reasons and is not to be modified by the callers.
   */
  public float[] getStartY() {
    return mStartY;
  }
 
  /**
   * Gets the current X coordinates for the all pointers
   * Mutable array is exposed for performance reasons and is not to be modified by the callers.
   */
  public float[] getCurrentX() {
    return mCurrentX;
  }
 
  /**
   * Gets the current Y coordinates for the all pointers
   * Mutable array is exposed for performance reasons and is not to be modified by the callers.
   */
  public float[] getCurrentY() {
    return mCurrentY;
  }
}