Skip to content

Commit 1904022

Browse files
タッチイベントを視覚化するカスタムビューの実装
Co-Authored-By: [email protected] <[email protected]>
1 parent 662c364 commit 1904022

File tree

6 files changed

+301
-0
lines changed

6 files changed

+301
-0
lines changed

examples/sample_kotlin/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
<category android:name="android.intent.category.LAUNCHER" />
2020
</intent-filter>
2121
</activity>
22+
23+
<activity
24+
android:name=".TouchVisualizerActivity"
25+
android:exported="false"
26+
android:theme="@style/Theme.AppCompat.NoActionBar" />
2227

2328
<service
2429
android:name=".MyFirebaseMessagingService"

examples/sample_kotlin/src/main/java/io/karte/sample_kotlin/MainActivity.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ class MainActivity : AppCompatActivity() {
5555
fetchInboxMessages()
5656
}
5757
}
58+
59+
button_touch_visualizer.setOnClickListener {
60+
val intent = android.content.Intent(this, TouchVisualizerActivity::class.java)
61+
startActivity(intent)
62+
}
5863
}
5964

6065
private fun sendIdentifyEvent() {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.karte.sample_kotlin
2+
3+
import android.os.Bundle
4+
import android.view.View
5+
import android.widget.Button
6+
import android.widget.TextView
7+
import androidx.appcompat.app.AppCompatActivity
8+
9+
/**
10+
* タッチ視覚化のデモを表示するアクティビティ
11+
*/
12+
class TouchVisualizerActivity : AppCompatActivity() {
13+
14+
override fun onCreate(savedInstanceState: Bundle?) {
15+
super.onCreate(savedInstanceState)
16+
setContentView(R.layout.activity_touch_visualizer)
17+
18+
// ツールバーの設定
19+
setSupportActionBar(findViewById(R.id.tool_bar))
20+
supportActionBar?.title = "タッチ視覚化デモ"
21+
supportActionBar?.setDisplayHomeAsUpEnabled(true)
22+
23+
// 説明テキストの設定
24+
val descriptionTextView = findViewById<TextView>(R.id.description_text)
25+
descriptionTextView.text = "画面上の任意の場所をタップすると、タッチした箇所が視覚的に表示されます。" +
26+
"複数の指でタップすると、それぞれのタッチポイントが表示されます。"
27+
28+
// ボタンのクリックリスナー設定
29+
findViewById<Button>(R.id.button_test).setOnClickListener {
30+
it.isSelected = !it.isSelected
31+
if (it.isSelected) {
32+
(it as Button).text = "ボタンがタップされました!"
33+
} else {
34+
(it as Button).text = "このボタンをタップしてください"
35+
}
36+
}
37+
}
38+
39+
override fun onSupportNavigateUp(): Boolean {
40+
onBackPressed()
41+
return true
42+
}
43+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package io.karte.sample_kotlin
2+
3+
import android.animation.ValueAnimator
4+
import android.content.Context
5+
import android.graphics.Canvas
6+
import android.graphics.Color
7+
import android.graphics.Paint
8+
import android.util.AttributeSet
9+
import android.view.MotionEvent
10+
import android.view.View
11+
import android.view.animation.DecelerateInterpolator
12+
import android.widget.FrameLayout
13+
import java.util.concurrent.ConcurrentHashMap
14+
15+
/**
16+
* タッチイベントを視覚的に表示するカスタムビュー
17+
* dispatchTouchEventをオーバーライドしてタッチ箇所を円で表示します
18+
*/
19+
class TouchVisualizerView @JvmOverloads constructor(
20+
context: Context,
21+
attrs: AttributeSet? = null,
22+
defStyleAttr: Int = 0
23+
) : FrameLayout(context, attrs, defStyleAttr) {
24+
25+
// タッチポイントを保持するマップ
26+
private val touchPoints = ConcurrentHashMap<Int, TouchPoint>()
27+
28+
// タッチポイントの描画用ペイント
29+
private val paint = Paint().apply {
30+
isAntiAlias = true
31+
color = Color.BLUE
32+
alpha = 128
33+
style = Paint.STYLE.FILL
34+
}
35+
36+
init {
37+
// 背景を透明に設定
38+
setWillNotDraw(false)
39+
setBackgroundColor(Color.TRANSPARENT)
40+
}
41+
42+
/**
43+
* タッチイベントをインターセプトして視覚化
44+
*/
45+
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
46+
val pointerIndex = event.actionIndex
47+
val pointerId = event.getPointerId(pointerIndex)
48+
val x = event.getX(pointerIndex)
49+
val y = event.getY(pointerIndex)
50+
51+
when (event.actionMasked) {
52+
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
53+
// 新しいタッチポイントを追加
54+
val touchPoint = TouchPoint(x, y)
55+
touchPoints[pointerId] = touchPoint
56+
touchPoint.startAnimation()
57+
}
58+
MotionEvent.ACTION_MOVE -> {
59+
// 既存のポインターすべての位置を更新
60+
for (i in 0 until event.pointerCount) {
61+
val id = event.getPointerId(i)
62+
touchPoints[id]?.apply {
63+
this.x = event.getX(i)
64+
this.y = event.getY(i)
65+
}
66+
}
67+
}
68+
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> {
69+
// タッチポイントをフェードアウト
70+
touchPoints[pointerId]?.startFadeOut()
71+
}
72+
}
73+
74+
invalidate()
75+
// 子ビューにイベントを渡す
76+
return super.dispatchTouchEvent(event)
77+
}
78+
79+
override fun onDraw(canvas: Canvas) {
80+
super.onDraw(canvas)
81+
82+
// すべてのタッチポイントを描画
83+
for (touchPoint in touchPoints.values) {
84+
paint.alpha = touchPoint.alpha
85+
canvas.drawCircle(touchPoint.x, touchPoint.y, touchPoint.radius, paint)
86+
}
87+
}
88+
89+
/**
90+
* タッチポイントを表すデータクラス
91+
*/
92+
private inner class TouchPoint(
93+
var x: Float,
94+
var y: Float,
95+
var radius: Float = 0f,
96+
var alpha: Int = 0
97+
) {
98+
private var animator: ValueAnimator? = null
99+
private var fadeOutAnimator: ValueAnimator? = null
100+
101+
/**
102+
* タッチポイントのアニメーションを開始
103+
*/
104+
fun startAnimation() {
105+
// 既存のアニメーションをキャンセル
106+
animator?.cancel()
107+
fadeOutAnimator?.cancel()
108+
109+
// 出現アニメーション
110+
animator = ValueAnimator.ofFloat(0f, 80f).apply {
111+
duration = 300
112+
interpolator = DecelerateInterpolator()
113+
addUpdateListener { animation ->
114+
radius = animation.animatedValue as Float
115+
alpha = 128
116+
invalidate()
117+
}
118+
start()
119+
}
120+
}
121+
122+
/**
123+
* フェードアウトアニメーションを開始
124+
*/
125+
fun startFadeOut() {
126+
fadeOutAnimator = ValueAnimator.ofInt(128, 0).apply {
127+
duration = 500
128+
addUpdateListener { animation ->
129+
alpha = animation.animatedValue as Int
130+
invalidate()
131+
if (alpha == 0) {
132+
// アニメーション終了時にマップから削除
133+
touchPoints.values.removeIf { it === this@TouchPoint }
134+
}
135+
}
136+
start()
137+
}
138+
}
139+
}
140+
}

