Posted at

[Android] フリック入力機能を実装してみた


完成形!

flickMovie.gif


はじめに

フリック機能を搭載したアプリを作りたかったのですが、公式を見ると

フリックされたことを検知する処理しか掲載されていなかったので、

こちらの記事を参考にさせていただき、自作でリスナーを作成しました。

これからフリック入力機能を実装したいと思う方の一助になればと思います。


実装


FlickListener


FlickListener.kt

class FlickListener(

private val listener: Listener
) : View.OnTouchListener {

// フリックイベントのリスナー
interface Listener {
fun onButtonPressed()
fun onButtonReleased()
fun onFlickToLeft()
fun onFlickToRight()
fun onFlickToUp()
fun onFlickToDown()
fun onFlickOutside()
fun onSwipingOnLeft()
fun onSwipingOnRight()
fun onSwipingOnUp()
fun onSwipingOnDown()
fun onSwipingOnCenter()
fun onSwipingOutside()
}

companion object {
// フリック判定時の遊び。小さいほど判定が敏感になる
const val DEFAULT_PLAY = 100f
}

private val play = DEFAULT_PLAY
private var startX: Float = 0f
private var startY: Float = 0f
private var endX: Float = 0f
private var endY: Float = 0f

override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> touchDown(event)
MotionEvent.ACTION_MOVE -> swipe(event)
MotionEvent.ACTION_UP -> touchOff(event)
}
return true
}

private fun touchDown(event: MotionEvent) {
startX = event.x
startY = event.y
listener.onButtonPressed()
}

private fun swipe(event: MotionEvent) {
endX = event.x
endY = event.y
when {
outScope() -> listener.onSwipingOutside()
leftScope() -> listener.onSwipingOnLeft()
rightScope() -> listener.onSwipingOnRight()
upScope() -> listener.onSwipingOnUp()
downScope() -> listener.onSwipingOnDown()
else -> listener.onSwipingOnCenter()
}
}

private fun touchOff(event: MotionEvent) {
endX = event.x
endY = event.y
when {
outScope() -> listener.onFlickOutside()
leftScope() -> listener.onFlickToLeft()
rightScope() -> listener.onFlickToRight()
upScope() -> listener.onFlickToUp()
downScope() -> listener.onFlickToDown()
else -> listener.onButtonReleased()
}
}

private fun leftScope(): Boolean = endX < startX - play
private fun rightScope(): Boolean = startX + play < endX
private fun upScope(): Boolean = endY < startY - play
private fun downScope(): Boolean = startY + play < endY
private fun outScope(): Boolean =
(leftScope() && upScope()) || (rightScope() && upScope()) ||
(leftScope() && downScope()) || (rightScope() && downScope())
}


startX, startY, endX, endYplay との関係ついては、次の図を見てもらえればわかると思います!

ボタンのサイズや感度に合うように、play を変更して調整しましょう。

FlickScope.png


MainActivity

centerボタンにFlickListernerを設定し、そのプロパティをオブジェクト式で実装します。


MainActivity.kt

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
center.setOnTouchListener(FlickListener(flickListener))
}

private val flickListener = object : FlickListener.Listener {

override fun onButtonPressed() {
center.setBackgroundButtonColor(R.color.pressedButtonColor)
toggleVisible()
}

override fun onSwipingOnCenter() = center.settingSwipe()
override fun onSwipingOnLeft() = left.settingSwipe()
override fun onSwipingOnRight() = right.settingSwipe()
override fun onSwipingOnUp() = top.settingSwipe()
override fun onSwipingOnDown() = bottom.settingSwipe()

override fun onButtonReleased() = settingFlick("中")
override fun onFlickToLeft() = settingFlick("左")
override fun onFlickToRight() = settingFlick("右")
override fun onFlickToUp() = settingFlick("上")
override fun onFlickToDown() = settingFlick("下")

private fun settingFlick(label: String) {
showToast(label)
Thread.sleep(100)
toggleInvisible()
clearAll()
}

private fun toggleVisible() {
top.visibility = View.VISIBLE
left.visibility = View.VISIBLE
right.visibility = View.VISIBLE
bottom.visibility = View.VISIBLE
}

private fun toggleInvisible() {
top.visibility = View.INVISIBLE
left.visibility = View.INVISIBLE
right.visibility = View.INVISIBLE
bottom.visibility = View.INVISIBLE
}

private fun clearAll() {
center.setBackgroundButtonColor(R.color.baseButtonColor)
left.setBackgroundButtonColor(R.color.baseButtonColor)
right.setBackgroundButtonColor(R.color.baseButtonColor)
top.setBackgroundButtonColor(R.color.baseButtonColor)
bottom.setBackgroundButtonColor(R.color.baseButtonColor)
}

private fun View.settingSwipe() {
clearAll()
setBackgroundButtonColor(R.color.pressedButtonColor)
}

private fun View.setBackgroundButtonColor(@ColorRes resId: Int) =
setBackgroundColor(ContextCompat.getColor(applicationContext, resId))

private fun showToast(msg: String) = Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
}
}



おわりに

今回、フリックする速度は考慮していません!

それでも機能としては十分だったので、よければ使ってみてください\(^o^)/

また、この機能の実装自体は難しいものではなかったので、

Android初心者の方の勉強用としても最適だと思いました!

もっとこうすれば良くなる!などの改善点やアドバイスがあれば教えていただけると嬉しいです!