AndroidのViewDragHelperを使ってYouTube, SoundCloudのようなアニメーションを実装する!

こんにちは!エンジニアの後藤です。

業務ではGo言語でウェブアプリケーションを作ることに専念しているので、Webエンジニアと化していますが、ネイティブアプリが大好きなので休日はネイティブエンジニアに変化します。

さて、本題に。今回は、Androidに焦点を当てて、リッチなドラッグを作成するポイントを紹介します。YouTubeやSoundCloudで使われているアニメーションと言えば想像がつくでしょうか。

YouTube, SoundCloudのようなドラッグを実装する

実は既にライブラリが複数存在しているので、それらを使用するのが一番です。

YouTubeのような動きは DraggablePanel

SoundCloudのような動きは AndroidSlidingUpPanel

をそれぞれ使えば良いです。

今回はこれらのライブラリが共通して使用している ViewDragHelper というクラスの理解を深めたいと思います。これを使えばどんなドラッグもお手の物です。かっこいいカスタムView作りまくりましょう!

ViewDragHeleperとは

カスタマイズされたViewGroupクラスのためのUtilityクラスです。

ViewGroupの子Viewのドラッグや位置状態をトラッキングするための便利なメソッドが複数用意されています。

以下のようなアニメーションを簡単に実装できます。

drager-sample2

さっそくですが、ViewDragHelperの使い方を解説していきます!

ViewDragHelperのインスタンスを作成する

まずは ViewGroup を継承したクラスを用意します。

public class HogeLayout extends ViewGroup {
...
}
```

次に ViewDragHelperのインスタンスを作成するために ViewDragHelper.create メソッドを呼び出します。

ViewDragHelper.createメソッドの引数について解説しておきます。

第1引数には ViewGroupを継承したクラスが入ります。

第2引数には ドラッグ検知の感度合を示すfloat型の値が入ります。(AndroidのDocumentではsensitivityと呼ばれています)

第3引数には ViewDragHelper.Callbackクラスが入ります。これについては後ほど紹介します。

private static final float SENSITIVITY = 1.0f;
private ViewDragHelper viewDragHelper;
private void setupViewDragHelper() {
viewDragHelper = ViewDragHelper.create(HogeLayout.this, SENSITIVITY, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
});
}
ViewGroup継承クラスの onInterceptTouchEventonTouchEvent メソッドをオーバーライドする
TouchEventをViewDragHelperに譲渡してあげます。
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
viewDragHelper.cancel();
return false;
default:
break;
}
return viewDragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
viewDragHelper.processTouchEvent(event);
return true;
}
以上でViewDragHelperを使う準備は完了です!続いて Callback について解説していきます!
ViewDragHelper.Callbackについて
Callbackクラスは tryCaptureView メソッドを必ずオーバーライドしなければなりません。

このメソッドでドラッグしたいViewとドラッグしたくないViewを切り分けることできます。
new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return draggableView == child; // or unDraggableView != child
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
// tryCaptureViewでtrueが返った場合はonViewCapturedメソッドが呼ばれます。
}
}
ドラッグする
clampViewPositionVertical, clampViewPositionHorizontal メソッドをオーバーライドします。

ここで返された値がドラッグしているViewの位置になります。
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
ドラッグ可能な範囲を決める
getViewVerticalDragRange, getViewHorizontalDragRange メソッドをオーバーライドします。ここで返された値がドラッグ可能な範囲になります。0を返すとViewをドラッグすることが出来なくなります。
@Override
public int getViewVerticalDragRange(View child) {
return draggableRange;
}
@Override
public int getViewHorizontalDragRange(View child) {
return draggableRange;
}
Viewの位置を受け取る
Viewの位置が変更された場合は onViewPositionChanged メソッドが呼ばれます。
引数の dx, dy とは前の位置から移動した距離です。
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
}
さいごに
ViewDragHelperには沢山のAPIが存在しますが、今回解説したポイントを押さえておけば、YouTube, SoundCloudのようなドラッグを実現することが出来ます。
サポートライブラリで提供されている SlidingPaneLayout にも使用されているのでそちらも参考に!
今回使ったコードのサンプルはGithubに置いてあります。
参考URL
http://developer.android.com/reference/android/support/v4/widget/ViewDragHelper.html
http://developer.android.com/reference/android/support/v4/widget/ViewDragHelper.Callback.html
https://android.googlesource.com/platform/frameworks/support/+/master/v4/java/android/support/v4/widget/ViewDragHelper.java
https://android.googlesource.com/platform/frameworks/support/+/master/v4/java/android/support/v4/widget/SlidingPaneLayout.java
Like what you read? Give eureka_developers a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.