2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Androidでナイトライダー風アニメーション

Last updated at Posted at 2021-11-17

できたもの

URL読み取りアプリのローディング表示に使ってみました。

コード

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View
import java.util.*
import kotlin.math.max

class KnightRiderView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    enum class Pattern {
        OneWay,
        TwoWay
    }

    companion object {
        private val PATTERN = Pattern.TwoWay
        private const val COLOR = Color.GREEN
        private const val BLOCK_NUM = 100         // ブロック数
        private const val MOVE_SPEED = 1f         // 移動速度(左端から右端に何秒かけて移動するか)
        private const val ATTENUATION_TIME = 0.5f // 減衰時間(光った後に何秒かけて消灯するか)
    }

    private val paint = Paint()
    private val blocks = Array(BLOCK_NUM) { 0f }
    private var brightIndex = 0             // 現在光っているブロックのインデックス
    private var moveDir = 1                 // 光の移動方向(1で右、-1で左)
    private var lastDrawTime : Long? = null // 前回描画時間
    private val timer = Timer()

    init {
        paint.color = COLOR
        paint.style = Paint.Style.FILL

        // 指定秒ごとに光を移動させる
        timer.schedule(object : TimerTask() {
            override fun run() {
                move()
            }
        }, 0L, (1000 * MOVE_SPEED / BLOCK_NUM).toLong())
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        timer.cancel()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        val deltaTime = if (lastDrawTime != null) (System.currentTimeMillis() - lastDrawTime!!).toFloat() / 1000f else 0.01f

        for (i in blocks.indices) {

            // 指定秒で光を減衰させる
            blocks[i] = max(0f, blocks[i] - (deltaTime / ATTENUATION_TIME))

            // ブロックを描画する
            paint.alpha = (blocks[i] * 255).toInt()
            canvas?.drawRect(getBlockRect(i), paint)
        }

        lastDrawTime = System.currentTimeMillis()
        invalidate()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val width = MeasureSpec.getSize(widthMeasureSpec)
        when (MeasureSpec.getMode(heightMeasureSpec)) {
            MeasureSpec.EXACTLY -> setMeasuredDimension(width, measuredHeight)
            else -> setMeasuredDimension(width, width / BLOCK_NUM)
        }
    }

    private fun getBlockRect(index: Int): RectF {
        val blockWidth = width.toFloat() / BLOCK_NUM
        return RectF(index * blockWidth, 0f, (index + 1) * blockWidth, height.toFloat())
    }

    private fun move() {
        when (PATTERN) {
            Pattern.OneWay -> moveOneWay()
            Pattern.TwoWay -> moveTwoWay()
        }
    }

    private fun moveOneWay() {
        // 端まで到達したら折り返す
        if ((moveDir == 1 && brightIndex == blocks.size - 1) || (moveDir == -1 && brightIndex == 0))
            moveDir *= -1

        // 光をひとつ横のブロックに移動させる
        brightIndex += moveDir
        blocks[brightIndex] = 1f
    }

    private fun moveTwoWay() {
        // 左端⇔中心に到達したら折り返す
        if ((moveDir == 1 && brightIndex == (blocks.size / 2) - 1) || (moveDir == -1 && brightIndex == 0))
            moveDir *= -1

        // 光をひとつ横のブロックに移動させる
        brightIndex += moveDir

        // 中心から対称の位置にある2つのブロックを光らせる
        blocks[brightIndex] = 1f
        blocks[(blocks.size - 1) - brightIndex] = 1f
    }
}

レイアウトxmlにこのビューを置けばそのままそれだけで使えます。
layout_heightはwrap_contentにすると1ブロックが正方形になるように大きさ設定されます。dp指定で任意の高さに設定してもOKです。

冒頭のGifはPatternでTwoWayを指定した時のアニメーションで、OneWayを選択すると1つの光が左右に動くアニメーションになります。

BROCK_NUMを変更して粗さを変えたり、MOVE_SPEEDで動きの速度を変更できます。
ATTENUATION_TIMEを長くすると、光の尾が長くなります。

おわりに

Canvasを使えば自由にアニメーションが使えて楽しいですね!
是非ナイトライダー風アニメーション使ってみてください。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?