這篇文章主要給大家介紹了關於Flutter啓動頁(閃屏頁)的具體實現及原理的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Flutter具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
爲什麼要有啓動頁?
在以下文章中,啓動頁就是閃屏頁。
現在大部分App都有啓動頁,那麼爲什麼要有啓動頁?這是個值得思考的問題,如果沒有啓動頁會怎樣,大部分的App會白屏(也有可能是黑屏,主題設置有關係)非常短的時間,然後才能展示App的內容。
那麼問題來了,一定要有啓動頁嗎?答案:不是,而且是儘可能不要有啓動頁,因爲啓動頁會讓用戶體驗不夠連貫,甚至IOS在開發手冊上就不推薦使用啓動頁。
我們深入思考一下,既然不推薦爲什麼這樣流行,答案非常簡單,啓動頁的成本非常低,如果你想把的App啓動優化到一個非常短的時間,還是有一定成本的。
Android啓動流程
爲什麼要談Android的啓動流程呢?因爲Flutter啓動的時候,依賴的是Android的運行環境,其本質是Activity上添加了一個FlutterView,FlutterView繼承SurfaceView,那麼就容易理解了,Flutter的全部頁面都是渲染到了FlutterView上,如果不熟悉Flutter的啓動流程可以參考Flutter啓動流程 這篇文章,下面是對Flutter啓動的一個簡單描述。
在Flutter中,啓動頁的作用是在FlutterView顯示第一幀之前,不要出現白屏,在FlutterView顯示第一幀之前,我們分成兩個階段,Android啓動階段和Flutter啓動階段,Android啓過程添加啓動頁非常容易,在主題xml中添加android:windowBackground屬性,Flutter怎麼添加啓動頁呢?其實框架已經幫助咱們實現好了,我下面就給大家說一下原理。
Flutter啓動頁具體實現和原理
創建一個SplashActivity,這Activity繼承FlutterActivity,重寫onCreate()方法,在onCreate()方法中調用GeneratedPluginRegistrant.registerWith()
,下面是啓動頁的代碼。
public class SplashActivity extends FlutterActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); } }
在Manifest中添加SplashActivity作爲App的啓動Activity,設置SplashActivity的主題是LaunchTheme。下面是Manifest的配置文件。
<activity android:name=".SplashActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize"> <meta-data android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" android:value="true" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
meta-data的name = "io.flutter.app.android.SplashScreenUntilFirstFrame"
的value一定要設置成true,一定要設置成true,一定要設置成true重要的事情說三遍,如果這個屬性設置成false,效果是這樣的。
從現象觀察,啓動頁中間有一段時間黑屏,這個爲什麼呢?前面我們說過,Flutter的啓動流程分成兩部分,一部分是Android啓動階段,一個是Flutter的啓動階段,這個黑屏就是Flutter的啓動階段沒有啓動頁所造成的。我們從源碼入手,詳細分析一下,下面是FlutterActivityDelegate的部分源碼。
public final class FlutterActivityDelegate implements FlutterActivityEvents, FlutterView.Provider, PluginRegistry { private static final String SPLASH_SCREEN_META_DATA_KEY = "io.flutter.app.android.SplashScreenUntilFirstFrame"; private View launchView; @Override public void onCreate(Bundle savedInstanceState) { String[] args = getArgsFromIntent(activity.getIntent()); FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args); flutterView = viewFactory.createFlutterView(activity); if (flutterView == null) { FlutterNativeView nativeView = viewFactory.createFlutterNativeView(); flutterView = new FlutterView(activity, null, nativeView); flutterView.setLayoutParams(matchParent); activity.setContentView(flutterView); launchView = createLaunchView();//1 if (launchView != null) { addLaunchView();//2 } } } private View createLaunchView() { if (!showSplashScreenUntilFirstFrame()) {//3 return null; } final Drawable launchScreenDrawable = getLaunchScreenDrawableFromActivityTheme(); final View view = new View(activity); view.setBackground(launchScreenDrawable); return view; } private Drawable getLaunchScreenDrawableFromActivityTheme() { //省略了部分代碼 try { return activity.getResources().getDrawable(typedValue.resourceId); } catch (NotFoundException e) { return null; } } private Boolean showSplashScreenUntilFirstFrame() { try { ActivityInfo activityInfo = activity.getPackageManager().getActivityInfo( activity.getComponentName(), PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES); Bundle metadata = activityInfo.metaData; return metadata != null && metadata.getBoolean(SPLASH_SCREEN_META_DATA_KEY); } catch (NameNotFoundException e) { return false; } } private void addLaunchView() { activity.addContentView(launchView, matchParent);//4 flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() {//5 @Override public void onFirstFrame() { FlutterActivityDelegate.this.launchView.animate() .alpha(0f) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { ((ViewGroup) FlutterActivityDelegate.this.launchView.getParent()) .removeView(FlutterActivityDelegate.this.launchView);//5 } }); } }); activity.setTheme(android.R.style.Theme_Black_NoTitleBar); } }
註釋1
這個段代碼很容易理解,創建一個LaunchView,主要邏輯在createLaunchView()中,原理也很簡單,根據主題中的R.attr.windowBackground
屬性,生成一個Drawable,然後創建了一個View,並且把這個View的背景設置成Drawable。
註釋3
showSplashScreenUntilFirstFrame()是得到Manifet中io.flutter.app.android.SplashScreenUntilFirstFrame
的屬性的值,如果是false,那麼久返回一個空的的LaunchView,也就不會執行註釋2的代碼。這就是我們上面說的如果設置成false就顯示黑屏的原因。
註釋2
調用addLaunchView(),這方法也很簡單,首先看註釋4,把LaunchView添加到當前的Activity中,然後添加了一個監聽,在註釋5處,這個監聽是當FlutterView第一幀加載完成後回調,回調做了什麼事情呢?很簡單,把LaunchView刪除了,顯示FlutterView的第一幀。
總結一下,就是把Android的啓動頁生成一個Drawable,創建了一個LaunchView,把Drawable設置成LaunchView的背景,當前的Activity添加這LaunchView,如果FlutterView的第一幀顯示了,把LaunchView刪除。
設置主題,下面是LaunchTheme的代碼。
<resources> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <!-- Show a splash screen on the activity. Automatically removed when Flutter draws its first frame --> <item name="android:windowBackground">@drawable/launch_background</item> </style> </resources>
下面是launch_background的代碼。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"> <item> <bitmap android:src="@mipmap/ic_launch_bg" /> </item> <item android:width="90dp" android:height="90dp" android:gravity="center"> <bitmap android:src="@mipmap/ic_launch_logo" /> </item> </layer-list>
最終效果如下,沒有黑屏,非常順滑。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對神馬文庫的支持。