شاشات البداية

اعتبارًا من Android 12، تتيح واجهة برمجة التطبيقات SplashScreen للتطبيقات بدء التشغيل مع مؤثرات حركية، بما في ذلك مؤثرات خاصة عند بدء تشغيل التطبيق وكذلك عند الانتقال إلى التطبيق، بالإضافة إلى شاشة بداية تعرض رمز التطبيق. SplashScreen هو Window، وبالتالي يشمل Activity.

الشكل 1. شاشة بداية

توفّر تجربة شاشة البداية عناصر تصميم قياسية عند كل عملية بدء لتطبيقك، ولكن يمكن أيضًا تخصيصها كي يحافظ تطبيقك على علامته التجارية الفريدة.

بالإضافة إلى استخدام واجهة برمجة تطبيقات نظام التشغيل SplashScreen، يمكنك أيضًا استخدام مكتبة SplashScreen compat التي تُغلِّف واجهة برمجة تطبيقات SplashScreen.

آلية عمل شاشة البداية

عندما يشغِّل مستخدم تطبيقًا عندما لا تكون عملية التطبيق قيد التشغيل (بدء على البارد) أو لم يتم إنشاء Activity (إعادة التشغيل البطيء)، تحدث العميل التالية:

  1. يعرِض النظام شاشة البداية باستخدام المظاهر وأي تأثيرات متحرّكة تحدِّدها.

  2. عندما يصبح التطبيق جاهزًا، يتم إغلاق شاشة البداية وعرض التطبيق.

لا تظهر شاشة البداية مطلقًا أثناء التشغيل السريع.

عناصر شاشة البداية وآلية عملها

يتم تحديد عناصر شاشة البداية من خلال ملفات موارد XML فيملف بيان Android. يجب أن يتوفّر لكل عنصر إصداران للوضع الفاتح والوضع الداكن.

تتألف العناصر القابلة للتخصيص في شاشة البداية من رمز التطبيق وخلفية الرمز وخلفية النافذة:

صورة تعرِض العناصر المضمّنة في شاشة البداية
الشكل 2. عناصر قابلة للتخصيص في شاشة البداية

راجِع العناصر التالية الموضّحة في الشكل 2:

1 يجب أن يكون رمز التطبيق متّجهًا قابلاً للرسم. ويمكن أن يكون ثابتًا أو متحركًا. على الرغم من أنّ الصور المتحركة يمكن أن تبلغ مدّتها مدة غير محدودة، ننصح بعدم تجاوز 1,000 ملي ثانية. رمز مشغّل التطبيقات هو الرمز التلقائي.

2 خلفية الرمز اختيارية ومفيدة إذا كنت بحاجة إلى المزيد من التباين بين الرمز وخلفية النافذة. إذا كنت تستخدِم رمزًا قابلاً للتكيّف، يتم عرض خلفيته إذا كان هناك تباين كافٍ مع خلفية النافذة.

3 كما هو الحال مع الرموز التكيُّفية، يتم حجب ثلث العناصر الأمامية.

4 تتألّف خلفية النافذة من لون واحد غير شفاف. إذا تم ضبط خلفية النافذة وكانت بلون عادي، يتم استخدامها تلقائيًا في حال عدم ضبط السمة.

أبعاد شاشة البداية

يستخدم رمز شاشة البداية المواصفات نفسها المستخدَمة في الرموز المتكيّفة، على النحو التالي:

  • صورة العلامة التجارية: يجب أن تكون بحجم 200×80 بكسل غير مرتبطة بالكثافة.
  • رمز التطبيق مع خلفية الرمز: يجب أن يكون بحجم ‎240×240 dp وأن يلائم دائرة قطرها 160 dp.
  • رمز التطبيق بدون خلفية: يجب أن يكون بحجم 288×288 بكسل وأن يلائم دائرة قطرها 192 بكسل.

على سبيل المثال، إذا كان الحجم الكامل للصورة هو 300×300 نقطة كثافة بكسل، يجب أن يتلاءم الرمز مع دائرة قطرها 200 نقطة كثافة بكسل. يصبح كل شيء خارج الدائرة غير مرئي (مموَّه).

