הגדרת האפליקציה ל'תמונה בתוך תמונה'

בתג הפעילות של הקובץ AndroidManifest.xml, מבצעים את הפעולות הבאות:

  1. מוסיפים את supportsPictureInPicture ומגדירים אותו ל-true כדי להצהיר שתשתמשו בתכונה 'תמונה בתוך תמונה' (PiP) באפליקציה.
  2. מוסיפים את configChanges ומגדירים אותו לערך orientation|screenLayout|screenSize|smallestScreenSize כדי לציין שהפעילות מטפלת בשינויים בהגדרות הפריסה. כך הפעילות לא תופעל מחדש כשמתרחשים שינויים בפריסה במהלך מעברים למצב תמונה בתוך תמונה.
<activity
    android:name=".SnippetsActivity"
    android:exported="true"
    android:supportsPictureInPicture="true"
    android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
    android:theme="@style/Theme.Snippets">

בקוד של Compose, מבצעים את הפעולות הבאות:

  1. הוספה של התוסף הזה באתר Context. תצטרכו להשתמש בתוסף הזה כמה פעמים במהלך המדריך כדי לגשת לפעילות.
    internal fun Context.findActivity(): ComponentActivity {
        var context = this
        while (context is ContextWrapper) {
            if (context is ComponentActivity) return context
            context = context.baseContext
        }
        throw IllegalStateException("Picture in picture should be called in the context of an Activity")
    }

הוספת תמונה בתוך תמונה באפליקציית בקשת חופשה בגרסאות Android קודמות ל-Android 12

כדי להוסיף את מצב 'תמונה בתוך תמונה' בגרסאות Android שקדמו לגרסה 12, משתמשים בלחצן addOnUserLeaveHintProvider. כדי להוסיף את התכונה 'תמונה בתוך תמונה' בגרסאות Android קודמות לגרסה 12, פועלים לפי השלבים הבאים:

  1. מוסיפים שער גרסה כדי שהגישה לקוד הזה תתאפשר רק בגרסאות O עד R.
  2. שימוש ב-DisposableEffect עם Context כמפתח.
  3. בתוך DisposableEffect, מגדירים את ההתנהגות של onUserLeaveHintProvider כשמופעל באמצעות lambda. ב-lambda, קוראים ל-enterPictureInPictureMode() ב-findActivity() ומעבירים את PictureInPictureParams.Builder().build().
  4. מוסיפים addOnUserLeaveHintListener באמצעות findActivity() ומעבירים את ה-lambda.
  5. ב-onDispose, מוסיפים את removeOnUserLeaveHintListener באמצעות findActivity() ומעבירים את ה-lambda.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
    Build.VERSION.SDK_INT < Build.VERSION_CODES.S
) {
    val context = LocalContext.current
    DisposableEffect(context) {
        val onUserLeaveBehavior = Runnable {
            context.findActivity()
                .enterPictureInPictureMode(PictureInPictureParams.Builder().build())
        }
        context.findActivity().addOnUserLeaveHintListener(
            onUserLeaveBehavior
        )
        onDispose {
            context.findActivity().removeOnUserLeaveHintListener(
                onUserLeaveBehavior
            )
        }
    }
} else {
    Log.i("PiP info", "API does not support PiP")
}

הוספת תמונה בתוך תמונה באפליקציית בקשת חופשה בגרסאות Android מגרסה 12 ואילך

אחרי Android 12, התוסף PictureInPictureParams.Builder מתווסף באמצעות משנה שמועבר לנגן הווידאו של האפליקציה.

  1. יוצרים modifier ומתקשרים אל onGloballyPositioned. הקואורדינטות של הפריסה ישמשו בשלב מאוחר יותר.
  2. יוצרים משתנה ל-PictureInPictureParams.Builder().
  3. מוסיפים הצהרת if כדי לבדוק אם ה-SDK הוא S או גרסה מאוחרת יותר. אם כן, מוסיפים את setAutoEnterEnabled ל-builder ומגדירים אותו ל-true כדי להיכנס למצב תמונה בתוך תמונה בהחלקה. כך האנימציה חלקה יותר מאשר אם היא עוברת דרך enterPictureInPictureMode.
  4. שימוש ב-findActivity() כדי להתקשר אל setPictureInPictureParams(). מתקשרים למספר build() בטלפון builder ומעבירים את השיחה.

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(true)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}
VideoPlayer(pipModifier)

שימוש ב-setAspectRatio כדי להגדיר את יחס הגובה-רוחב של חלון התמונה בתוך תמונה

כדי להגדיר את יחס הגובה-רוחב של חלון התמונה בתוך תמונה, אפשר לבחור יחס גובה-רוחב ספציפי או להשתמש ברוחב ובגובה של גודל הסרטון בנגן. אם אתם משתמשים בנגן media3, צריך לוודא שהנגן לא ריק ושהגודל של הסרטון בנגן לא שווה ל-VideoSize.UNKNOWN לפני שמגדירים את יחס הגובה-רוחב.

val context = LocalContext.current

val pipModifier = modifier.onGloballyPositioned { layoutCoordinates ->
    val builder = PictureInPictureParams.Builder()
    if (shouldEnterPipMode && player != null && player.videoSize != VideoSize.UNKNOWN) {
        val sourceRect = layoutCoordinates.boundsInWindow().toAndroidRectF().toRect()
        builder.setSourceRectHint(sourceRect)
        builder.setAspectRatio(
            Rational(player.videoSize.width, player.videoSize.height)
        )
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        builder.setAutoEnterEnabled(shouldEnterPipMode)
    }
    context.findActivity().setPictureInPictureParams(builder.build())
}

VideoPlayer(pipModifier)

אם אתם משתמשים בנגן בהתאמה אישית, אתם צריכים להגדיר את יחס הגובה-רוחב של הנגן באמצעות התחביר שמתאים לנגן שלכם. שימו לב: אם הנגן משנה את הגודל שלו במהלך האתחול, והגודל החדש חורג מהגבולות התקפים של יחס הגובה-רוחב, האפליקציה תקרוס. יכול להיות שתצטרכו להוסיף בדיקות לגבי המקרים שבהם אפשר לחשב את יחס הגובה-רוחב, בדומה למה שקורה בנגן media3.