最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android 超詳細(xì)SplashScreen入門教程

 更新時(shí)間:2022年03月26日 15:44:51   作者:guolin  
Android 12正式版即將發(fā)布,有一個(gè)非常顯著的視覺(jué)變化就是,Android 12強(qiáng)制給所有的App都增加了SplashScreen的功能。是的,即使你什么都不做,只要你的App安裝到了Android 12手機(jī)上,都會(huì)自動(dòng)擁有這個(gè)新功能

這次的Android系統(tǒng)變化當(dāng)中,UI的變化無(wú)疑是巨大的。Google在Android 12中采取了一種叫作Material You的界面設(shè)計(jì),一切以你為中心,以你的喜好為風(fēng)格。相信大家一旦上手Android 12之后應(yīng)該能立刻察覺(jué)到這些視覺(jué)方面的變化。

關(guān)于這個(gè)SplashScreen,今天就值得好好講一講了。

什么是SplashScreen

SplashScreen其實(shí)通俗點(diǎn)講就是指的閃屏界面。這個(gè)我們國(guó)內(nèi)開發(fā)者一定不會(huì)陌生,因?yàn)榻^大多數(shù)的國(guó)內(nèi)App都會(huì)有閃屏界面這個(gè)功能,很多的App還會(huì)利用閃屏界面去打廣告。下圖是QQ的閃屏界面:

然而在海外,閃屏界面其實(shí)并不太常見(jiàn),甚至Google之前都不推薦我們?cè)贏pp中加入閃屏界面,所以這次Android 12中官方推出了SplashScreen功能還是讓我有點(diǎn)意外的。

不過(guò)這次官方的SplashScreen和我們國(guó)內(nèi)常見(jiàn)的閃屏界面還不一樣,它并不是為了讓你在這個(gè)界面打廣告的,而是為了在App啟動(dòng)初始化的時(shí)候避免讓用戶在一個(gè)空白界面等待過(guò)長(zhǎng)時(shí)間。

雖說(shuō)Android一直是建議我們將重量級(jí)的操作延后執(zhí)行,讓App的啟動(dòng)時(shí)間越短越好,但是仍然無(wú)法完全避免一些App啟動(dòng)時(shí)的短暫白屏情況。

因此,這次的SplashScreen就是為了解決這個(gè)問(wèn)題而推出的,它將會(huì)在一定程度上提升用戶體驗(yàn),徹底告別過(guò)去的啟動(dòng)白屏現(xiàn)象。

何時(shí)會(huì)顯示SplashScreen

注意,SplashScreen在Android 12上是強(qiáng)制的,即使你什么都不做,你的App在Android 12上也會(huì)自動(dòng)擁有SplashScreen界面。默認(rèn)情況下,App的Launcher圖標(biāo)會(huì)作為SplashScreen界面的中央圖標(biāo),windowBackground屬性指定的顏色會(huì)作為SplashScreen界面的背景顏色。不過(guò)這些都可以修改。

關(guān)于如何修改我們稍后再談,既然SplashScreen界面是強(qiáng)制顯示的,我們首先應(yīng)該搞清楚,在什么情況下會(huì)顯示SplashScreen?

根據(jù)官方文檔的說(shuō)明,SplashScreen會(huì)在App冷啟動(dòng)和溫啟動(dòng)的時(shí)候顯示,永遠(yuǎn)不會(huì)在App熱啟動(dòng)的時(shí)候顯示。

那么,什么是冷啟動(dòng)、溫啟動(dòng)和熱啟動(dòng)呢?

簡(jiǎn)單概括一下的話,如果App被完全殺死了,這個(gè)時(shí)候去啟動(dòng)它就是冷啟動(dòng)。如果App的主Activity被銷毀或回收了,這個(gè)時(shí)候去啟動(dòng)它就是溫啟動(dòng)。如果App只是被掛起到了后臺(tái),這個(gè)時(shí)候去啟動(dòng)它就是熱啟動(dòng)。

