
官方Android 12的Splash Screen檔案地址
官方Splash Screen兼容庫,支持所有版本系統
本篇文章主要圍繞下面三個問題來介紹:
- 我們能從Android 12 SplashScreen API里面學到什么?
- 新出的SplashScreen兼容庫又是什么?能做成什么樣子?
- 小甲同學:我想看Android12 SplashScreen原始碼,可以嗎?
SplashScreen使用
首先我們需要把 compileSdk 和 targetSdk(可選) 升級到31 ,
Android12版本
(A).主題和外觀配置
<!--文章末尾我們會把包含所有示例的鏈接地址提供出來,如有需要:請翻到文章末尾-->
<!-- values-v31/themes.xml -->
<!--單一顏色填充「啟影片面」視窗背景-->
<item name="android:windowSplashScreenBackground">@color/...</item>
<!--「啟影片面」中心的圖示,
可以配置AnimationDrawable 和 AnimatedVectorDrawable型別的drawable-->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
<!--「啟影片面」中心圖示影片的持續時間,這個屬性不會對螢屏顯示的實際時間產生任何影響-->
<item name="android:windowSplashScreenAnimationDuration">1000</item>
<!--「啟影片面」中心圖示后面設定背景-->
<item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>
<!--「啟影片面」底部顯示的品牌圖示-->
<item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
(B).延長啟影片面
val content: View = findViewById(android.R.id.content)
content.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
// 模擬一些資料的初始化,再取消掛起
return if (viewModel.isReady) {
// 取消掛起,恢復頁面內容繪制
content.viewTreeObserver.removeOnPreDrawListener(this)
true
} else {
// 掛起,內容還沒有準備好
false
}
}
}
)
(C ).關閉啟畫面的影片
// 自己定制關閉的影片
splashScreen.setOnExitAnimationListener { splashScreenView ->
val slideUp = ObjectAnimator.ofFloat(
// 你們自己控制,自己隨便寫什么影片,這里我們測驗讓圖示移動
splashScreenView.iconView,
View.TRANSLATION_Y,
0f,
-splashScreenView.height.toFloat()
)
slideUp.interpolator = AnticipateInterpolator()
slideUp.duration = 200L
slideUp.doOnEnd { splashScreenView.remove() }
slideUp.start()
}
(D).遇到的問題
- android:windowSplashScreenBrandingImage 定義的圖片尺寸要求是多少?總覺得有點拉伸;
- 使用AnimationDrawable 或者 AnimatedVectorDrawable,來設定中心圖示,會出現“中心圖示”消失的情況,靜態圖示不會有這種問題出現;
- Android12父主題設定android:windowBackground被覆寫,看不到效果
問題1: 在原始碼里面也沒有看到具體的值或者比例大小,怎么辦呢?
小技巧: 使用一個超大的正方形的圖示設定進去測驗了一下,拉伸不要緊,我們要的是比例, 然后測量了一下比例為:2.5 : 1,所以設計品牌名圖示的時候,可以設定為400 * 160這樣的比例為2.5:1的圖示
問題2: 針對中心圖示會閃現消失的問題做測驗,
測驗一:靜態Icon,測驗二:動態Icon
靜態中心圖示 - 正常 ……飄過……
下面我們來測驗 動態中心圖示,為了方便測驗出效果,我們覆寫住圖示后面的背景色,方便對比,最后發現:測驗結果不太理想,效果不行
<!--AnimationDrawable寫法-->
<!--沒有真機測驗,這個寫法,效果看起來也挺奇怪的,可能是模擬器且是預覽版的問題吧-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@mipmap/ic_launcher" android:duration="600" />
<item android:drawable="@drawable/api12_logo" android:duration="200" />
<item android:drawable="@mipmap/ic_launcher" android:duration="200" />
</animation-list>

動態中心圖示,不正常
我們再來看一下官方檔案中的順滑效果

