【Android & Kotlin】タッチのフィードバックを自前の画像がフェードアウトするアニメーションで実装してみた

Android & Kotlin

画面をタッチしたときのフィードバックに、自分で用意した画像をフェードアウトするアニメーションで実装する方法を説明します。

動作イメージ

タッチした座標にフィードバック用の画像を300ミリ秒かけてフェードアウトさせています。

ソースコード

タッチのフィードバック用に作成したTouchFeedbackViewを上に重ねて、タッチした座標にフィードバック用の画像を描画&フェードアウトするアニメーションをtouchFeedbackメソッドで実行しています。

開発中のマインスイーパーのソースから関連する部分を抜粋して載せておきます。

開発環境は次の通りです。

PCMacBook Pro(2016年モデル)
IDEAndroid Studio 4.0.1
Android SDKminSdkVersion 21
targetSdkVersion 30
言語Kotlin 1.3.72
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <com.sosotata.minesweeper.ui.widgets.SquareTileGameView
        android:id="@+id/squareView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.sosotata.minesweeper.ui.widgets.TouchFeedbackView
        android:id="@+id/touchAnimationView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
package com.sosotata.minesweeper.ui.main

import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.sosotata.minesweeper.R
import kotlinx.android.synthetic.main.game_fragment.*

class GameFragment : Fragment(), View.OnTouchListener {
    companion object {
        fun newInstance() = GameFragment()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.game_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        squareView.setOnTouchListener(this)
    }

    override fun onTouch(vw: View?, e: MotionEvent?): Boolean {
        if (e?.action == MotionEvent.ACTION_UP) {
            // タッチのフィードバック実行!!
            touchAnimationView.touchFeedback(e.x, e.y)
        }
        return squareView.onTouchEvent(e)
    }
}
package com.sosotata.minesweeper.ui.widgets

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.PointF
import android.util.AttributeSet
import android.view.View
import android.view.animation.AlphaAnimation
import com.sosotata.minesweeper.R

/**
 * タッチアニメーションビュー
 */
class TouchFeedbackView: View {
    /** タッチアニメーション用画像 **/
    private val mTouchImage: Bitmap

    /** タッチ座標 **/
    private val mTouchPos = PointF()

    /**
     * コンストラクタ
     */
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        mTouchImage = BitmapFactory.decodeResource(context.resources, R.drawable.touch_image)
    }

    /**
     * [android.view.View.onDraw]
     */
    override fun onDraw(canvas: Canvas) {
        if (mTouchPos.x >= 0 && mTouchPos.y >= 0) {
            canvas.drawBitmap(
                mTouchImage,
                mTouchPos.x - (mTouchImage.width / 2f),
                mTouchPos.y - (mTouchImage.height / 2f),
                null
            )
        }
        super.onDraw(canvas)
    }

    /**
     * タッチフィードバック実行
     */
    fun touchFeedback(x: Float, y: Float) {
        mTouchPos.x = x
        mTouchPos.y = y

        val fadeOut = AlphaAnimation(1.0f, 0.0f)
        fadeOut.duration = 300
        fadeOut.fillAfter = true
        startAnimation(fadeOut)
    }
}

コメント

タイトルとURLをコピーしました