TL;DR

  1. 导入implementation 'androidx.core:core-splashscreen:1.0.1'
  2. 继承主题 Theme.ScreenSplash,继承的主题可以设置前景和背景以及时间(最大500),类似icon
  3. 使用主题

在国产大厂的APP上,每一个Feature,每一个模块,每一个按钮,甚至每一个不可交互的地方,它都有出生的意义:广告位。

闪屏页的战略地位不言而喻😄

在 Android 12(SDK 31)之前,应用的启动画面(Splash Screen)一直是个令人头疼的问题。开发者们不得不绞尽脑汁,通过自定义 Activity 主题、设置 windowBackground 或者创建一个独立的 Splash Activity 来模拟启动效果。

不仅需要作为天然的广告位,这个一闪而过页面承载太多东西了。你的所有需要注册的内容,所有内容的完整性检查,服务器判断,DNS解析。当然还有广告的加载以及用户的手有没有抖动…

虽然目前看来体验不尽人意,适配寥寥无几,不过当初Android12似乎真的想改变这些(当然不止这些,更多的可能是体验统一的问题),所以带来一个原生,更高效的解决方案。抛开这些开发决定不了的内容,下面我简单记录一下自己适配这个SplashScreen,当然这也是我第一次使用这个特性。

核心概念与 Theme.SplashScreen 属性详解

Android 12 的启动画面不再是应用自身绘制的 View,而是由系统根据主题配置生成并管理。你的启动 Activity 会首先应用一个继承自 Theme.SplashScreen 的主题,系统会根据该主题的属性来渲染启动画面,并在应用准备就绪后平滑过渡到应用真正的界面。下面是我在网上找到的VerseAPP的动画闪屏页(你也可以下载查看teelgram的动画效果):

以下是 Theme.SplashScreen 主题及其关键属性的详细解析:

1. 父主题:Theme.SplashScreen

  • 作用: 这是 Android 12+ 系统提供的启动画面基础主题。你的自定义启动主题必须继承这个主题,才能享受到系统级别的启动画面管理和动画效果。

2. 背景属性:windowSplashScreenBackground