下面我們使用AnimatedVectorDrawable來制作動態圖示,
為了觀察出效果:我們打開模擬器的開發者選項,找到Animator時長縮放設定為:影片時長x10,自己看效果,此處略……
笑臉眼睛影片的矢量圖檔案 👇👇,點擊查看在線制作矢量圖影片
<!--僅測驗玩耍,感興趣的可以自己去制作一個-->
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group android:name="group">
<path
android:name="path_4"
android:pathData="M 11.99 2 C 6.47 2 2 6.48 2 12 C 2 17.52 6.47 22 11.99 22 C 17.52 22 22 17.52 22 12 C 22 6.48 17.52 2 11.99 2 Z M 12 20 C 7.58 20 4 16.42 4 12 C 4 7.58 7.58 4 12 4 C 16.42 4 20 7.58 20 12 C 20 16.42 16.42 20 12 20 Z M 12 17.5 C 14.33 17.5 16.32 16.05 17.12 14 L 15.45 14 C 14.76 15.19 13.48 16 12 16 C 10.52 16 9.25 15.19 8.55 14 L 6.88 14 C 7.68 16.05 9.67 17.5 12 17.5 Z"
android:fillColor="#FFFFFF"/>
</group>
<group android:name="group_1">
<path
android:name="path_1"
android:pathData="M 8.5 9.5 M 7 9.5 C 7 9.102 7.158 8.721 7.439 8.439 C 7.721 8.158 8.102 8 8.5 8 C 8.898 8 9.279 8.158 9.561 8.439 C 9.842 8.721 10 9.102 10 9.5 C 10 9.898 9.842 10.279 9.561 10.561 C 9.279 10.842 8.898 11 8.5 11 C 8.102 11 7.721 10.842 7.439 10.561 C 7.158 10.279 7 9.898 7 9.5"
android:fillColor="#FFFFFF"/>
<path
android:name="path_3"
android:pathData="M 8.5 9.5 M 7 9.5 C 7 9.102 7.158 8.721 7.439 8.439 C 7.721 8.158 8.102 8 8.5 8 C 8.898 8 9.279 8.158 9.561 8.439 C 9.842 8.721 10 9.102 10 9.5 C 10 9.898 9.842 10.279 9.561 10.561 C 9.279 10.842 8.898 11 8.5 11 C 8.102 11 7.721 10.842 7.439 10.561 C 7.158 10.279 7 9.898 7 9.5"
android:fillColor="#FFFFFF"/>
</group>
<group android:name="group_2">
<path
android:name="path"
android:pathData="M 15.5 9.5 M 14 9.5 C 14 9.102 14.158 8.721 14.439 8.439 C 14.721 8.158 15.102 8 15.5 8 C 15.898 8 16.279 8.158 16.561 8.439 C 16.842 8.721 17 9.102 17 9.5 C 17 9.898 16.842 10.279 16.561 10.561 C 16.279 10.842 15.898 11 15.5 11 C 15.102 11 14.721 10.842 14.439 10.561 C 14.158 10.279 14 9.898 14 9.5"
android:fillColor="#FFFFFF"/>
<path
android:name="path_2"
android:pathData="M 15.5 9.5 M 14 9.5 C 14 9.102 14.158 8.721 14.439 8.439 C 14.721 8.158 15.102 8 15.5 8 C 15.898 8 16.279 8.158 16.561 8.439 C 16.842 8.721 17 9.102 17 9.5 C 17 9.898 16.842 10.279 16.561 10.561 C 16.279 10.842 15.898 11 15.5 11 C 15.102 11 14.721 10.842 14.439 10.561 C 14.158 10.279 14 9.898 14 9.5"
android:fillColor="#FFFFFF"/>
</group>
</vector>
</aapt:attr>
<target android:name="group_1">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="translateX"
android:duration="1000"
android:valueFrom="0"
android:valueTo="7"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="group_2">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="translateX"
android:duration="1000"
android:valueFrom="0"
android:valueTo="-7"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
</animated-vector>
后來我們又用了AnimationDrawable測驗了一下慢放效果也不行,你仔細想一下:圖片輪播放效果能好嗎?
所以:AnimationDrawable不推薦,我們這里推薦使用:AnimatedVectorDrawable為矢量圖添加影片效果,
問題3: Android12父主題設定android:windowBackground被覆寫,看不到效果
不要緊,只要我們的UI設計師(美工)按照如下尺寸規范來設計,使用靜態中心圖示,一樣可以實作同樣效果:
- 中心圖示: 圖示內容區域內邊距2/3,防止元素被切
- 品牌名圖示: 設計的尺寸比例為:2.5:1
SplashScreen兼容庫
點擊查看官方Splash Screen兼容庫檔案
(A).依賴庫
點擊查看Core庫里面的最新版本
// 可在所有Android版本上使用的兼容庫
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
(B).主題和外觀配置
- 定義Activity應該使用的主題
<style name="Theme.App" parent="Theme.MaterialComponents.xxxxx.DarkActionBar">
<item name="android:windowBackground">@color/...</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">......</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
- 創建父主題給啟影片面使用
<style name="Theme.App.Starting" parent="Theme.SplashScreen.IconBackground">
<item name="android:windowBackground">@drawable/...</item>
<item name="windowSplashScreenBackground">@color/...</item>
<item name="windowSplashScreenAnimationDuration">200</item>
<item name="postSplashScreenTheme">@style/Theme.App</item>
</style>
- AndroidManifest.xml配置Activity的主題
<manifest>
<application android:theme="@style/Theme.App.Starting">
<!-- application和activity,兩個選一個配置: @style/Theme.App.Starting -->
<activity android:theme="@style/Theme.App.Starting">
...
(C ).初始化SplashScreen
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val splashScreen = installSplashScreen()
setContent { ...... }
splashScreen.setKeepVisibleCondition {
!mainViewModel.mockDataLoading()
}
splashScreen.setOnExitAnimationListener(this)
}
(D).中心圖示大小修改
<item name="splashScreenIconSize">@dimen/....</item>
(E).遇到的問題
兼容庫目前存在的問題
- 沒有android:windowSplashScreenBrandingImage這個屬性
- 配置了中心圖示,會裁剪成圓形
- 低版本系統不配置windowSplashScreenAnimatedIcon會出現默認的Icon
- Android12父主題設定android:windowBackground被覆寫,看不到效果
問題1: 是因為兼容庫的layout檔案目錄下面的splash_screen_view.xml沒有“品牌名的視圖”,大家點擊查看一下,兩個布局的xml內容就知道了:
frameworks下面的Android12的splash_screen_view.xml
core-splashscreen下面的兼容庫的splash_screen_view.xml
但是我們在Android12即values-v31的themes.xml里面依然可以配置android:windowSplashScreenBrandingImage這個屬性,因為Android12的SplashScreen是集成在frameworks里面的;
問題2: 是因為兼容庫里面使用了MaskedDrawable包裝了Icon,會裁剪成圓形,圖示內容設計要保留2/3的內邊距,否則會出現內容被裁剪掉的問題;
如何修復這個裁剪圓形問題呢?
把原始碼拷貝出來,總共就3個源代碼檔案,自己復制出來修改洗掉也可以的
或者,圖示設計準則為:內容保留內邊距為2/3,防止元素被裁剪
問題3: 寫一個透明的drawable.xml然后替換就行了,類似如下方式
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent"/>
<size android:width="0dp" android:height="0dp"/>
</shape>
問題4: Android12父主題設定android:windowBackground被覆寫,看不到效果
不要緊,只要我們的UI設計師(美工)按照如下尺寸規范來設計,使用靜態中心圖示,一樣可以實作同樣效果:
- 中心圖示: 圖示內容區域內邊距2/3,防止元素被切
- 品牌名圖示: 設計的尺寸比例為:2.5:1
(F).制作一個啟動頁
- 模仿快手App的啟動頁
只需要配置父主題的android:windowBackground