我這種概括方式在一些細(xì)節(jié)方面其實(shí)并不足夠準(zhǔn)確,但如果只是為了大概了解SplashScreen的顯示時(shí)機(jī),那么簡(jiǎn)單這樣理解就可以了。

而如果你想更加細(xì)致地學(xué)習(xí)這幾種啟動(dòng)模式的區(qū)別,可以參考以下官方文檔鏈接:

https://developer.android.google.cn/topic/performance/vitals/launch-time

何時(shí)會(huì)隱藏SplashScreen

SplashScreen是為了防止App在冷啟動(dòng)或溫啟動(dòng)的時(shí)候初始化時(shí)間過(guò)長(zhǎng),導(dǎo)致用戶看到白屏現(xiàn)象而引入的。那么很顯然,只要App初始化完成,可以將內(nèi)容展示給用戶的時(shí)候,SplashScreen就會(huì)自動(dòng)隱藏。

如果用更加科學(xué)一點(diǎn)的定義來(lái)描述的話,那就是當(dāng)App開始在界面上繪制第一幀的時(shí)候,SplashScreen就會(huì)消失。

那么一個(gè)App什么時(shí)候會(huì)在界面上繪制第一幀呢?我們可以不用知道它準(zhǔn)確的時(shí)機(jī),但是要知道它大致的時(shí)機(jī)范圍,因?yàn)檫@決定要我們?nèi)绾胃玫鼐帉懘a。

假如我們?cè)谝粋€(gè)應(yīng)用的主Activity中編寫如下代碼:

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Thread.sleep(3000)
    }

}

或者也可以這樣寫:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onResume() {
        super.onResume()
        Thread.sleep(3000)
    }

}

可以看到,我們分別在onCreate()和onResume()方法中讓主線程沉睡了3秒鐘。然后運(yùn)行一下程序:

你會(huì)發(fā)現(xiàn),SplashScreen真的顯示了3秒鐘以上才消失。

同時(shí)這也說(shuō)明了,不管是onCreate()還是onResume()方法,它們都還處于App的初始化階段,并沒(méi)有開始在界面上繪制第一幀。

接下來(lái)我們可以嘗試這樣改造一下代碼:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val contentView: View = findViewById(android.R.id.content)
        contentView.post {
            Thread.sleep(3000)
        }
    }

}

這里可以借助任何一個(gè)View的實(shí)例調(diào)用一下它的post函數(shù),并在post的回調(diào)當(dāng)中讓主線程沉睡3秒。然后再次運(yùn)行程序:

你會(huì)發(fā)現(xiàn),SplashScreen只是短暫顯示了一下就進(jìn)入了App的主界面。但現(xiàn)在主界面其實(shí)還是不能響應(yīng)任何事件的,而是要等待3秒鐘以后才能響應(yīng)。

由此我們就可以大致得出一些結(jié)論,比如說(shuō)onCreate()和onResume()方法都是在App開始繪制第一幀之前執(zhí)行的,而View的post回調(diào)則是在App繪制第一幀之后執(zhí)行的。

當(dāng)?shù)谝粠L制出來(lái)以后,說(shuō)明App的界面上已經(jīng)可以有東西展示出來(lái)了,將不會(huì)再是一個(gè)空白界面,此時(shí)繼續(xù)展示SplashScreen就沒(méi)有意義了,所以SplashScreen理應(yīng)在這個(gè)時(shí)候消失。但同時(shí),如果在第一幀繪制出來(lái)之后我們?cè)僭谥骶€程里去執(zhí)行耗時(shí)邏輯,那么用戶將會(huì)實(shí)實(shí)在在感受到卡頓的體驗(yàn),SplashScreen已經(jīng)無(wú)法再幫我們進(jìn)行掩蓋。

