从0到1打造一款安卓app之5-启动页处理

1,383 阅读3分钟

从0到1打造一款安卓app之5-启动页

1.启动页黑/白屏问题处理

假如应用启动很慢(虽然在开发过程中要尽量避免,并且要优化),应用未启动时,从点击桌面图标打开应用,会有一瞬间的黑/白屏出现

我们可以手动模拟这一情况

class MainApplication : BaseApplication() {

    override fun onCreate() {
        super.onCreate()
        //模拟启动慢
        Thread.sleep(4500)
    }
}

处理方式

设置<item name="android:background">@drawable/splash_background_layer_list</item>

<style name="fullscreenNoTitleTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:background">@drawable/splash_background_layer_list</item>
</style>
<activity
    android:name=".ui.SplashActivity"
    android:exported="true"
    android:theme="@style/fullscreenNoTitleTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

2.启动页背景图片拉伸变形问题处理

设置<item name="android:background">@drawable/splash_background_layer_list</item>这种处理方式虽然可以避免黑/白屏问题出现,但是一个不太好处理的是,background;默认是拉伸的,如果图片宽高比例和屏幕宽高比例差太多,背景会明显变形。

手机屏幕尺寸多种多样,但常见屏幕尺寸只有比例两种,可以做两种比例的背景图,拉伸起来就不会那么明显了。

长宽比为1.6~1.8之间的 ,800x1280px ,768x1280px,1080x1920px

全面屏流行之后,屏幕长宽比约等于2.0~2.2之间,2400x1080px

虽然比例只在这几个范围内,但是屏幕尺寸实在太多,各种各样,还是会出现拉伸的情况,可以使用layer-list处理,一张图片作为背景图填充拉伸(比例差不太多情况下,拉伸并不会太明显),一张图片作为logo前景不拉伸,layer-list里的bitmap不支持使用drawable里的xml作为资源,否则会报资源找不到。

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

    <item>
        <bitmap
            android:src="@mipmap/splash_background"
            android:antialias="true"
            android:gravity="fill"/>
    </item>
    <item>
        <bitmap
            android:src="@mipmap/ic_logo"
            android:gravity="center"
            android:antialias="true"/>
    </item>
</layer-list>

3.安卓12新增的 SplashScreen API,

官方教程

res文件夹下,新建 values-v31 并且新建themes.xml文件,v31即安卓12

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="fullscreenNoTitleTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowNoTitle">true</item>
<!--        <item name="android:background">@drawable/splash_background_layer_list</item>-->
        <item name="android:windowSplashScreenBackground">@color/white</item>
        <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_help_other</item>
        <item name="android:windowSplashScreenAnimationDuration">1000</item>
        <item name="android:windowSplashScreenIconBackgroundColor">@color/white</item>
        <item name="android:windowSplashScreenBrandingImage">@mipmap/splash_background</item>
    </style>
</resources>

让启动画面在屏幕上显示更长时间

当应用绘制第一帧后,启动画面会立即关闭。如果您需要从本地磁盘异步加载少量数据(如应用内主题设置),您可以使用 ViewTreeObserver.OnPreDrawListener 让应用暂停绘制第一帧。

// Create a new event for the activity.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Set the layout for the content view.
    setContentView(R.layout.main_activity)

    // Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
            	//如果不removeOnPreDrawListener,这里会一直回调
                // Check if the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready; start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content is not ready; suspend.
                    false
                }
            }
        }
    )
}

自定义用于关闭启动画面的动画

您可以通过 Activity.getSplashScreen 进一步自定义启动画面的动画。

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

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    splashScreen.setOnExitAnimationListener { splashScreenView ->
        // Create your custom animation.
        val slideUp = ObjectAnimator.ofFloat(
            splashScreenView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.doOnEnd { splashScreenView.remove() }

        // Run your animation.
        slideUp.start()
    }
}

在此回调开始时,启动画面上动画形式的矢量可绘制对象已经开始。根据应用启动的时长,可绘制对象可能在其动画的中间。使用 SplashScreenView.getIconAnimationStartMillis 可了解动画何时开始。您可以按如下方式计算图标动画的剩余时长:

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDurationMillis
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationDurationMillis
// Calculate the remaining duration of the animation.
val remainingDuration = (
        animationDuration - (SystemClock.uptimeMillis() - animationStart)
    ).coerceAtLeast(0L)

4.使状态栏透明并隐藏状态栏文字

BarUtils.transparentStatusBar(this)
ViewCompat.getWindowInsetsController(activitySplashBinding.root)?.hide(WindowInsetsCompat.Type.systemBars())