Android5.0 ~ Android11 效果
由于我們在文章上面介紹到Android12上,無法為SplashScreen設定父主題的android:windowBackground,但我們依然可以通過配置靜態中心圖示來做到一樣的效果的,請看下面的效果:

Android12 效果
如果你的UI設計師,給你矢量圖,那么你就可以讓中心圖示在Android12系統上動起來了😆另外,可以建議UI設計師:統一所有系統上,啟動頁“中心”圖示,居中展示,不然會有點怪
- 動態圖示啟動頁
如果設計成動態啟動圖示,這個需要考慮2個因素:
一、 低版本系統表現效果不一致,有些系統里面,動態圖示只在啟動頁關閉的時候才顯示(親測Android平板5.1.1系統就是這樣的);
二、如何兼容低版本系統,是先展示底部品牌名,最后只能等動態圖示顯示咯?
如果喜歡折騰的同學,可以多測驗測驗,我在使用Android10.0測驗動態圖示的時候,效果看著還可以,具體系統下限在哪?
這個看你們產品設計定位了,還有測驗妹妹是否同意你們用,效果是否符合你們的產品;
建議的做法是:
- Android 5.0 ~ Android 11.0系統,都統一使用android:windowBackground配置啟動頁背景
- Android12.0 如果UI設計師給你做了矢量圖,你可以做動態的中心圖示,不給你,使用靜態圖示也可以的,參考上面:模擬快手App啟動頁
為了在模擬器上能正常顯示出效果,我們在模擬器的開發者選項,找到Animator時
長縮放設定為:影片時長x10,放慢10倍,缺真機測驗😂😂😂😂😂