صورة تعرض أبعادًا مختلفة للرمز لاستخدامه مع خلفية صلبة وشفافة
الشكل 3. أبعاد رمز شاشة البداية للخلفيات الملونة والشفافة، على التوالي

الصور المتحركة في شاشة البداية وتسلسل الإطلاق

غالبًا ما يكون وقت الاستجابة الإضافي مرتبطًا بتشغيل تطبيق عند بدء التشغيل على البارد. تضيف إضافة رمز متحرك إلى شاشة البداية لمسة جمالية واضحة، و توفّر تجربة أكثر رواجًا. تُظهر أبحاث المستخدمين أنّ وقت بدء التشغيل المُلاحظ هو أقل عند عرض صورة متحركة.

يتم تضمين تأثير متحرك لشاشة البداية ضمن مكوّنات تسلسل التشغيل، كما هو موضح في الشكل 4.

صورة تعرض تسلسل الإطلاق في اثني عشر لقطة متتالية، بدءًا من النقر على رمز المشغّل وملء الشاشة أثناء تكبيره
الشكل 4. تسلسل الإطلاق
  1. إدخال صورة متحركة: تتألف من عرض النظام إلى شاشة البداية ويتحكم النظام في هذه القيمة ولا يمكن تخصيصها.

  2. شاشة البداية (التي تظهر أثناء جزء "الانتظار" من التسلسل): يمكن تخصيص شاشة البدء، ما يتيح لك تقديم علامتك التجارية وصورة شعارك المتحركة. يجب أن يستوفي المتطلبات الموضّحة في هذه الصفحة لكي يعمل بشكل صحيح.

  3. صورة الخروج المتحركة: تتألف من الصورة المتحركة التي تخفي شاشة البداية. إذا أردت تخصيصه، استخدِم رمز SplashScreenView و رمزه. يمكنك تشغيل أيّ تأثير متحرّك عليها، مع إعدادات للتحويل والشفافية واللون. في هذه الحالة، عليك إزالة شاشة البداية يدويًا عند انتهاء المَعلمة.

عند تشغيل الصورة المتحركة للرمز، يمنحك إطلاق التطبيق خيار تخطّي السلسلة في الحالات التي يكون فيها التطبيق جاهزًا في وقت أبكر. يشغِّل التطبيق onResume() أو تنتهي مهلة شاشة البداية تلقائيًا، لذا تأكَّد من إمكانية تخطّي الصور المتحرّكة بشكل مريح. يجب إغلاق شاشة البداية باستخدام onResume() فقط عندما يكون التطبيق ثابتًا من الناحية المرئية، وبالتالي لا يلزم استخدام مؤشرات الدوران الإضافية. قد يكون تقديم واجهة غير مكتملة مزعجًا للمستخدمين وقد يؤدي إلى ترك انطباع بعدم الوضوح أو عدم الدقة.

متطلبات الصور المتحركة في شاشة البداية

يجب أن تلتزم شاشة البداية بالمواصفات التالية:

  • اضبط لون خلفية نافذة واحدة بدون شفافية. يتوفّر وضعان مختلفان ليلاً ونهارًا عند استخدام SplashScreen مكتبة التوافق.

  • تأكَّد من أنّ الرمز المتحرك يستوفي المواصفات التالية:

    • التنسيق: يجب أن يكون الرمز ملف XML لملف AnimatedVectorDrawable (AVD).
    • الأبعاد: يجب أن يكون حجم رمز AVD أكبر بأربعة أضعاف من حجم رمز المتوافق، على النحو التالي:
      • يجب أن تكون مساحة الرمز 432 نقطة شاشة، أي أربعة أضعاف مساحة 108 نقطة شاشة لرمز قابل للتكيّف غير مُعرَّف.
      • يظهر الثلثان الداخليان من الصورة في رمز مشغِّل التطبيقات، ويجب أن يكونا بحجم 288 نقطة كثافة، أي أربعة أضعاف الحجم 72 نقطة كثافة الذي يشكّل المنطقة الداخلية المخفية للرمز التكيُّفي.
    • المدة: ننصح بعدم تجاوز 1,000 ملي ثانية على الهواتف. يمكنك استخدام بدء متأخر، ولكن لا يمكن أن تزيد مدته عن 166 مللي ثانية. إذا كان وقت بدء التطبيق أطول من 1,000 مللي ثانية، ننصحك باستخدام صورة متحركة مكرّرة.
  • حدِّد وقتًا مناسبًا لإغلاق شاشة البداية، وذلك عندما يرسم تطبيقك اللقطة الأولى. يمكنك تخصيص هذه الإعدادات بشكل أكبر كما هو موضّح في القسم المعنيّ بإبقاء شاشة البداية معروضة على الشاشة لفترات أطول.

