ViewDragHelperを紐解く

ViewのUI操作(特にマルチタップやドラッグ＆ドロップ)などに使われる
ViewDragHelperを紐解いていきます

## ViewDragHelperの呼び出し

```ViewDragHelper.java
/**
 * Factory method to create a new ViewDragHelper.
 *
 * @param forParent Parent view to monitor
 * @param sensitivity Multiplier for how sensitive the helper should be about detecting
 *                    the start of a drag. Larger values are more sensitive. 1.0f is normal.
 * @param cb Callback to provide information and receive events
 * @return a new ViewDragHelper instance
*/
public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {
    final ViewDragHelper helper = create(forParent, cb);
    helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
    return helper;
}
```

ViewDragHelperのインスタンスを作成する
引数の
・第2引数`sensitivity`はドラッグ検知度合いを表す値ですね、デフォルトで1.0fであり、この値自体あまり変えることはないと思います

・第3引数`Callback`は`ViewDragHelper.Callback`であり、自分で実装することができます


## `ViewDragHelper.Callback`の動作を見ていきます 

### onViewDragStateChanged


```ViewDragHelper.java
/**
 * Called when the drag state changes. See the <code>STATE_*</code> constants
 * for more information.
 *
 * @param state The new drag state
 *
 * @see #STATE_IDLE
 * @see #STATE_DRAGGING
 * @see #STATE_SETTLING
 */
public void onViewDragStateChanged(int state) {}
```

Drag状態が変わった場合に呼び出されるということですね、Drag&Dropに使えそうです

### onViewPositionChanged

```ViewDragHelper.java
/**
 * Called when the captured view's position changes as the result of a drag or settle.
 *
 * @param changedView View whose position changed
 * @param left New X coordinate of the left edge of the view
 * @param top New Y coordinate of the top edge of the view
 * @param dx Change in X position from the last call
 * @param dy Change in Y position from the last call
 */
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {}
```

Viewの位置が変わった場合ということですね
移動した後のNewXやNewYを主に使いそうですね
`ViewGroup#getChildAt`等で元のchildViewのtopやleftを保存しておいて、そこからどれくらい移動したかとれそうですね

### onViewCaptured

```ViewDragHelper.java
/**
 * Called when a child view is captured for dragging or settling. The ID of the pointer
 * currently dragging the captured view is supplied. If activePointerId is
 * identified as {@link #INVALID_POINTER} the capture is programmatic instead of
 * pointer-initiated.
 *
 * @param capturedChild Child view that was captured
 * @param activePointerId Pointer id tracking the child capture
 */
public void onViewCaptured(View capturedChild, int activePointerId) {}
```

Dragしている最中の場合に使えますね、例えばDrag中にそのViewの透明度を上げておくとかですかね

### onViewReleased

```ViewDragHelper.java
/**
 * Called when the child view is no longer being actively dragged.
 * The fling velocity is also supplied, if relevant. The velocity values may
 * be clamped to system minimums or maximums.
 *
 * <p>Calling code may decide to fling or otherwise release the view to let it
 * settle into place. It should do so using {@link #settleCapturedViewAt(int, int)}
 * or {@link #flingCapturedView(int, int, int, int)}. If the Callback invokes
 * one of these methods, the ViewDragHelper will enter {@link #STATE_SETTLING}
 * and the view capture will not fully end until it comes to a complete stop.
 * If neither of these methods is invoked before <code>onViewReleased</code> returns,
 * the view will stop in place and the ViewDragHelper will return to
 * {@link #STATE_IDLE}.</p>
 *
 * @param releasedChild The captured child view now being released
 * @param xvel X velocity of the pointer as it left the screen in pixels per second.
 * @param yvel Y velocity of the pointer as it left the screen in pixels per second.
 */
public void onViewReleased(View releasedChild, float xvel, float yvel) {}
```

ドラッグ等をしてそこからViewが離された場合に呼ばれることになります
`releasedChild`ではViewDragHelper#createでParentViewを呼んだと思いますが、そのchildViewを呼ぶことになります
例えば `FrameLayout -> ImageView` という構成でcreateでFrameLayoutを使った場合はImageViewが対象になるということですね

今回で言えば、onViewPositionChangedで動いた分の長さをとっておいて、しきい値を超えたらActivity/Fragmentを終了したりできそうですね(swipeToDismiss)

### onEdgeTouched

```ViewDragHelper.java
/**
 * Called when one of the subscribed edges in the parent view has been touched
 * by the user while no child view is currently captured.
 *
 * @param edgeFlags A combination of edge flags describing the edge(s) currently touched
 * @param pointerId ID of the pointer touching the described edge(s)
 * @see #EDGE_LEFT
 * @see #EDGE_TOP
 * @see #EDGE_RIGHT
 * @see #EDGE_BOTTOM
 */
public void onEdgeTouched(int edgeFlags, int pointerId) {}
```

