0Pricing
Android Academy · レッスン

animate*AsStateの基本

単一の値を滑らかにアニメーションさせます。

「animate*AsStateの基本」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。

単一の値をアニメーションさせる

Composeでアニメーションを作成する最も簡単な方法は、animate*AsStateファミリーを使うことです。目標値を渡すと、現在の値から目標値まで滑らかにアニメーションする State が返されます。目標値が変わるたびにアニメーションが実行されます。

一般的な型ごとにバリエーションがあります。animateFloatAsState、animateDpAsState、animateColorAsState、animateOffsetAsStateなどです。

animateFloatAsState

animateFloatAsStateは単一の Float をアニメーションさせます。よくある用途は不透明度(アルファ)です。visible が切り替わると、返されたアルファ値が0fと1fの間を滑らかに移動します。

byキーワードにも注目してください。これを使うと、アニメーションする State<Float> を通常の Float として直接読み取れます。

@Composable
fun FadingLabel(visible: Boolean) {
    val alpha by animateFloatAsState(
        targetValue = if (visible) 1f else 0f,
        label = "alpha"
    )
    Text(
        "Hello",
        modifier = Modifier.graphicsLayer { this.alpha = alpha }
    )
}

animateDpAsState

UIのサイズやオフセットには Dp を使うため、パディング、サイズ、位置をアニメーションさせるには animateDpAsState を使用します。

ここでは、切り替えに応じてボックスが80dpから160dpに拡大します。アニメーションする Dp を size() に渡しているため、ボックスのサイズが滑らかに変化します。

@Composable
fun GrowingBox(big: Boolean) {
    val size by animateDpAsState(
        targetValue = if (big) 160.dp else 80.dp,
        label = "size"
    )
    Box(
        Modifier
            .size(size)
            .background(Color.Blue)
    )
}

animateColorAsState

色をアニメーションさせるには animateColorAsState を使用します。色空間内で補間するため、途中で不自然な色にちらつくことなく、滑らかで自然なトランジションになります。

ここでは、ブール値に応じてステータスドットが赤と緑の間でアニメーションします。

@Composable
fun StatusDot(online: Boolean) {
    val color by animateColorAsState(
        targetValue = if (online) Color(0xFF4CAF50) else Color(0xFFF44336),
        label = "status-color"
    )
    Box(
        Modifier
            .size(16.dp)
            .clip(CircleShape)
            .background(color)
    )
}

animationSpecでカスタマイズする

これらのヘルパーは、デフォルトで穏やかなスプリングを使用します。タイミングを制御するには animationSpec を渡します。一定の時間とイージングカーブを使用する場合は tween を使います。

このパネルは、滑らかに加速してから減速するカーブで400ミリ秒かけてスライドします。

@Composable
fun SlidingPanel(open: Boolean) {
    val offset by animateDpAsState(
        targetValue = if (open) 0.dp else 250.dp,
        animationSpec = tween(
            durationMillis = 400,
            easing = FastOutSlowInEasing
        ),
        label = "offset"
    )
    Box(Modifier.offset(x = offset)) { /* content */ }
}

イージングカーブ

イージングは、アニメーションの加速と減速の仕方を決めます。Composeには次のイージングが用意されています。

  • LinearEasing - 一定速度(機械的な印象)。
  • FastOutSlowInEasing - Materialのデフォルト。素早く始まり、穏やかに終わります。
  • LinearOutSlowInEasing - 画面に入ってくる要素向け。
  • FastOutLinearInEasing - 画面から出ていく要素向け。

適切なイージングを選ぶと、動きに意図が感じられ、機械的な印象を避けられます。

val progress by animateFloatAsState(
    targetValue = if (done) 1f else 0f,
    animationSpec = tween(500, easing = LinearOutSlowInEasing),
    label = "progress"
)

スプリングの仕様

固定された時間の代わりに、物理的で自然な動きになるスプリングを使用できます。スプリングは、dampingRatio(弾みやすさ)と stiffness(速さ)で定義されます。

スプリングは、アニメーションの途中で目標値が変わっても滑らかに適応するため便利です。

