ボタンのサイズを変えずにタップ領域のみを拡張するという要件を実装していてハマったので備忘録として記事に残します。
コードだけ知りたいという方はこの記事の最後の方に載せていますのでそちらを確認してください。
どうやって実現するか
TouchDelegate
を使って実装します。
公式リファレンスはこちら
上記の公式レファレンスで
Helper class to handle situations where you want a view to have a larger touch area than its actual view bounds.
と記載があるので今回の要件を実装する際の正攻法だと思います。
他にも良い方法がありましたらコメントで教えていただけると嬉しいです。
TouchDelegate
TouchDelegate
を使って何をしているかというと
「タップ領域を拡張したいView(この例だとボタン)の祖先のViewに対して拡張したタップ領域(図のピンクの領域)を指定して、この領域内でタップイベントが発生した時の処理をタップ領域を拡張したいView(この例だとボタン)に委譲する」
→ピンクの領域をタップした時の処理を、ボタンをタップした時の処理として扱う
ということをやっています。
ここでの注意点は、ピンクの領域を指定する祖先のViewにクリック処理を実装していた場合、ピンクの領域をタップしてボタンの処理が実行された後祖先のViewのクリックイベントが反応しなくなるという点です。
TouchDelegateを使ってみる
前置きが長くなりましたが実際に TouchDelegate
を使ってみます。
ピンクの領域は祖先のViewに対して指定できるので、ボタンから見て階層が上のViewであれば利用できるのですが、個人的には親のViewに対して指定するのが良いかなと思います。
何故かというと、
ピンクの領域の座標をTouchDelegateを指定する祖先のViewからの相対座標で指定する必要があるからです。
※実際にタップ領域を拡張する時は ConstraintLayout
や FrameLayout
に対して指定する場面が多いと思うので親ViewGroupとしてますがViewでも大丈夫です。
コード
コードはこんな感じになります。
func View.expandTapArea() {
val parentViewGroup = this.parent as ViewGroup
parentViewGroup?.post {
val rect = Rect() // <- ピンク領域のRect
// ピンク領域のRectを生成
parentViewGroup.touchDelegate = TouchDelegate(rect, this)
}
}
// 使い方
button.expandTapArea()
以上です。