【Kotlin】タッチイベントをフックする【Androidアプリ開発】
動画のように画面に指を触れて動かしたときの移動量を取得できるようなプログラムを作ってみた。
TouchableFragmentクラス
今回はフラグメントを使用してタッチイベントのフックを行った。 TouchableFragmentクラスを作成し、フラグメントのレイアウトファイルにイベントフック専用のビューであるtouchableViewを配置した。 このビューをフラグメントのonCreateViewで取得しリスナー登録を済ませておく。
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_touchable, container, false)
view.findViewById<View>(R.id.touchableView).setOnTouchListener(this)
return view
}
次にTouchableFragmentクラスにView.OnTouchListenerインタフェースを実装する。そしてそのメンバメソッドであるonTouchメソッドを次のようにオーバーライドする。
private var yPrec = 0.0f
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.actionMasked) {
MotionEvent.ACTION_DOWN -> {
yPrec = event.getY(0)
listener?.actionDown()
}
MotionEvent.ACTION_MOVE -> {
val dy = yPrec - event.getY(0)
listener?.actionMove(dy)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
listener?.actionUp()
}
}
return true
}
今回は移動量を計算させているが他にもVelocityTrackerを使って指の速度を得ることもできるようになっている。詳しくはこちらを参考にしてもらいたい。
タップとポインタの動きのトラッキング - Androidデベロッパードキュメントガイド
さてTouchableFragmentクラスの全体のプログラム内容は次のようになっている。
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
class TouchableFragment : Fragment(),View.OnTouchListener {
private var param1: String? = null
private var param2: String? = null
private var listener: OnTouchableFragmentListener? = null
private var yPrec = 0.0f
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.actionMasked) {
MotionEvent.ACTION_DOWN -> {
yPrec = event.getY(0)
listener?.actionDown()
}
MotionEvent.ACTION_MOVE -> {
val dy = yPrec - event.getY(0)
listener?.actionMove(dy)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
listener?.actionUp()
}
}
return true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_touchable, container, false)
view.findViewById<View>(R.id.touchableView).setOnTouchListener(this)
return view
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnTouchableFragmentListener) {
listener = context
} else {
throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener")
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
interface OnTouchableFragmentListener {
fun actionDown()
fun actionMove(dy: Float)
fun actionUp()
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
TouchableFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
MainActivityクラス
次にMainActivityクラスでの実装を行っていく。アクティビティのレイアウトファイルの左半分にはテキストビューを設置し、右半分にはフラグメントを入れ込むためにフレームレイアウトを設置した。
そしてMainActivityから動的にフラグメントを生成させるためのコードは次のようになる。
private const val TOUCHABLE_FRAGMENT_TAG = "touchableFragment"
class MainActivity : AppCompatActivity(), TouchableFragment.OnTouchableFragmentListener {
override fun actionDown() {
@SuppressLint("SetTextI18n")
resultTextView.text = "Action Down\n${resultTextView.text}"
}
override fun actionMove(dy: Float) {
@SuppressLint("SetTextI18n")
resultTextView.text = "dy: $dy\n" +
"${resultTextView.text}"
}
override fun actionUp() {
@SuppressLint("SetTextI18n")
resultTextView.text = "\nAction Up\n" +
"${resultTextView.text}"
}
lateinit var resultTextView:TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
resultTextView = findViewById(R.id.result)
if (supportFragmentManager.findFragmentByTag(TOUCHABLE_FRAGMENT_TAG) == null) {
supportFragmentManager.beginTransaction()
.add(R.id.container, TouchableFragment.newInstance("hoge", "fuga"), TOUCHABLE_FRAGMENT_TAG)
.commit()
}
}
}
フラグメントをファクトリメソッドで生成し引数を渡せるようにしている。またフラグメントからのコールバックを受けて取れるようにインターフェイスを使った実装を行っている。先頭のactionDown、actionMove、actionUpメソッドはTouchableFragmentからのコールバック関数となる。actionMoveでは指の移動量が渡されるのでその値をテキストビューにセットして表示させている。
最後にプロジェクトを実行し、画面右半分の中で指を動かせばテキストビューに移動量が表示されるだろう。
タッチイベントの処理をアクティビティに直接書いてしまうとアクティビティが読みづらくなってしまうので、できるだけフラグメントに追い出して管理したい。
参考
タップとポインタの動きのトラッキング - Androidデベロッパードキュメントガイド android: move a view on touch move (ACTION_MOVE) - Stack Overflow
▼ こんな記事も書いてます。