Android12 Jetpack SplashScreen API总结
官方Android 12的Splash Screen文檔地址
官方Splash Screen兼容庫,支持所有版本系統
本篇文章主要圍繞下面三個問題來介紹:
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 = 200LslideUp.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-vectorxmlns:android="http://schemas.android.com/apk/res/android"xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vectorandroid:name="vector"android:width="24dp"android:height="24dp"android:viewportWidth="24"android:viewportHeight="24"><group android:name="group"><pathandroid: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"><pathandroid: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"/><pathandroid: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"><pathandroid: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"/><pathandroid: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"><objectAnimatorandroid: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"><objectAnimatorandroid: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庫里面的最新版本
(B).主題和外觀配置
- 定義Activity應該使用的主題
- 創建父主題給啟動畫面使用
- AndroidManifest.xml配置Activity的主題
(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).遇到的問題
兼容庫目前存在的問題
問題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.Activityclass SplashScreenImpl implements SplashScreen {......//把SplashScreenImpl添加到這個單例類里面private final SplashScreenManagerGlobal mGlobal;public SplashScreenImpl(Context context) {mGlobal = SplashScreenManagerGlobal.getInstance();}@Overridepublic 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>() {@Overrideprotected 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.ActivityThreadpublic void registerSplashScreenManager(SplashScreen.SplashScreenManagerGlobal manager) {synchronized (this) {mSplashScreenGlobal = manager;} }如何把SplashScreen添加到當前的窗口的呢?
ActivityThread繼承ClientTransactionHandler,里面有一個這樣的抽象方法:
ActivityThread肯定會實現這個方法,那么是誰調用了它呢?由于篇幅問題,就不一行一行代碼的去介紹分析了,感興趣的同學,可以自己深入研究,我們下面貼出來調用的流程圖
誰調用了handleAttachSplashScreenView(),它的調用鏈流程圖如下:
好了,看完流程圖,我們再看一下ActivityThread#handleAttachSplashScreenView
核心的部分源碼已經分析差不多了,剩下的一些源碼,感興趣的同學可以自己查看閱讀
總結
- compileSdk升級到31
- 產品中統一使用兼容庫SplashScreen
- 演示示例中資源目錄
- 啟動頁圖標設計準則
中心圖標大圖,內容需要保留2/3的內邊距,否則圖標會被裁剪掉,另外:圖標尺寸大小可以修改;
底部品牌名圖標:尺寸比例需要為 2.5:1
兼容庫SplashScreen低版本不支持設置底部品牌圖標,
Android12需要在values-v31目錄手動添加如下屬性,才可以顯示品牌名圖標;
- Android12以下系統可以使用android:windowBackground為父主題設置窗口背景,切記不要在Android12及以上系統設置父主題的窗口背景(因為沒有效果😅😅)
- Android12系統以下系統,使用android:windowBackground的話,一定要給windowSplashScreenAnimatedIcon設置一個透明的drawable,否則會出現機器人圖標
- windowSplashScreenBackground 這個屬性的顏色一定要注意,配置有問題的話,啟動頁過渡到主頁面的時候,會有這個顏色閃出來,建議和Activity的android:windowBackground配置成一樣的顏色
- 在啟動畫面上面,添加一個“廣告或者推廣頁面”,代碼和效果如下:
實踐 - 啟動頁添加廣告或者推廣頁
參考地址
- 在線流程圖制作
- 官方文檔 Splash screens
- Google官網介紹矢量圖
- 在線svg編輯器
- 在線制作矢量圖動畫
- 在線合并多個GIF制作
所有示例源碼GitHub:
https://github.com/Pangu-Immortal
https://github.com/Pangu-Immortal/SplashScreens
總結
以上是生活随笔為你收集整理的Android12 Jetpack SplashScreen API总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对比Compose 、kotlin、fl
- 下一篇: Android基于Docker容器的双系