موارد شاشة البداية

الشكل 5. مثال على AVD

نزِّل مثال على حزمة البدء، التي توضّح كيفية إنشاء صورة متحركة وتنسيقها وتصديرها إلى ملف AVD. ويشمل ذلك ما يلي:

  • ملف مشروع Adobe After Effects للصورة المتحركة
  • ملف AVD XML النهائي الذي تم تصديره
  • مثال على صورة GIF متحركة للصورة

يعني تنزيل هذه الملفات موافقتك على بنود خدمة Google.

توضّح سياسة خصوصية Google كيفية معالجة البيانات في هذه الخدمة.

تخصيص شاشة البداية في تطبيقك

بشكلٍ تلقائي، يستخدم SplashScreen windowBackground من تصميمك إذا كان windowBackground لونًا واحدًا. لتخصيص شاشة البداية، أضِف سمات إلى مظهر التطبيق.

يمكنك تخصيص شاشة البداية لتطبيقك من خلال إجراء أيّ مما يلي:

  • اضبط سمات المظهر لتغيير مظهره.

  • اعرضه على الشاشة لفترة أطول.

  • تخصيص الحركة لإغلاق شاشة البداية

البدء

توفّر مكتبة SplashScreen الأساسية شاشة البداية لنظام التشغيل Android 12 على جميع الأجهزة التي تعمل بالإصدار 23 من واجهة برمجة التطبيقات. لإضافته إلى مشروعك، أضِف المقتطف التالي إلىملف build.gradle:

رائع

dependencies {
    implementation "androidx.core:core-splashscreen:1.0.0"
}

Kotlin

dependencies {
    implementation("androidx.core:core-splashscreen:1.0.0")
}

ضبط مظهر شاشة البداية

يمكنك تحديد السمات التالية في موضوع Activity لتخصيص شاشة البداية لتطبيقك. إذا كان لديك تنفيذ شاشة بداية قديم يستخدم سمات مثل android:windowBackground، ننصحك بتوفير ملف موارد بديل لنظام التشغيل Android 12 والإصدارات الأحدث.

  1. استخدِم رمز windowSplashScreenBackground لملء الخلفية بلون واحد محدّد:

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. استخدِم رمز windowSplashScreenAnimatedIcon لاستبدال الرمز في وسط نافذة البدء.

    بالنسبة إلى التطبيقات التي تستهدف الإصدار 12 من Android (المستوى 32 لواجهة برمجة التطبيقات) فقط، عليك اتّباع الخطوات التالية:

    إذا كان العنصر قابلاً للتحريك والرسم من خلال AnimationDrawable وAnimatedVectorDrawable، اضبط windowSplashScreenAnimationDuration على تشغيل الصورة المتحركة أثناء عرض النافذة الافتتاحية. ليس هذا مطلوبًا لنظام التشغيل Android 13، لأنّ المدة يتم استنتاجها مباشرةً من AnimatedVectorDrawable.

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. استخدِم windowSplashScreenAnimationDuration للإشارة إلى مدة تأثير رمز شاشة البداية. لا يؤثر ضبط هذا الإعداد في الوقت الفعلي الذي يتم خلاله عرض الشاشة التمهيدية ، ولكن يمكنك استرجاعه عند تخصيص المَعلمة SplashScreenView.getIconAnimationDuration لحركة الخروج من الشاشة التمهيدية. اطّلِع على القسم التالي حول إبقاء شاشة البداية معروضة على الشاشة لفترات أطول لمزيد من التفاصيل.

    <item name="android:windowSplashScreenAnimationDuration">1000</item>
    
  4. استخدِم windowSplashScreenIconBackgroundColor لضبط خلفية خلف رمز شاشة البداية. يكون ذلك مفيدًا إذا كان هناك اختلاف غير كافٍ بين خلفية النافذة والرمز.

    <item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>
    
  5. يمكنك استخدام رمز windowSplashScreenBrandingImage لضبط صورة يتم عرضها في أسفل شاشة البداية. ومع ذلك، تنصح إرشادات التصميم بعدم استخدام صورة علامة تجارية.

    <item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
    
  6. يمكنك استخدام القيمة windowSplashScreenBehavior لتحديد ما إذا كان تطبيقك يعرض الرمز دائمًا على شاشة البداية في Android 13 والإصدارات الأحدث. القيمة التلقائية هي 0، ما يؤدي إلى عرض الرمز على شاشة البداية إذا ضبط نشاط الإطلاق القيمة splashScreenStyle على SPLASH_SCREEN_STYLE_ICON، أو اتّباع سلوك النظام إذا لم يحدّد نشاط الإطلاق أسلوبًا. إذا كنت تفضّل عدم عرض شاشة بداية فارغة أبدًا وكنت تريد دائمًا عرض الرمز المتحرك، اضبط هذه القيمة على icon_preferred.

    <item name="android:windowSplashScreenBehavior">icon_preferred</item>
    

