【Android】ニコニコのコメント風アニメーションを実装する【Animation】

# はじめに

デザイナー「ここボタン押したくなるようなアニメーションさせてください！」

ぼく「わかりました！」

ってことで、ニコニコのコメント風アニメーションを実装することにした。
思ったより簡単にできたので、実装方法をご紹介。

# 完成形

こんな感じのものを作るよ。
前提として、ルートのViewGroupはConstraintLayoutを使ってる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474683/1f90346a-ea25-8de9-fdfe-2d50ddfa7af0.gif" width="400"/>

# やること

1. 画面サイズ取得
2. TextViewを生成する
3. Y位置をランダムで設定する
4. 生成したTextViewに制約を設定する
5. 右から左に移動する

## 1.画面サイズ取得

3の**「Y位置をランダムで設定」** と5の**「右から左に移動」** をするのに、画面サイズが必要になる。
幅と高さ別々で取得してもいいんだけど、画面サイズのクラス作っちゃった方が僕は好み。

```kotlin
data class WindowSize(
    val width: Int,
    val height: Int
)

private fun getWindowSize(): WindowSize {
    val outMetrics = DisplayMetrics()
    display?.getRealMetrics(outMetrics)
    return WindowSize(
        width = outMetrics.widthPixels,
        height = outMetrics.heightPixels
    )
}
```

## 2.TextViewを生成

まずはコメントを表示させる画面に対してTextViewをインフレートさせる。

```kotlin
val textView = TextView(baseContext).apply {
    id = View.generateViewId()
    text = "Hello World!"
    layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
}
textView.setTextColor(Color.BLACK)
constraintLayout.addView(textView)
```

idはあとで使うから設定してるよ。

## 3.Y位置をランダムで設定

コメントが表示される高さの範囲は`画面の高さ - TextViewの高さ` で求められる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474683/cfe26527-d1d2-3f38-cb19-9e8a5750ff27.png" width="300"/>

これより下げちゃうと見切れちゃうからね。

```kotlin
val random = Random(System.currentTimeMillis())
val transitionY = random.nextInt(windowSize.height - textView.layoutParams.height)
textView.translationY = transitionY.toFloat()
```

乱数のシードはなんでもいいけど、今回は`System.currentTimeMillis()`を使うよ。

## 4.生成したTextViewに制約を設定する

設定する位置は画面の右側。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474683/dcdf90ae-828f-2861-b216-8afaa0ea825e.png" width="300">

```kotlin
ConstraintSet().also {
    it.clone(constraintLayout)
    it.connect(
        textView.id,
        ConstraintSet.LEFT,
        ConstraintSet.PARENT_ID,
        ConstraintSet.RIGHT
    )
    it.applyTo(constraintLayout)
}
```

多分「何やってんのコレ？」って感じだろうけど、ConstraintSetについて調べればわかると思う。
本筋と外れるからここでは解説しないけど、@soranakk さんの[Kotlin(Java)コード上でConstraintLayoutの制約を編集する](https://qiita.com/soranakk/items/ed7f11dfa9b68fc9d4ca)って記事がわかりやすく解説してくれてるよ。
あとは[公式ドキュメント](https://developer.android.com/reference/androidx/constraintlayout/widget/ConstraintSet)読んだら完璧。

## 5.右から左に移動する

これは多分色々やり方あるんだろうけど、今回はObjectAnimatorを使うよ。

移動する範囲は `画面幅 + TextViewの幅`で求められるので、doOnLayoutを使ってレイアウトが決定したタイミングでアニメーションの設定をしてる。
これもうちょいいい感じにできそうだけどね。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474683/aedbd972-73b3-20d1-c035-2c09441f7084.png" width="300">

```kotlin
textView.doOnLayout {
    val textViewWidth = it.width.toFloat()
    val objectAnimator = ObjectAnimator.ofFloat(
        textView,
        "translationX",
        0f,
        -(windowSize.width + textViewWidth)
    ).apply {
        interpolator = LinearInterpolator()
        duration = 3000L
    }.also {
        it.doOnEnd {
            // アニメーションが終わったViewを削除
            constraintLayout.removeView(textView)
        }
    }
    objectAnimator.start()
}
```

interpolatorはデフォルトのが気に入らなかったから、等速で移動するLinearInterpolatorに変更。

アニメーションが終わったら`doOnEnd`でremoveするのを忘れずに。
やらないとボタン押せば押すほどViewが溜まって、無限にアプリが重くなっていくよ。

ここまでやったらあとはボタンにクリックイベントつけて完成〜。

# 最後に

こういうの、意外と頑張ればなんとかなるものね。
また凝ったアニメーション挑戦したいなと思った。

一応[ここ](https://github.com/KKusumi/NicoVideoCommentAnimationSample)でサンプル公開してるから、「わかんねえよ！！」ってなったら見てね。

おわり！