縁をタッチしている際の状況を取得できます
縁を使うようなユースケースとしてはPowerPointのテキストや画像をイメージしてもらうとわかりやすいですが、縁で伸ばしたり縮小・拡大したりということに使えそうですね

### onEdgeLock

```ViewDragHelper.java
/**
 * Called when the given edge may become locked. This can happen if an edge drag
 * was preliminarily rejected before beginning, but after {@link #onEdgeTouched(int, int)}
 * was called. This method should return true to lock this edge or false to leave it
 * unlocked. The default behavior is to leave edges unlocked.
 *
 * @param edgeFlags A combination of edge flags describing the edge(s) locked
 * @return true to lock the edge, false to leave it unlocked
 */
public boolean onEdgeLock(int edgeFlags) {
    return false;
}
```

縁をロックしたい場合に使うようですね、あまり使う機会はないかもしれませんが、縁でも別の挙動を一時的に適用した場合は一時的にtrueにしそうですね

### onEdgeDragStarted

```ViewDragHelper.java
/**
 * Called when the user has started a deliberate drag away from one
 * of the subscribed edges in the parent view while no child view is currently captured.
 *
 * @param edgeFlags A combination of edge flags describing the edge(s) dragged
 * @param pointerId ID of the pointer touching the described edge(s)
 * @see #EDGE_LEFT
 * @see #EDGE_TOP
 * @see #EDGE_RIGHT
 * @see #EDGE_BOTTOM
 */
public void onEdgeDragStarted(int edgeFlags, int pointerId) {}
```

縁をタッチしてドラッグがスタートした場合に使えますね
例えば縁を持って拡大・縮小する前にそもそものサイズを持っておいたりしておくことに使えそうです
後々undoでキャンセルしたりとか

### getViewHorizontalDragRange

```ViewDragHelper.java
/**
 * Return the magnitude of a draggable child view's horizontal range of motion in pixels.
 * This method should return 0 for views that cannot move horizontally.
 *
 * @param child Child view to check
 * @return range of horizontal motion in pixels
 */
public int getViewHorizontalDragRange(View child) {
    return 0;
}
```

水平方向へのドラッグが可能な範囲を指定できます
また、0を返す場合は水平方向には移動させないことができます

### getViewVerticalDragRange

```ViewDragHelper.java
/**
 * Return the magnitude of a draggable child view's vertical range of motion in pixels.
 * This method should return 0 for views that cannot move vertically.
 *
 * @param child Child view to check
 * @return range of vertical motion in pixels
 */
public int getViewVerticalDragRange(@NonNull View child) {
     return 0;
}
```

上記の`getViewHorizontalDragRange`の垂直方向バージョンです

### tryCaptureView

```ViewDragHelper.java
/**
 * Called when the user's input indicates that they want to capture the given child view
 * with the pointer indicated by pointerId. The callback should return true if the user
 * is permitted to drag the given view with the indicated pointer.
 *
 * <p>ViewDragHelper may call this method multiple times for the same view even if
 * the view is already captured; this indicates that a new pointer is trying to take
 * control of the view.</p>
 *
 * <p>If this method returns true, a call to {@link #onViewCaptured(android.view.View, int)}
 * will follow if the capture is successful.</p>
 *
 * @param child Child the user is attempting to capture
 * @param pointerId ID of the pointer attempting the capture
 * @return true if capture should be allowed, false otherwise
 */
public abstract boolean tryCaptureView(@NonNull View child, int pointerId);
```

ドラッグすることができる場合に`true`が返り、その場合に`onViewCaptured`が呼ばれる

### clampViewPositionHorizontal

```
/**
 * Restrict the motion of the dragged child view along the horizontal axis.
 * The default implementation does not allow horizontal motion; the extending
 * class must override this method and provide the desired clamping.
 *
 *
 * @param child Child view being dragged
 * @param left Attempted motion along the X axis
 * @param dx Proposed change in position for left
 * @return The new clamped position for left
 */
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
    return 0;
}
```

ドラッグしているViewの水平方向の位置を取得


### clampViewPositionVertical

```
/**
 * Restrict the motion of the dragged child view along the vertical axis.
 * The default implementation does not allow vertical motion; the extending
 * class must override this method and provide the desired clamping.
 *
 *
 * @param child Child view being dragged
 * @param top Attempted motion along the Y axis
 * @param dy Proposed change in position for top
 * @return The new clamped position for top
 */
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
    return 0;
}
```

ドラッグしているViewの垂直方向の位置を取得