إبقاء شاشة البداية معروضة على الشاشة لفترات أطول

يتم إغلاق شاشة البداية فور رسم تطبيقك للإطار الأول. إذا كنت بحاجة إلى تحميل قدر صغير من البيانات، مثل تحميل الإعدادات داخل التطبيق من ملف على القرص المحلي بشكل غير متزامن، يمكنك استخدام ViewTreeObserver.OnPreDrawListener لتعليق التطبيق لرسم أول إطار له.

إذا انتهى نشاط البدء قبل الرسم، على سبيل المثال، من خلال عدم ضبط عرض المحتوى والانتهاء قبل onResume، لن يكون مستمع pre-draw ضروريًا.

Kotlin

// 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 {
                // Check whether the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready. Start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content isn't ready. Suspend.
                    false
                }
            }
        }
    )
}

Java

// Create a new event for the activity.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the layout for the content view.
    setContentView(R.layout.main_activity);

    // Set up an OnPreDrawListener to the root view.
    final View content = findViewById(android.R.id.content);
    content.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Check whether the initial data is ready.
                    if (mViewModel.isReady()) {
                        // The content is ready. Start drawing.
                        content.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    } else {
                        // The content isn't ready. Suspend.
                        return false;
                    }
                }
            });
}

تخصيص الرسوم المتحركة لإغلاق شاشة البداية

يمكنك تخصيص الصورة المتحركة لشاشة البداية من خلال Activity.getSplashScreen().

Kotlin

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()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    // Add a callback that's called when the splash screen is animating to the
    // app content.
    getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                splashScreenView.remove();
            }
        });

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

عند بدء هذا المرجع المرجعي، يبدأ العنصر المرسوم المتجه المتحرك على شاشة البداية. استنادًا إلى مدة تشغيل التطبيق، قد يكون العنصر drawable في منتصف الرسوم المتحركة. استخدِم رمز SplashScreenView.getIconAnimationStart لمعرفة وقت بدء الصورة المتحركة. يمكنك احتساب المدة المتبقية من التحريك المرئي للرمز على النحو التالي:

Kotlin

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDuration
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationStart
// Calculate the remaining duration of the animation.
val remainingDuration = if (animationDuration != null && animationStart != null) {
    (animationDuration - Duration.between(animationStart, Instant.now()))
        .toMillis()
        .coerceAtLeast(0L)
} else {
    0L
}

Java

// Get the duration of the animated vector drawable.
Duration animationDuration = splashScreenView.getIconAnimationDuration();
// Get the start time of the animation.
Instant animationStart = splashScreenView.getIconAnimationStart();
// Calculate the remaining duration of the animation.
long remainingDuration;
if (animationDuration != null && animationStart != null) {
    remainingDuration = animationDuration.minus(
            Duration.between(animationStart, Instant.now())
    ).toMillis();
    remainingDuration = Math.max(remainingDuration, 0L);
} else {
    remainingDuration = 0L;
}

مصادر إضافية