2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

VelocityTrackerを使うときはオフセットに気をつけなさいって話

Last updated at Posted at 2021-04-05

タッチ操作でViewなどを動かす場合、単にドラッグに合わせて動かすだけでなく、指を離す前の移動速度を計算し、慣性動作をさせると操作性が上がりますね。
AndroidではVelocityTrackerを使うことで、操作の速度を簡単に計算することができます。

ざっくり言うと以下のように使います。

private var velocityTracker: VelocityTracker? = null

override fun onTouchEvent(event: MotionEvent): Boolean {
    val velocityTracker = velocityTracker ?: VelocityTracker.obtain().also { velocityTracker = it }
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            velocityTracker.clear()
            velocityTracker.addMovement(event)
        }
        MotionEvent.ACTION_MOVE -> {
            velocityTracker.addMovement(event)
        }
        MotionEvent.ACTION_UP -> {
            // 引数はms、1000を渡すとpixel/sの速度が得られる
            velocityTracker.computeCurrentVelocity(1000)
            val vx = velocityTracker.xVelocity
            val vy = velocityTracker.yVelocity
            // ここで計算した速度を使って慣性動作させる
            velocityTracker.recycle()
            this.velocityTracker = null
        }
        MotionEvent.ACTION_CANCEL -> {
            velocityTracker.recycle()
            this.velocityTracker = null
        }
    }
    return true
}

View自体が動いている場合はオフセット計算が必要

しかし、View自体が動いている場合は、オフセット計算が必要です。
私は、毎回オフセット計算を忘れて時間を無駄にしております:cry:。頻繁に使うものでもないですしね。。

ViewのonTouchEventやOnTouchListenerのonTouchに渡されるMotionEventの座標は、そのViewの左上を原点とする相対座標系に変換されています。VelocityTrackerはこの相対的な座標を元に速度を計算しますので、このタッチ操作によって移動しているViewのMotionEventを渡してしまうと、操作の移動速度ではなく、タッチとViewの移動のズレだけを拾うことになるので、おかしな値が計算されてしまいます。

というわけで、親Viewの座標系に変換してVelocityTrackerに渡すか

private fun VelocityTracker.addAbsoluteMovement(v: View, event: MotionEvent) {
    val dx = v.x
    val dy = v.y
    event.offsetLocation(dx, dy)
    addMovement(event)
    event.offsetLocation(-dx, -dy)
}

raw情報を元に、絶対座標に変換してVelocityTrackerに渡す必要があります。

private fun VelocityTracker.addAbsoluteMovement(event: MotionEvent) {
    val dx = event.rawX - event.x
    val dy = event.rawY - event.y
    event.offsetLocation(dx, dy)
    addMovement(event)
    event.offsetLocation(-dx, -dy)
}

注意点としては、offsetLocationで座標系を変換した場合、必ず逆変換を行って元に戻しておきましょう。

以上です。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?