Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/sample_kotlin/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".TouchVisualizerActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.NoActionBar" />

<service
android:name=".MyFirebaseMessagingService"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class MainActivity : AppCompatActivity() {
fetchInboxMessages()
}
}

button_touch_visualizer.setOnClickListener {
val intent = android.content.Intent(this, TouchVisualizerActivity::class.java)
startActivity(intent)
}
}

private fun sendIdentifyEvent() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.karte.sample_kotlin

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

/**
* タッチ視覚化のデモを表示するアクティビティ
*/
class TouchVisualizerActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_touch_visualizer)

// ツールバーの設定
setSupportActionBar(findViewById(R.id.tool_bar))
supportActionBar?.title = "タッチ視覚化デモ"
supportActionBar?.setDisplayHomeAsUpEnabled(true)

// 説明テキストの設定
val descriptionTextView = findViewById<TextView>(R.id.description_text)
descriptionTextView.text = "画面上の任意の場所をタップすると、タッチした箇所が視覚的に表示されます。" +
"複数の指でタップすると、それぞれのタッチポイントが表示されます。"

// ボタンのクリックリスナー設定
findViewById<Button>(R.id.button_test).setOnClickListener {
it.isSelected = !it.isSelected
if (it.isSelected) {
(it as Button).text = "ボタンがタップされました!"
} else {
(it as Button).text = "このボタンをタップしてください"
}
}
}

override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package io.karte.sample_kotlin

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
import java.util.Collections
import java.util.concurrent.CopyOnWriteArrayList

/**
* タッチイベントを視覚的に表示するカスタムビュー
* dispatchTouchEventをオーバーライドしてタッチ箇所を円で表示します
*/
class TouchVisualizerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

// タッチポイントを保持するマップ(スレッドセーフ)
private val touchPoints = Collections.synchronizedMap(HashMap<Int, TouchPoint>())

// 描画用のタッチポイントリスト(スレッドセーフ)
private val drawingPoints = CopyOnWriteArrayList<TouchPoint>()

// UIスレッドでの処理用ハンドラ
private val mainHandler = Handler(Looper.getMainLooper())

// タッチポイントの描画用ペイント
private val paint = Paint().apply {
isAntiAlias = true
color = Color.BLUE
alpha = 128
style = Paint.Style.FILL
}

init {
// 背景を透明に設定
setWillNotDraw(false)
setBackgroundColor(Color.TRANSPARENT)
}

/**
* タッチイベントをインターセプトして視覚化
*/
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
val pointerIndex = event.actionIndex
val pointerId = event.getPointerId(pointerIndex)
val x = event.getX(pointerIndex)
val y = event.getY(pointerIndex)

when (event.actionMasked) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
// 新しいタッチポイントを追加(UIスレッドで実行)
mainHandler.post {
val touchPoint = TouchPoint(x, y)
synchronized(touchPoints) {
touchPoints[pointerId] = touchPoint
// 描画用リストにも追加
drawingPoints.add(touchPoint)
}
touchPoint.startAnimation()
}
}
MotionEvent.ACTION_MOVE -> {
// 既存のポインターすべての位置を更新(UIスレッドで実行)
mainHandler.post {
synchronized(touchPoints) {
for (i in 0 until event.pointerCount) {
val id = event.getPointerId(i)
touchPoints[id]?.apply {
this.x = event.getX(i)
this.y = event.getY(i)
}
}
}
// 再描画を要求
postInvalidateOnAnimation()
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> {
// タッチポイントをフェードアウト(UIスレッドで実行)
mainHandler.post {
synchronized(touchPoints) {
touchPoints[pointerId]?.startFadeOut()
}
}
}
}

// 子ビューにイベントを渡す
return super.dispatchTouchEvent(event)
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

// スレッドセーフなリストからすべてのタッチポイントを描画
for (touchPoint in drawingPoints) {
paint.alpha = touchPoint.alpha
canvas.drawCircle(touchPoint.x, touchPoint.y, touchPoint.radius, paint)
}
}

/**
* タッチポイントを表すデータクラス
*/
private inner class TouchPoint(
@Volatile var x: Float,
@Volatile var y: Float,
@Volatile var radius: Float = 0f,
@Volatile var alpha: Int = 0
) {
private var animator: ValueAnimator? = null
private var fadeOutAnimator: ValueAnimator? = null

/**
* タッチポイントのアニメーションを開始
*/
fun startAnimation() {
// 既存のアニメーションをキャンセル
animator?.cancel()
fadeOutAnimator?.cancel()

// 出現アニメーション
animator = ValueAnimator.ofFloat(0f, 80f).apply {
duration = 300
interpolator = DecelerateInterpolator()
addUpdateListener { animation ->
radius = animation.animatedValue as Float
alpha = 128
// UIスレッドで再描画を要求
postInvalidateOnAnimation()
}
start()
}
}

/**
* フェードアウトアニメーションを開始
*/
fun startFadeOut() {
fadeOutAnimator = ValueAnimator.ofInt(128, 0).apply {
duration = 500
addUpdateListener { animation ->
alpha = animation.animatedValue as Int
// UIスレッドで再描画を要求
postInvalidateOnAnimation()

if (alpha == 0) {
// アニメーション終了時にマップから削除(UIスレッドで実行)
mainHandler.post {
synchronized(touchPoints) {
// IDでの削除
val idToRemove = touchPoints.entries.find { it.value === this@TouchPoint }?.key
idToRemove?.let { touchPoints.remove(it) }

// 描画用リストからも削除
drawingPoints.remove(this@TouchPoint)
}
}
}
}
start()
}
}
}
}
7 changes: 7 additions & 0 deletions examples/sample_kotlin/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@
android:text="Fetch Inbox"
android:textAllCaps="false" />

<Button
android:id="@+id/button_touch_visualizer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="タッチ視覚化デモ"
android:textAllCaps="false" />

<TextView
android:id="@+id/inbox_content"
android:layout_width="match_parent"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<io.karte.sample_kotlin.TouchVisualizerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TouchVisualizerActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">

<TextView
android:id="@+id/description_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:textSize="16sp" />

<Button
android:id="@+id/button_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="このボタンをタップしてください" />

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:background="#DDDDDD" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="タッチ視覚化の使い方"
android:textSize="18sp"
android:textStyle="bold" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1. TouchVisualizerViewを任意のレイアウトのルート要素として使用します。"
android:textSize="14sp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="2. このビューは子要素のタッチイベントを妨げることなく、タッチ位置を視覚的に表示します。"
android:textSize="14sp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="3. マルチタッチにも対応しています。"
android:textSize="14sp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="サンプルコード:"
android:textSize="16sp"
android:textStyle="bold" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="#F5F5F5"
android:padding="12dp"
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;"
android:textSize="12sp"
android:typeface="monospace" />

</LinearLayout>
</ScrollView>
</LinearLayout>
</io.karte.sample_kotlin.TouchVisualizerView>
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal class IAMProcessor(application: Application, private val panelWindowMan
private val container = WebViewContainer(application, this)
private val webView: IAMWebView?
get() = container.get()
private var window: IAMWindow? = null
private var window: TouchVisualizerIAMWindow? = null
private var currentActivity: WeakReference<Activity>? = null
private var isWindowFocusByCross = false
private var isWindowFocus = false
Expand Down Expand Up @@ -102,7 +102,7 @@ internal class IAMProcessor(application: Application, private val panelWindowMan
}
dismiss()

window = IAMWindow(activity, panelWindowManager)
window = TouchVisualizerIAMWindow(activity, panelWindowManager)
window?.show(isWindowFocus, webView)
}

Expand Down
Loading
Loading