[Android] 020. 枠のアニメーション

Imageの枠線のアニメーションの説明がYoutubeのおすすめにたまたま出てきたので試してみました
https://www.youtube.com/watch?v=0mfCbXrYBPE&list=WL&index=2
※ 動画の一番最後のセクションで紹介されてます

扱いやすくるためテンプレートが生成したColor.ktを以下のように変更しました
虹色のブラシを作るため色を追加しています
原色だとちょっとキツイ感じがしたので調整しています
Theme.ktもAppColorに対応した修正をしてください

@Immutable
object AppColor {
    val Purple80 = Color(0xFFD0BCFF)
    val PurpleGrey80 = Color(0xFFCCC2DC)
    val Pink80 = Color(0xFFEFB8C8)

    val Purple40 = Color(0xFF6650a4)
    val PurpleGrey40 = Color(0xFF625b71)
    val Pink40 = Color(0xFF7D5260)

    val RedAA0 = Color(0xA0FF0000)
    val OrangeAA0 = Color(0xA0FFA500)
    val YellowAA0 = Color(0xA0FFFF00)
    val GreenAA0 = Color(0xA0008000)
    val CyanAA0 = Color(0xA000FFFF)
    val BlueAA0 = Color(0xA00000FF)
    val PurpleAA0 = Color(0xA0800080)

    val RainbowColors = listOf(
        RedAA0,
        OrangeAA0,
        YellowAA0,
        GreenAA0,
        CyanAA0,
        BlueAA0,
        PurpleAA0
    )
}

アニメーションを実装します
動画の説明にあったtweenのdurationMillisが1000だと早すぎな気がしたので2000にしてます

@Composable
fun CircleBorderAnimationImage(
    bitmap: Bitmap,
    modifier: Modifier = Modifier,
    drush: Brush = Brush.linearGradient(listOf(Color.Yellow, Color.Green)),
    borderWidth: Float = 12f,
    contentDescription: String? = null
) {
    val infinityTransition = rememberInfiniteTransition(label = "CircleBorderTransition")
    val rotationAnimation = infinityTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(tween(2000, easing = LinearEasing)),
        label = "CircleBorderAnimation"
    )

    Image(
        bitmap = bitmap.asImageBitmap(),
        contentDescription = contentDescription,
        contentScale = ContentScale.Crop,
        modifier = modifier
            .drawBehind {
                rotate(rotationAnimation.value) {
                    drawCircle(drush, style = Stroke(borderWidth))
                }
            }.padding(borderWidth.dp).clip(CircleShape)
    )
}

今回はAssetsに置いた画像を使うため別のところでも紹介したAssetsからイメージを読み込む実装をします

/**
 * Assetsにあるイメージファイルを読込みBitmapを返す
 */
fun Context.assetsToBitmap(fileName: String): Bitmap? {
    return try {
        with(assets.open(fileName)) {
            BitmapFactory.decodeStream(this)
        }
    } catch (e: IOException) { null }
}

使い方はこんな感じです

Column(Modifier.padding(10.dp)) {
    val context = LocalContext.current
    val bitmap: Bitmap? by remember {
        mutableStateOf(context.assetsToBitmap("xxx.png"))
    }
    val brush by remember { mutableStateOf(Brush.linearGradient(AppColor.RainbowColors)) }

    bitmap?.let {
        CircleBorderAnimationImage(it, Modifier.size(100.dp), brush)
    }
}

Android Studio Giraffe 2022.3.1 built on June 29, 2023