實(shí)際上,不管是在第一幀繪制之前還是之后,我們都不應(yīng)該在主線程執(zhí)行長(zhǎng)時(shí)間的耗時(shí)操作。最正確的做法是,只在主線程里做最少的事情,讓App可以快速響應(yīng)用戶的各種輸入事件,將所有耗時(shí)的邏輯都放到子線程當(dāng)中去處理。

延長(zhǎng)顯示SplashScreen

延長(zhǎng)SplashScreen的顯示時(shí)間是一種我不太建議的做法,但我們確實(shí)可以這樣做。

先說(shuō)為什么不建議延長(zhǎng)SplashScreen的顯示時(shí)間。

原則上我們應(yīng)該讓App的啟動(dòng)時(shí)間越短越好,即使有了SplashScreen,我們也不應(yīng)該故意讓App的啟動(dòng)時(shí)間變得更長(zhǎng)。

要知道,在SplashScreen的顯示過(guò)程中,App是一直在主線程里執(zhí)行初始化操作的。這也就意味著,你的App主線程是一直被占據(jù)著的,從而無(wú)法響應(yīng)用戶的各種輸入,這也就導(dǎo)致了應(yīng)用程序ANR的可能。不管有沒(méi)有SplashScreen,只要在主線程里執(zhí)行了過(guò)多耗時(shí)操作,都可能會(huì)導(dǎo)致ANR。

那么為什么還要延長(zhǎng)顯示SplashScreen呢?

有一種說(shuō)法是,他們App的內(nèi)容都是從服務(wù)器或者從本地磁盤讀取的,即使App初始化完成了,數(shù)據(jù)還沒(méi)有準(zhǔn)備好,也就沒(méi)有內(nèi)容可以展示,所以想要將SplashScreen延長(zhǎng)到數(shù)據(jù)準(zhǔn)備完成。

但我個(gè)人認(rèn)為這并不是一種非常合適的做法,這種情況我們完全可以先在界面上顯示一個(gè)加載進(jìn)度條,或者占位圖之類的東西,然后等有了數(shù)據(jù)之后再更新界面上的內(nèi)容。

還有一種說(shuō)法是,他們希望SplashScreen不僅僅是用來(lái)加載等待的,還可以用來(lái)做一些品牌展示和推廣之類的工作。這樣如果SplashScreen過(guò)快地消失,可能用戶根本來(lái)不及看到SplashScreen上的內(nèi)容。

當(dāng)然,也有另一種說(shuō)法是,他們?cè)赟plashScreen上顯示的并不是一個(gè)靜態(tài)的圖標(biāo),而是一個(gè)動(dòng)畫,所以至少要等到動(dòng)畫結(jié)束之后再隱藏SplashScreen。

不管你是屬于哪一種,Google都給我們提供了延長(zhǎng)顯示SplashScreen的能力。

剛才說(shuō)了,SplashScreen會(huì)在App開始在界面上繪制第一幀的時(shí)候自動(dòng)消失,那么如果我們阻止了App在界面上繪制第一幀,是不是SplashScreen就不會(huì)消失了?

沒(méi)錯(cuò),這就是延長(zhǎng)顯示SplashScreen的工作原理。具體代碼如下:

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val contentView: View = findViewById(android.R.id.content)
        contentView.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                return false
            }
        })
    }

}

這里我們?cè)诨卣{(diào)函數(shù)onPreDraw()中返回了一個(gè)false,也就意味著,我們的PreDraw階段始終沒(méi)有準(zhǔn)備好。既然PreDraw都還沒(méi)準(zhǔn)備好,App肯定是不會(huì)開始繪制第一幀的,那么SplashScreen自然也就不會(huì)消失了。

于是上述代碼將會(huì)實(shí)現(xiàn)一個(gè)永久顯示SplashScreen的效果。

有了這個(gè)原理,那么我們就可以根據(jù)自己的需求編寫一些邏輯了。比如剛才提到的從磁盤讀取數(shù)據(jù)的場(chǎng)景,我們可以一開始在onPreDraw()中函數(shù)中返回false,然后開啟子線程去讀取數(shù)據(jù),等到數(shù)據(jù)讀取完成再將返回值改成true即可。示例代碼如下:

class MainActivity : AppCompatActivity() {

    @Volatile
    private var isReady = false
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val contentView: View = findViewById(android.R.id.content)
        contentView.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                if (isReady) {
                    contentView.viewTreeObserver.removeOnPreDrawListener(this)
                }
                return isReady
            }
        })
        thread { 
            // Read data from disk
            ...
            isReady = true
        }
    }

}