类型: Drawable 引用(@drawable/@color/

作用: 定义启动画面的背景。这是替换传统“白屏”的关键。

自定义方案:

  • 纯色: 最简单也是最高效的方式,直接引用一个颜色资源,如 <item name="windowSplashScreenBackground">@color/your_brand_color</item>
  • 渐变色: 创建一个 shape Drawable,并在其中定义 gradient 标签,实现平滑的颜色过渡。
  • 图片作为背景(慎用,推荐 LayerList): 虽然可以直接引用一张图片 (@drawable/your_image),但通常不推荐直接将一张大图作为背景,因为它可能导致屏幕适配问题、文件尺寸增大和内存开销。
  • LayerList 组合背景

3. 图标属性:windowSplashScreenAnimatedIcon

  • 类型: Drawable 引用(@drawable/@mipmap/
  • 作用: 定义在启动画面中心显示的图标或动画。
  • 可选动画类型:
    • Animated Vector Drawable (AVD)
      • 格式: XML (.xml),定义了矢量图的动画。
      • 优点: 官方推荐,文件小巧,可缩放不失真,性能优秀,可以实现复杂的矢量动画。
      • 创建: 复杂动画通常需要手动编写 XML 或借助工具。
    • Animation Drawable:
      • 格式: XML (.xml),引用一系列帧图片。
      • 优点: 简单易懂,适合简单的帧动画。
      • 缺点: 每一帧都是一张图片,文件体积大,内存占用高,缩放可能失真。适用于帧数非常少且简单的动画。
    • 自适应图标 (Adaptive Icon):
      • 格式: 通常是矢量图,由前景层 (@mipmap/ic_launcher_foreground) 和背景层 (@mipmap/ic_launcher_background) 组成。
      • 优点: 系统原生支持,自动适应不同形状的图标蒙版,无需额外动画即可平滑缩放。
      • 最常用方案: 如果没有自定义动画需求,直接使用你的自适应图标前景层即可,例如:<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher_foreground</item>

4. 动画持续时间:windowSplashScreenAnimationDuration

  • 类型: 整数(毫秒)
  • 作用: 定义 windowSplashScreenAnimatedIcon 中图标动画的持续时间。系统会在这个时间结束后开始淡出启动画面。
  • 注意: 你的动画设计应尽量在这个持续时间内完成,以避免动画被截断或过早结束。建议值为 200ms 到 1000ms 之间,保持快速且流畅。

5. 品牌 Logo:windowSplashScreenBrandDrawable (可选)

  • 类型: Drawable 引用
  • 作用: 在启动画面底部显示一个可选的品牌 Logo。
  • 位置: 这个 Logo 会固定显示在启动画面的底部,不会随图标一起动画。
  • 用处: 适合展示公司或产品的额外品牌标识。

6. 核心属性:postSplashScreenTheme

  • 类型: Style 引用(@style/
  • 作用: 这是最重要的属性!它指定了启动画面结束后,Activity 应该切换到哪个主题来渲染你的应用界面。
  • 重要性: 如果没有正确设置这个属性,你的 Activity 在启动画面消失后,可能会显示错误的样式,甚至出现界面空白或闪烁。务必将其指向你应用正常运行时所使用的主要主题。

二、应用开发实践:从零开始配置

让我们通过一个完整的示例,一步步将 Android 12 的启动画面集成到你的应用中。

1: 确保你的项目兼容 Android 12 (SDK 31+)

首先,在你的 build.gradle (Module: app) 文件中,确保 compileSdktargetSdk 至少是 31:

android {
    compileSdk 31 // 或更高版本
    defaultConfig {
        targetSdk 31 // 或更高版本
        // ...
    }
    // ...
}

2. 添加 Splash Screen 库依赖

dependencies {
    // ... 其他依赖
    implementation 'androidx.core:core-splashscreen:1.0.1' 
}

3. 定义你的应用主题

res/values/themes.xml (和 res/values-night/themes.xml,用于深色模式) 中定义应用主主题。这将是 postSplashScreenTheme 指向的主题。

<resources>
    <style name="Theme.MyAwesomeApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="colorPrimary">#6200EE</item>
        <item name="colorPrimaryVariant">#3700B3</item>
        <item name="colorOnPrimary">#FFFFFF</item>
        <item name="colorSecondary">#03DAC6</item>
        <item name="colorSecondaryVariant">#018786</item>
        <item name="colorOnSecondary">#000000</item>
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
        <item name="android:navigationBarColor">@color/black</item>
        <item name="android:windowBackground">@color/white</item>
        </style>
</resources>

<resources>
    <style name="Theme.MyAwesomeApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="colorPrimary">#BB86FC</item>
        <item name="colorPrimaryVariant">#3700B3</item>
        <item name="colorOnPrimary">#000000</item>
        <item name="colorSecondary">#03DAC6</item>
        <item name="colorSecondaryVariant">#03DAC6</item>
        <item name="colorOnSecondary">#000000</item>
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
        <item name="android:navigationBarColor">@color/black</item>
        <item name="android:windowBackground">@color/dark_gray</item>
    </style>
</resources>

4. 准备启动画面背景(渐变色和Logo)

我们创建一个 LayerList 来实现一个包含渐变背景和底部品牌Logo的启动画面。(实际上不推荐 用渐变色,我更推荐telegram或者X的方案,纯色背景+AVD/静态LOGO)

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)">
    <item>
        <shape android:shape="rectangle">
            <gradient
                android:angle="270"
                android:startColor="#6200EE"
                android:endColor="#03DAC6"
                android:type="linear" />
        </shape>
    </item>

    <item android:bottom="32dp"> <bitmap
            android:src="@drawable/ic_my_company_logo"
            android:gravity="bottom|center_horizontal" />
    </item>
</layer-list>

注意:

  • @drawable/ic_my_company_logo 应该是你的 Logo 图片(Vector Drawable 或 Bitmap)。
  • android:bottom 可以调整 Logo 的位置。
  • android:gravity="bottom|center_horizontal" 将 Logo 放置在底部中央。

5. 准备启动画面动画图标(AVD 示例)

假设你已经有一个名为 ic_animated_logo.xml 的 Animated Vector Drawable。

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    android:drawable="@drawable/ic_static_logo"> <target android:name="group_name_in_static_logo"> <propertyValuesHolder
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType"
            android:duration="1000"
            android:interpolator="@android:interpolator/fast_out_slow_in" />
    </target>
</animated-vector>

如果不想使用自定义动画,直接使用自适应图标前景部分:@mipmap/ic_launcher_foreground

6. 定义启动画面主题

现在,在 res/values/themes.xml (和 res/values-night/themes.xml) 中定义你的启动主题,并引用我们准备好的 Drawable。

<resources>
    <style name="Theme.MyAwesomeApp.SplashScreen" parent="Theme.SplashScreen">
        <item name="windowSplashScreenBackground">@drawable/splash_layer_bg</item>

        <item name="windowSplashScreenAnimatedIcon">@drawable/ic_animated_logo</item>
        <item name="windowSplashScreenAnimationDuration">1000</item> <item name="postSplashScreenTheme">@style/Theme.MyAwesomeApp</item>
    </style>
</resources>

<resources>
    <style name="Theme.MyAwesomeApp.SplashScreen" parent="Theme.SplashScreen">
        <item name="windowSplashScreenBackground">@color/dark_splash_bg_color</item>
        <item name="windowSplashScreenAnimatedIcon">@drawable/ic_animated_logo_dark</item>
        <item name="windowSplashScreenAnimationDuration">1000</item>
        <item name="postSplashScreenTheme">@style/Theme.MyAwesomeApp</item>
    </style>
</resources>

7. 在 AndroidManifest.xml 中应用主题

AndroidManifest.xml 中,将 Theme.MyAwesomeApp.SplashScreen 主题应用到启动 Activity(通常是 MainActivity,也有可能是SplashActivity之类的)。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    xmlns:tools="[http://schemas.android.com/tools](http://schemas.android.com/tools)">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyAwesomeApp"> <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/Theme.MyAwesomeApp.SplashScreen"> <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".OtherActivity"
            android:theme="@style/Theme.MyAwesomeApp"/>

    </application>
</manifest>

8. 在 MainActivity 中安装启动画面

最后,在启动 Activity 的 onCreate() 方法中调用 installSplashScreen()这是启用系统启动画面 API 的核心步骤。

// MainActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

    private var isContentReady = false // 用于控制启动画面何时消失

    override fun onCreate(savedInstanceState: Bundle?) {
        // 1. 调用 installSplashScreen() 必须在 super.onCreate() 之前
        val splashScreen = installSplashScreen()

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 2. 延迟启动画面消失,直到内容准备就绪
        splashScreen.setKeepOnScreenCondition {
            !isContentReady // 当 isContentReady 为 false 时,启动画面会一直显示
        }

        // 3. 模拟数据加载或应用初始化
        loadAppContent()
    }

    private fun loadAppContent() {
        lifecycleScope.launch(Dispatchers.IO) {
            // 模拟耗时的初始化操作,例如网络请求、数据库加载等
            delay(3000) // 模拟 3 秒的加载时间

            // 数据加载完成
            isContentReady = true

            withContext(Dispatchers.Main) {
                // 在这里可以执行数据加载完成后的 UI 更新或跳转操作
                // 例如:navigateToHome()
            }
        }
    }
}

Java 示例(AI转换的代码,应该咩问题):

// MainActivity.java
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.splashscreen.SplashScreen;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

    private boolean isContentReady = false; // 用于控制启动画面何时消失

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 1. 调用 installSplashScreen() 必须在 super.onCreate() 之前
        SplashScreen splashScreen = SplashScreen.Companion.installSplashScreen(this);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 2. 延迟启动画面消失,直到内容准备就绪
        splashScreen.setKeepOnScreenCondition(() -> !isContentReady);

        // 3. 模拟数据加载或应用初始化
        loadAppContent();
    }

    private void loadAppContent() {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.schedule(() -> {
            // 模拟耗时的初始化操作
            isContentReady = true; // 数据加载完成

            new Handler(Looper.getMainLooper()).post(() -> {
                // 在主线程执行数据加载完成后的 UI 更新或跳转操作
            });
        }, 3, TimeUnit.SECONDS); // 模拟 3 秒的加载时间
    }
}

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注