Jetpack Compose实现首页splash动画

本文讨论下如何在Jetpack Compose中实现一个首页的splash动画效果,涉及的知识点包括:1 google提供的androidx.core:core-splashscreen;2 VectorDrawable;3 ObjectAnimator属性动画。其中第二点VectorDrawable相关知识点可以参考这2篇文章:Android矢量图(一)--VectorDrawable基础Android矢量图(二)--VectorDrawable所有属性全解析

1 google splash sdk

app/build.gradle.kts添加依赖:
implementation("androidx.core:core-splashscreen:1.0.1")

然后在res/values/themes.xml里写2个style,splash动画主要是通过style来实现:

// res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.Starkdemo" parent="android:Theme.Material.Light.NoActionBar" />

    <style name="Theme.App.Starting" parent="Theme.SplashScreen">
        <item name="windowSplashScreenBackground">@color/black</item>
        <item name="postSplashScreenTheme">@style/Theme.Starkdemo</item>
        <item name="android:windowSplashScreenAnimatedIcon">@drawable/animated_logo</item>
    </style>
</resources>

可以看到,themes.xml里定义两个style,第一个style是splash动画结束后的采用的默认的style,第二个style是用来展示splash动画。第二个style里我们定义了三个属性,第一个属性是动画的背景颜色,这里是黑色,第二个属性是动画结束后的采用的style,第三个属性表示具体的动画,动画值是res/drawable/animated_logo.xml,这里xml在下面的第二小节里具体介绍。

最后AndroidManifest.xml里的application和activity标签下的android:theme属性都设置成这个Theme.App.Starting。

2 编辑 res/drawable/animated_logo.xml

animated_logo.xml里需要指定两个东西,第一个是动画的目标,这里是VectorDrawabele,通过android:drawable属性指定;第二个是动画类型,这里是属性动画,通过<target/>元素指定,源码如下:

// res/drawable/animated_logo.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" 
    android:drawable="@drawable/baseline_diamond_24">

    <target
        android:animation="@animator/logo_animator"
        android:name="logoGroup"/>

</animated-vector>

baseline_diamond_24.xml和logo_animator.xml源码如下:

// res/drawable/baseline_diamond_24.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="50dp"
    android:height="50dp"
    android:viewportWidth="24"
    android:viewportHeight="24">

    <group
        android:name="logoGroup"
        android:pivotX="12"
        android:pivotY="12"
        >

        <path
            android:fillColor="@android:color/holo_green_dark"
            android:pathData="M12.16,3l-0.32,0l-2.63,5.25l5.58,0z"
            android:strokeColor="@android:color/holo_red_dark" />

        <path
            android:fillColor="@android:color/holo_green_dark"
            android:pathData="M16.46,8.25l5.16,0l-2.62,-5.25l-5.16,0z"
            android:strokeColor="@android:color/holo_red_dark" />

        <path
            android:fillColor="@android:color/holo_green_dark"
            android:pathData="M21.38,9.75l-8.63,0l0,10.35z"
            android:strokeColor="@android:color/holo_red_dark" />

        <path
            android:fillColor="@android:color/holo_green_dark"
            android:pathData="M11.25,20.1l0,-10.35l-8.63,0z"
            android:strokeColor="@android:color/holo_red_dark" />

        <path
            android:fillColor="@android:color/holo_green_dark"
            android:pathData="M7.54,8.25l2.62,-5.25l-5.16,0l-2.62,5.25z"
            android:strokeColor="@android:color/holo_red_dark" />
    </group>


</vector>


// res/animator/logo_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@anim/overshoot_interpolator">

    <propertyValuesHolder
        android:propertyName="rotation"
        android:valueFrom="0.0"
        android:valueTo="360.0"
        android:valueType="floatType" />

    <propertyValuesHolder
        android:propertyName="scaleX"
        android:valueFrom="0.0"
        android:valueTo="0.4"
        android:valueType="floatType" />

    <propertyValuesHolder
        android:propertyName="scaleY"
        android:valueFrom="0.0"
        android:valueTo="0.4"
        android:valueType="floatType" />
</objectAnimator>

// res/anim/overshoot_interpolator.xml
<?xml version="1.0" encoding="utf-8"?>
<overshootInterpolator xmlns:android="http://schemas.android.com/apk/res/android">
</overshootInterpolator>

VectorDrawable这里就不介绍了,可以参考之前提到的两篇文章。这里对res/animator/logo_animator.xml坐下简单说明,对三个属性rotation、scaleX、scaleY做了持续1000ms的动画,起始属性值和终止属性值分别通过android:valueFrom和android:valueTo指定。

3 installSplashScreen

最后,还需要写点代码,在MainActivity的onCreate里,需要调用installSplashScreen(),而且时机需要在setContent之前。installSplashScreen()函数返回的是一个SplashScreen对象,这个对象提供了setKeepOnScreenCondition和setOnExitAnimationListener等接口。

class MainActivity : ComponentActivity() {
    private val TAG = "MainActivity"
    private val mLoginViewModel by viewModels<LoginViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        installSplashScreen().apply {
            setKeepOnScreenCondition {
                !mLoginViewModel.login.value
            }
            setOnExitAnimationListener { screen ->
                val zoomX = ObjectAnimator.ofFloat(screen.iconView, View.SCALE_X, 0.4f, 0.0f).apply {
                    interpolator = OvershootInterpolator()
                    duration = 500
                    doOnEnd { screen.remove(); StarkLog.w(TAG, "testsplash exit anim end x") }
                }
                val zoomY = ObjectAnimator.ofFloat(screen.iconView, View.SCALE_Y, 0.4f, 0.0f).apply {
                    interpolator = OvershootInterpolator()
                    duration = 500
                    doOnEnd { screen.remove(); StarkLog.w(TAG, "testsplash exit anim end y") }
                }
                zoomX.start()
                zoomY.start()
            }
        }

        setContent {
            StarkdemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting("Android")
                }
            }
        }
    }

    @Composable
    fun Greeting(name: String, modifier: Modifier = Modifier) {
        Text(
            text = "Hello $name!",
            modifier = modifier
        )
    }
}
// LoginViewModel.kt
package com.zzh.eden.stark.demo

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

class LoginViewModel() : ViewModel() {

    private val _login: MutableStateFlow<Boolean> = MutableStateFlow(false)
    val login: StateFlow<Boolean> = _login.asStateFlow()

    fun onLoginChanged(l: Boolean) {
        _login.update { l }
    }

    init {
        // test
        viewModelScope.launch {
            delay(3000)
            onLoginChanged(true)
            // _login.value = true
        }
    }
}


最终效果:


demo.gif






参考:

How to Build an Animated Splash Screen on Android - The Full Guide

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,809评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,189评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,290评论 0 359
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,399评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,425评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,116评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,710评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,629评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,155评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,261评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,399评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,068评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,758评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,252评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,381评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,747评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,402评论 2 358

推荐阅读更多精彩内容