注意,在SplashScreen的顯示過(guò)程中,onPreDraw()函數(shù)是以很高的頻率在持續(xù)刷新的。所以它依然會(huì)將主線程阻塞住,導(dǎo)致應(yīng)用程序無(wú)法響應(yīng)用戶的輸入事件,直到我們?cè)趏nPreDraw()函數(shù)返回true才會(huì)停止刷新。

自定義SplashScreen樣式

接下來(lái)終于到了可能許多朋友最為關(guān)心的部分,自定義SplashScreen的樣式。

雖然默認(rèn)的SplashScreen界面并不難看,對(duì)于大多數(shù)的App來(lái)說(shuō)可能也已經(jīng)完全足夠了,但是Google仍然給了我們比較高的控制權(quán)來(lái)自定義SplashScreen的樣式。

這里我就將幾個(gè)比較重要的自定義樣式屬性來(lái)跟大家介紹一下。

剛才有提到過(guò),SplashScreen默認(rèn)會(huì)使用windowBackground屬性指定的顏色作為界面的背景顏色。但如果我想要單獨(dú)給SplashScreen界面指定一個(gè)背景色呢?可以在主題文件中定義如下屬性:

<item name="android:windowSplashScreenBackground">#CCCCCC</item>

這里我們單獨(dú)將SplashScreen的背景指定成了淺灰色,效果如下圖所示:

需要注意,這個(gè)屬性以及接下來(lái)要介紹的所有屬性都是在Android 12系統(tǒng)上新增的,所以你應(yīng)該在一個(gè)values-v31的專屬目錄下使用它們。

既然能夠自定義SplashScreen的背景色,那么我們是不是也可以自定義SplashScreen上的圖標(biāo)呢?

很難想象為什么要在SplashScreen界面上展示一個(gè)和Launcher Icon不同的圖標(biāo),但Google確實(shí)允許我們這么做:

<item name="android:windowSplashScreenAnimatedIcon">@drawable/splash_screen_icon</item>

這里我們給SplashScreen界面指定了一個(gè)單獨(dú)的圖標(biāo),注意這個(gè)圖標(biāo)可以是一張靜態(tài)的圖片,也可以是一個(gè)動(dòng)畫資源。由于制作動(dòng)畫比較復(fù)雜,不在本文的討論范圍內(nèi),所以我們只以靜態(tài)圖片來(lái)舉例。

我準(zhǔn)備了這樣一張圖,并將它命名為splash_screen_icon.jpg。

然后運(yùn)行程序,效果如下圖所示:

你會(huì)發(fā)現(xiàn),雖然我提供的圖標(biāo)是正方形的,但最終顯示在SplashScreen上的卻是一個(gè)圓形圖片。

由此我們可以得出結(jié)論,SplashScreen和Launcher Icon一樣,也是同樣會(huì)受到廠商mask的影響的。它的大致工作原理如下圖所示:

可以看到,這里背景層是一張藍(lán)色的網(wǎng)格圖,前景層是一張Android機(jī)器人Logo圖,然后蓋上一層圓形的mask,最終就裁剪出了一張圓形的應(yīng)用圖標(biāo)。

如果對(duì)此還不夠了解的話,可以去參考我之前寫的一篇文章 Android 8.0系統(tǒng)中的應(yīng)用圖標(biāo)適配 。

上述例子中我使用的是一張不透明的圖片來(lái)作為圖標(biāo),其實(shí)我們也可以提供一張有透明度的圖片,然后再借助如下屬性來(lái)控制圖標(biāo)的背景色:

<item name="android:windowSplashScreenIconBackgroundColor">#BB86FC</item>

這樣,只要前景圖標(biāo)是有透明度的圖片,背景顏色就可以顯示出來(lái)了,如下圖所示:

最后,如果你希望在SplashScreen上再進(jìn)行一些品牌方面的推廣,還可以通過(guò)以下屬性來(lái)顯示你的品牌信息:

<item name="android:windowSplashScreenBrandingImage">@drawable/brand_logo</item>

這里可以傳入一張品牌圖片,我沒(méi)能在官網(wǎng)找到Google對(duì)這張圖片尺寸比例的定義,但如果你隨便傳入一張圖片的話,可能會(huì)出現(xiàn)拉伸的情況。

為此,我通過(guò)自己做實(shí)驗(yàn),大概總結(jié)出了這里應(yīng)該使用一張2.4:1的圖片,最終的效果如下圖所示:

適配舊版SplashScreen

最后,我們?cè)賮?lái)了解一下,如何才能去適配舊版的SplashScreen。

準(zhǔn)確來(lái)說(shuō),Android官方是沒(méi)有舊版SplashScreen這一說(shuō)的,因?yàn)镾plashScreen是在Android 12中才新增加的功能。

但是,有很多的App早在官方提供API之前,就已經(jīng)自己實(shí)現(xiàn)了SplashScreen功能。正如前面所說(shuō),這個(gè)功能在國(guó)內(nèi)很常見(jiàn)。

那么接下來(lái)問(wèn)題來(lái)了。過(guò)去通過(guò)自己的方式實(shí)現(xiàn)的SplashScreen,和現(xiàn)在官方提供的SplashScreen要如何兼容呢?

這著實(shí)是一個(gè)問(wèn)題,主要原因在于,SplashScreen在Android 12上是強(qiáng)制啟用的。所以,如果你的代碼中還保留著過(guò)去自己實(shí)現(xiàn)的那一套SplashScreen,在Android 12中就會(huì)出現(xiàn)雙重SplashScreen的現(xiàn)象。

但如果我們從代碼中移除了過(guò)去自己實(shí)現(xiàn)的SplashScreen,那么在Android 12之前的系統(tǒng)版本就沒(méi)有SplashScreen功能了。

要如何解決這個(gè)問(wèn)題呢?不要著急,Google在AndroidX中提供了一個(gè)向下兼容的SplashScreen庫(kù)。根據(jù)官方的說(shuō)法,我們只要使用這個(gè)庫(kù)就可以輕松解決舊版SplashScreen的適配問(wèn)題。

用法很簡(jiǎn)單,跟著如下步驟走即可。

第一步,修改build.gradle文件,將targetSdkVersion指定到31,并添加如下依賴庫(kù):

android {
compileSdkVersion 31
...
}
dependencies {
...
implementation 'androidx.core:core-splashscreen:1.0.0-alpha01'
}

第二步,修改主題文件,如下所示:

<style name="MySplashTheme" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">#CCCCCC</item>
    <item name="windowSplashScreenAnimatedIcon">@drawable/splash_screen_icon</item>
    <item name="postSplashScreenTheme">@style/Theme.SplashTest</item>
</style>

注意這里的變動(dòng)至關(guān)重要。我們新定義了一個(gè)主題,這個(gè)主題的名字叫什么都可以,但它一定要繼承自Theme.SplashScreen。

然后我們可以使用windowSplashScreenBackground和windowSplashScreenAnimatedIcon這兩個(gè)屬性來(lái)分別指定SplashScreen的背景色和中央圖標(biāo)。

不過(guò)我比較疑惑的是,我們不能像剛才那樣在SplashScreen界面指定圖標(biāo)的背景色和品牌圖片,因?yàn)檫@里并沒(méi)有那兩個(gè)屬性。不知道是不是因?yàn)楝F(xiàn)在庫(kù)還屬性比較早期的階段,以后或許會(huì)加上這些屬性。

