Custom view XxxView overrides onTouchEvent but not performClick って怒られるケースについて

# 概要

custom view を作成した際に、[onTouchEvent()](https://developer.android.com/reference/android/view/View#onTouchEvent(android.view.MotionEvent)) をオーバーライドしたが [performClick()](https://developer.android.com/reference/android/view/View#performClick())
 をオーバーライドしていない場合に、以下のような警告が表示されます。[^1]

```
Custom view XxxView overrides onTouchEvent but not performClick
```

# performClick() について
まずは [performClick()](https://developer.android.com/reference/android/view/View#performClick()) から読み解いてみようと思います。

## ◆ javadoc

> Call this view's OnClickListener, if it is defined. Performs all normal actions associated with clicking: reporting accessibility event, playing a sound, etc.

ざっくりこんな感じですね。

1. OnClickListener が定義されていればそれを呼び出す。
2. クリックに関連するすべての通常の処理(accessibility event の報告や音声再生など)を実行する。

## ◆ 解釈

これを言い換えると、

- クリック時の処理は OnClickListener に含まれる。
- クリックに関連する **OnClickListener 以外の処理**は [performClick()](https://developer.android.com/reference/android/view/View#performClick()) に含まれる。

のようなことが言えると思います。

別の見方をすると、

- custom view の外部から動的に設定される機能は OnClickListener で実行される。
- custom view として固定のクリック音やクリック時のUI効果、デザイン上の決まり事である accessibility event の報告、などは performClick() 上で実行される。

とも言えそうです。

## ◆ Viewのコード

[View#performClick()](https://developer.android.com/reference/android/view/View#performClick()) のコードでは

```java
li.mOnClickListener.onClick(this)
```

のように OnClickListener の処理が呼び出され、

```java
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED
```

のように accessibility 系のコードも呼び出されているということがわかります。

ということで、performClick() を叩けば OnClickListener やら accessibility 系などすべて含んだクリック処理を行ってくれるってことですね。

## ◆ performClick() まとめ

**クリック処理は、常に [performClick()](https://developer.android.com/reference/android/view/View#performClick()) 経由で実行しましょう。**

# onTouchEvent() について

## ◆ javadoc
以下が [onTouchEvent()](https://developer.android.com/reference/android/view/View#onTouchEvent(android.view.MotionEvent)) の javadoc の内容。

> Implement this method to handle touch screen motion events.
>
> If this method is used to detect click actions, it is recommended that the actions be performed by implementing and calling performClick(). This will ensure consistent system behavior, including:
> 
> 　　・obeying click sound preferences
> 　　・dispatching OnClickListener calls
> 　　・handling ACTION_CLICK when accessibility features are enabled

第一パラグラフは普通にタッチイベントを扱うという話。

第二パラグラフの2文目以降は [performClick()](https://developer.android.com/reference/android/view/View#performClick()) の話で、既に前項で扱った話。

ということで、重要なのは残った以下の文章。

> If this method is used to detect click actions, it is recommended that the actions be performed by implementing and calling performClick().

『このメソッドが**クリック処理を検出するために使われる**のであれば、performClick() を実装してそれを呼び出すことを推奨する』ということですが、逆を言えば、**クリック処理を検出するために使われない**場合には performClick() を実装して呼び出す必要はないという事になります。

## ◆ 補足
補足ですが、Accessibility 系のオプションを有効にしている場合など、タッチ関連の通知経路が変わることがあります。

具体的な例としては、普段は onTouchEvent() にてクリックの判断をしている場合に、端末の TalkBack を on にしていると、Accessibility 系の仕組み経由でクリックの判断が行われるため、onTouchEvent() が発生せずにいきなり performClick() が呼び出されることがあり、その場合は **onTouchEvent() 内部に書かれた処理は一切実行されません**。

# まとめ

**Custom view XxxView overrides onTouchEvent but not performClick って怒られるケースについて**という文脈でまとめると、以下のようになるのかなと思います：

- onTouchEvent() がクリック処理を検出するために**使われない**場合
  - performClick() を実装して呼び出す**必要はない**。← 警告を suppress しちゃってOKだと思われ。
- onTouchEvent() がクリック処理を検出するために**使われる**場合
  - クリックを検出した箇所で、クリックに関する処理はすべて performClick() に丸投げしましょう。[^2]

以上。

[^1]: クラス名が XxxView の場合。
[^2]: クリックに関する処理が onTouchEvent() に含まれている場合、performClick() 経由ではその処理が実行されない。