Android12 動態啟動頁圖示
原始碼分析
我們這里只分析Android12 SplashScreen,兼容庫沒有太多內容不足500行,感興趣的同學可以自己閱讀一下
我們在XXXActivity里面第一次用到了splashScreen.setOnExitAnimationListener,從這里開始往源頭開始找,在下面的方法初始化了SplashScreen
//android.app.Activity
private SplashScreen getOrCreateSplashScreen() {
synchronized (this) {
if (mSplashScreen == null) {
mSplashScreen = new SplashScreen.SplashScreenImpl(this);
}
return mSplashScreen;
}
}
我們來看SplashScreenImpl實作類
//android.app.Activity
class SplashScreenImpl implements SplashScreen {
......
//把SplashScreenImpl添加到這個單例類里面
private final SplashScreenManagerGlobal mGlobal;
public SplashScreenImpl(Context context) {
mGlobal = SplashScreenManagerGlobal.getInstance();
}
@Override
public void setOnExitAnimationListener(@NonNull SplashScreen.OnExitAnimationListener listener) {
......
mGlobal.addImpl(this); // 用于后面執行啟影片面將退出的回呼
}
......
public void setSplashScreenTheme(@StyleRes int themeId) {
......
try {
//設定啟影片面的主題
AppGlobals.getPackageManager().setSplashScreenTheme(......);
} catch (RemoteException e) {
Log.w(TAG, "Couldn't persist the starting theme", e);
}
}
}
// 啟影片面管理器
class SplashScreenManagerGlobal {
......
// 管理多個閃屏實作
private final ArrayList<SplashScreenImpl> mImpls = new ArrayList<>();
private SplashScreenManagerGlobal() {
// 向此行程注冊啟影片面管理器
ActivityThread.currentActivityThread().registerSplashScreenManager(this);
}
......
private static final Singleton<SplashScreenManagerGlobal> sInstance =
new Singleton<SplashScreenManagerGlobal>() {
@Override
protected SplashScreenManagerGlobal create() {
return new SplashScreenManagerGlobal();
}
};
private void addImpl(SplashScreenImpl impl) {
synchronized (mGlobalLock) {
mImpls.add(impl);
}
}
private void removeImpl(SplashScreenImpl impl) {
synchronized (mGlobalLock) {
mImpls.remove(impl);
}
}
......
public void handOverSplashScreenView(IBinder token,SplashScreenView splashScreenView) {
//呼叫的是 => splashScreenView.transferSurface();
transferSurface(splashScreenView);
//回呼 => impl.mExitAnimationListener.onSplashScreenExit(view);
dispatchOnExitAnimation(token, splashScreenView);
}
......
}
我們看到初始化SplashScreenManagerGlobal的時候,向此行程注冊啟影片面管理器
//android.app.ActivityThread
public void registerSplashScreenManager(SplashScreen.SplashScreenManagerGlobal manager) {
synchronized (this) {
mSplashScreenGlobal = manager;
}
}
如何把SplashScreen添加到當前的視窗的呢?
ActivityThread繼承ClientTransactionHandler,里面有一個這樣的抽象方法:
//android.app.ClientTransactionHandler
public abstract void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
@NonNull SplashScreenViewParcelable parcelable);
ActivityThread肯定會實作這個方法,那么是誰呼叫了它呢?由于篇幅問題,就不一行一行代碼的去介紹分析了,感興趣的同學,可以自己深入研究,我們下面貼出來呼叫的流程圖
誰呼叫了handleAttachSplashScreenView(),它的呼叫鏈流程圖如下:

好了,看完流程圖,我們再看一下ActivityThread#handleAttachSplashScreenView
//android.app.ActivityThread
@Override
public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
@Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {
final DecorView decorView = (DecorView) r.window.peekDecorView();
if (parcelable != null && decorView != null) {
createSplashScreen(r, decorView, parcelable);
}
......
}
private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
SplashScreenView.SplashScreenViewParcelable parcelable) {
// 初始化SplashScreenView構建器
final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
// 從parcelable中獲取配置資料,并通過build()初始化SplashScreenView,設定資料
final SplashScreenView view = builder.createFromParcel(parcelable).build();
// 把SplashScreenView添加到DecorView中
decorView.addView(view);
// 設定SystemUI顏色
view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);
// 重繪視圖
view.requestLayout();
......
}
核心的部分原始碼已經分析差不多了,剩下的一些原始碼,感興趣的同學可以自己查看閱讀
總結
- compileSdk升級到31
- 產品中統一使用兼容庫SplashScreen
implementation 'androidx.core:core-splashscreen:最新版本'
- 演示示例中資源目錄
drawable —— 定義低版本的drawable資源
drawable-v23 —— 定義6.0以上的資源
drawable-v31 —— 定義Android12及以上的資源
values —— 定義默認資源
values-night —— 定義默認深色模式資源
values-v23 —— 定義6.0以上系統資源
values-v31 —— 定義Android12及以上的資源
values-night-v31 —— 定義Android12及以上的深色模式資源
- 啟動頁圖示設計準則
中心圖示大圖,內容需要保留2/3的內邊距,否則圖示會被裁剪掉,另外:圖示尺寸大小可以修改;
底部品牌名圖示:尺寸比例需要為 2.5:1
兼容庫SplashScreen低版本不支持設定底部品牌圖示,
Android12需要在values-v31目錄手動添加如下屬性,才可以顯示品牌名圖示;
<!--兼容庫沒有這個屬性,我們需要在values-v31單獨配置一下-->
<item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
- Android12以下系統可以使用android:windowBackground為父主題設定視窗背景,切記不要在Android12及以上系統設定父主題的視窗背景(因為沒有效果😅😅)
- Android12系統以下系統,使用android:windowBackground的話,一定要給windowSplashScreenAnimatedIcon設定一個透明的drawable,否則會出現機器人圖示
- windowSplashScreenBackground 這個屬性的顏色一定要注意,配置有問題的話,啟動頁過渡到主頁面的時候,會有這個顏色閃出來,建議和Activity的android:windowBackground配置成一樣的顏色
- 在啟影片面上面,添加一個“廣告或者推廣頁面”,代碼和效果如下:
override fun onSplashScreenExit(splashScreenViewProvider: SplashScreenViewProvider) {
if(splashScreenViewProvider.view is ViewGroup){
// 在這里添加一個啟動頁廣告或者推廣頁面
val composeView = ComposeView(this@SplashScreenCompatActivity).apply {
setContent {
SplashAdScreen(onCloseAd = {
splashScreenViewProvider.remove()
})
}
}
(splashScreenViewProvider.view as ViewGroup).addView(composeView)
return
}
}

實踐 - 啟動頁添加廣告或者推廣頁
參考地址
- 在線流程圖制作
- 官方檔案 Splash screens
- Google官網介紹矢量圖
- 在線svg編輯器
- 在線制作矢量圖影片
- 在線合并多個GIF制作
所有示例原始碼GitHub: https://github.com/yugu88/SplashScreens
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/325621.html
標籤:其他