examples/sample_kotlin/src/main/res/layout/activity_main.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@
8989
android:text="Fetch Inbox"
9090
android:textAllCaps="false" />
9191

92+
<Button
93+
android:id="@+id/button_touch_visualizer"
94+
android:layout_width="match_parent"
95+
android:layout_height="wrap_content"
96+
android:text="タッチ視覚化デモ"
97+
android:textAllCaps="false" />
98+
9299
<TextView
93100
android:id="@+id/inbox_content"
94101
android:layout_width="match_parent"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<io.karte.sample_kotlin.TouchVisualizerView xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent"
6+
tools:context=".TouchVisualizerActivity">
7+
8+
<LinearLayout
9+
android:layout_width="match_parent"
10+
android:layout_height="match_parent"
11+
android:orientation="vertical">
12+
13+
<androidx.appcompat.widget.Toolbar
14+
android:id="@+id/tool_bar"
15+
android:layout_width="match_parent"
16+
android:layout_height="wrap_content"
17+
android:background="?attr/colorPrimary"
18+
android:minHeight="?attr/actionBarSize"
19+
android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
20+
21+
<ScrollView
22+
android:layout_width="match_parent"
23+
android:layout_height="match_parent">
24+
25+
<LinearLayout
26+
android:layout_width="match_parent"
27+
android:layout_height="wrap_content"
28+
android:orientation="vertical"
29+
android:padding="16dp">
30+
31+
<TextView
32+
android:id="@+id/description_text"
33+
android:layout_width="match_parent"
34+
android:layout_height="wrap_content"
35+
android:layout_marginBottom="16dp"
36+
android:textSize="16sp" />
37+
38+
<Button
39+
android:id="@+id/button_test"
40+
android:layout_width="match_parent"
41+
android:layout_height="wrap_content"
42+
android:layout_marginTop="16dp"
43+
android:text="このボタンをタップしてください" />
44+
45+
<View
46+
android:layout_width="match_parent"
47+
android:layout_height="1dp"
48+
android:layout_marginTop="24dp"
49+
android:layout_marginBottom="24dp"
50+
android:background="#DDDDDD" />
51+
52+
<TextView
53+
android:layout_width="match_parent"
54+
android:layout_height="wrap_content"
55+
android:layout_marginBottom="16dp"
56+
android:text="タッチ視覚化の使い方"
57+
android:textSize="18sp"
58+
android:textStyle="bold" />
59+
60+
<TextView
61+
android:layout_width="match_parent"
62+
android:layout_height="wrap_content"
63+
android:text="1. TouchVisualizerViewを任意のレイアウトのルート要素として使用します。"
64+
android:textSize="14sp" />
65+
66+
<TextView
67+
android:layout_width="match_parent"
68+
android:layout_height="wrap_content"
69+
android:layout_marginTop="8dp"
70+
android:text="2. このビューは子要素のタッチイベントを妨げることなく、タッチ位置を視覚的に表示します。"
71+
android:textSize="14sp" />
72+
73+
<TextView
74+
android:layout_width="match_parent"
75+
android:layout_height="wrap_content"
76+
android:layout_marginTop="8dp"
77+
android:text="3. マルチタッチにも対応しています。"
78+
android:textSize="14sp" />
79+
80+
<TextView
81+
android:layout_width="match_parent"
82+
android:layout_height="wrap_content"
83+
android:layout_marginTop="32dp"
84+
android:text="サンプルコード:"
85+
android:textSize="16sp"
86+
android:textStyle="bold" />
87+
88+
<TextView
89+
android:layout_width="match_parent"
90+
android:layout_height="wrap_content"
91+
android:layout_marginTop="8dp"
92+
android:background="#F5F5F5"
93+
android:padding="12dp"
94+
android:text="&lt;io.karte.sample_kotlin.TouchVisualizerView\n android:layout_width=&quot;match_parent&quot;\n android:layout_height=&quot;match_parent&quot;&gt;\n \n &lt;!-- ここに通常のレイアウト要素を配置 --&gt;\n &lt;LinearLayout\n android:layout_width=&quot;match_parent&quot;\n android:layout_height=&quot;match_parent&quot;\n android:orientation=&quot;vertical&quot;&gt;\n ...\n &lt;/LinearLayout&gt;\n \n&lt;/io.karte.sample_kotlin.TouchVisualizerView&gt;"
95+
android:textSize="12sp"
96+
android:typeface="monospace" />
97+
98+
</LinearLayout>
99+
</ScrollView>
100+
</LinearLayout>
101+
</io.karte.sample_kotlin.TouchVisualizerView>

0 commit comments

Comments
 (0)