另外,我們還必須要指定postSplashScreenTheme這個(gè)屬性,將它的值指定成你的App原來(lái)的主題。這樣,當(dāng)SplashScreen結(jié)束時(shí),你的主題就能夠被復(fù)原,從而不會(huì)影響到你的App的主題外觀。

第三步,修改AndroidManifest.xml文件,應(yīng)用我們剛剛新定義的主題:

<manifest>
   <application android:theme="@style/MySplashTheme">
    <!-- or -->
        <activity android:theme="@style/MySplashTheme">
	...

這里視你之前代碼的寫法來(lái)決定是替換application標(biāo)題里的theme,還是activity標(biāo)題里的theme。

第四步,在你的啟動(dòng)Activity中加入如下代碼:

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        installSplashScreen()
        setContentView(R.layout.activity_main)
        ...
    }

}

如果你還在使用Java語(yǔ)言的話,那么需要改成如下寫法:

public class MainActivity extends AppCompatActivity {

    @Override
	protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    SplashScreen.installSplashScreen(this);
	    setContentView(R.layout.activity_main);
	    ...
	}
    
}

注意,installSplashScreen()這句代碼一定要加入到setContentView()的前面。

這樣,當(dāng)我們剛剛進(jìn)入App的時(shí)候,就會(huì)先顯示一個(gè)SplashScreen界面,然后當(dāng)App初始化完成之后,SplashScreen會(huì)自動(dòng)消失,并且主題也會(huì)變成原來(lái)App的主題樣式。

接下來(lái)我們只需要把過(guò)去自己實(shí)現(xiàn)的SplashScreen移除即可,不然的話仍然還是會(huì)產(chǎn)生雙重SplashScreen的現(xiàn)象。

以上步驟是官方提供的適配舊版SplashScreen的解決方案,但是我按照上述步驟進(jìn)行了一下實(shí)現(xiàn),最終的測(cè)試效果卻非常差。

主要問(wèn)題集中在于舊版Android系統(tǒng)上中央圖標(biāo)不會(huì)被mask,而在Android 12上中央圖標(biāo)卻會(huì)被mask,從而導(dǎo)致新舊系統(tǒng)的SplashScreen界面差別很大,也很難看。

不過(guò)畢竟我們現(xiàn)在使用的SplashScreen庫(kù)還處于alpha階段,后面發(fā)生變動(dòng)的可能性很大,或許這些問(wèn)題在正式版出現(xiàn)之后都會(huì)被修復(fù)。

另外,即使官方的庫(kù)有問(wèn)題,我們還是完全有辦法去規(guī)避它。比如說(shuō)在代碼中進(jìn)行邏輯判斷,如果是Android 12系統(tǒng)就不顯示自己的SplashScreen界面,因?yàn)橄到y(tǒng)有默認(rèn)的SplashScreen。而在Android 12以下的系統(tǒng),就顯示自己的SplashScreen界面。

方法總比困難多,不是嗎?

那么本篇文章的內(nèi)容就到這里,讓我們一起靜靜等待Android 12的到來(lái)吧。

到此這篇關(guān)于Android 超詳細(xì)SplashScreen入門教程的文章就介紹到這了,更多相關(guān)Android SplashScreen內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

昭苏县| 安西县| 平昌县| 吕梁市| 德惠市| 石景山区| 佳木斯市| 汾西县| 乐都县| 墨竹工卡县| 上蔡县| 友谊县| 宜川县| 乐昌市| 博罗县| 滁州市| 福海县| 大英县| 漾濞| 五莲县| 湟源县| 盘锦市| 增城市| 黑龙江省| 太原市| 西峡县| 宁强县| 万源市| 策勒县| 图们市| 福清市| 色达县| 陆良县| 岚皋县| 中江县| 奎屯市| 尉犁县| 海丰县| 青岛市| 长春市| 黔西县|