TouchDelegateを使ってタッチ可能領域を拡張する

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

Buttonのクリックに反応する範囲を拡張したい時がある。
今回は下図のようなカスタムViewを作りたかった。

Image

ImageButtonとTextViewを内包したViewである。
親レイアウトのViewGroupをタッチする=ImageButtonをタッチするという扱いにしたかった。

ImageButtonにしているのは、他のViewとの兼ね合いである。
他のViewはImageButtonにstyle="@style/Base.Widget.AppCompat.ActionButton"を適用して、アイコン画像の周りにだけリップルエフェクトがかかるようになっていて、このカスタムViewもそれに合わせたかったのである。

これはTouchDelegateを使うと実現できる。

TouchDelegate – Android Developers

使い方

TouchDelegateは親のViewGroupに設定する。

TouchDelegateのインスタンスを作成するには、タッチエリアを表すRectと委譲先のView(今回でいうとImageButton)への参照が必要。
Rectはローカル座標系(ディスプレイ上での絶対座標ではなく、ViewGroupの左上を0とした相対座標)を使う。
今回はViewGroupのgetDrawingRect()を使って取得したRectを使った。

ぐぐるとgetHitRect()を使った例がみつかったが、これだとうまく動かなかった。絶対座標になっているからだと思われる。

コード例

class CommentStatusView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    private lateinit var binding: ViewCommentStatusBinding

    init {
        binding = ViewCommentStatusBinding.inflate(LayoutInflater.from(context), this, true)
        doOnPreDraw {
            val rect = Rect()
            binding.viewGroup.getDrawingRect(rect)
            binding.viewGroup.touchDelegate = TouchDelegate(rect, binding.imageButton)
        }
    }

    override fun setOnClickListener(l: OnClickListener?) {
        binding.imageButton.setOnClickListener(l)
    }

}

Kotlinで、Android-ktxとDataBindingを使っている。

doOnPreDrawがAndroid-ktxを使っている部分で、ViewTreeObserverを利用してViewの大きさが決まった後で中のブロックの処理(TouchDelegateの設定)を行っているだけである。

後はこのカスタムViewに対するsetOnClicklistenerをImageButtonに対して設定するようにしている。

ポイントは

  1. 親のViewGroupに対してTouchDelegateを設定する
  2. rectはTouchDelegateを指定するViewGroupからみて、どの位置のタッチイベントを委譲先に渡すのかをローカル座標系で指定する

ドキュメントに書いてあるとおりなのだが、英語力がなくていまいちわからず、ググったコードを参考にしながらやってもうまくいかずでちょっとハマった。

実際の動きはこんな感じになった。
リップルエフェクトもクリックリスナもViewGroupに対するものがImageButtonを押している扱いになっている。

Ezgif 4 fc7d03caa2

ご意見ご感想はこちら

お名前 (必須)

メールアドレス (必須)

メッセージ本文

メッセージを送信するにはチェックを付けてください。