val scale by animateFloatAsState(
    targetValue = if (active) 1.2f else 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioMediumBouncy,
        stiffness = Spring.StiffnessLow
    ),
    label = "scale"
)

完了への反応

アニメーションが完了したときに、値が目標値に到達したらコールバックを一度実行するなど、何らかの処理が必要になる場合があります。animate*AsState には、そのための finishedListener が用意されています。

val alpha by animateFloatAsState(
    targetValue = if (gone) 0f else 1f,
    label = "alpha",
    finishedListener = { finalValue ->
        if (finalValue == 0f) onFullyHidden()
    }
)

複数のプロパティをアニメーションさせる

複数の animate*AsState 呼び出しを組み合わせると、複数のプロパティを同時にアニメーションさせられます。それぞれが独自の値を追跡し、組み合わせることで、より豊かな効果を作り出せます。

ここでは、選択されたチップが拡大し、色も変化します。

@Composable
fun SelectableChip(selected: Boolean, text: String) {
    val scale by animateFloatAsState(if (selected) 1.1f else 1f, label = "s")
    val bg by animateColorAsState(
        if (selected) Color(0xFF3F51B5) else Color.LightGray,
        label = "bg"
    )
    Box(
        Modifier
            .graphicsLayer { scaleX = scale; scaleY = scale }
            .clip(RoundedCornerShape(16.dp))
            .background(bg)
            .padding(horizontal = 12.dp, vertical = 6.dp)
    ) { Text(text) }
}

純粋なKotlinでスプリングを実装する

スプリングが「どのように動くのか」を直感的に理解するために、小さな純粋なKotlinのシミュレーションを見てみましょう。これは、減衰を加えながら各ステップで値を目標に近づけます。Composeのスプリングが多くのフレームを通して目標値に収束する仕組みによく似ています。

fun main() {
    var value = 0f
    val target = 100f
    var velocity = 0f
    val stiffness = 0.1f
    val damping = 0.6f
    repeat(6) { step ->
        val force = (target - value) * stiffness
        velocity = (velocity + force) * damping
        value += velocity
        println("step $step -> ${"%.1f".format(value)}")
    }
}

IntとOffsetをアニメーションさせる

このファミリーは浮動小数点数や色だけにとどまりません。バッジのカウント目標値のような整数値には animateIntAsState を使用します。2D位置を1回の呼び出しでアニメーションさせるには、animateOffsetAsState または animateIntOffsetAsState を使用します。

ここでは、IntOffset をアニメーションさせて、チップを斜めにスライドさせます。

@Composable
fun SlidingChip(moved: Boolean) {
    val pos by animateIntOffsetAsState(
        targetValue = if (moved) IntOffset(120, 60) else IntOffset.Zero,
        label = "pos"
    )
    Box(
        Modifier
            .offset { pos }
            .size(40.dp)
            .background(Color.Magenta)
    )
}

確認問題

ブール値が変化したときに、Dpで測定したボックスの幅を滑らかにアニメーションさせたいとします。最適なAPIはどれですか?

まとめ:animate*AsState

animate*AsStateファミリーは、単一の値をアニメーションさせるときの基本的な選択肢です。

  • 型に対応したバリエーションを選びます。animateFloatAsState、animateDpAsState、animateColorAsStateなどがあります。
  • targetValue を設定すると、値が変わるたびに結果がその値へ向かってアニメーションします。
  • tweenとイージング、または spring を使い、animationSpec で動きを調整します。
  • 複数を組み合わせて、複数のプロパティを一緒にアニメーションさせます。
  • アニメーションの完了に反応するには finishedListener を使用します。

次は、AnimatedVisibility とトランジションを使って、コンテンツの表示と非表示をアニメーションさせます。

よくある質問

「animate*AsStateの基本」レッスンは無料ですか?

はい。「animate*AsStateの基本」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。

「animate*AsStateの基本」で何を学びますか?

単一の値を滑らかにアニメーションさせます。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Android Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「animate*AsStateの基本」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAndroid Academyレッスンでコードを書いて実行できますか?

はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. アニメーションが重要な理由
  2. animate*AsStateの基本
  3. AnimatedVisibilityとトランジション
  4. ジェスチャーとスプリングアニメーション
← Android Academyに戻る