Pencere içinde pencere (PIP), çoğunlukla aşağıdakilerden hangisi için kullanılan özel bir çoklu pencere modu türüdür: Video oynatma. Kullanıcının bir videoyu sabitlenmiş küçük bir pencerede izlemesini sağlar. uygulamalar arasında gezinirken veya ana ekrana dokunun.
PiP, sabitlenmiş video yer paylaşımı penceresini sağlamak için Android 7.0'da kullanıma sunulan çok pencereli API'lerden yararlanır. Uygulamanıza PIP eklemek için açın, etkinliğinizi gerektiği şekilde PiP moduna geçirin ve kullanıcı arayüzü öğelerinin etkinlik PiP modundayken video oynatmaya devam eder.
Bu kılavuzda, Compose'da PiP özelliğini bir Compose videosuyla uygulamanıza nasıl ekleyeceğiniz açıklanmaktadır. bazı ipuçları vereceğim. En iyilerini görmek için Socialite uygulamasına göz atın bazı ipuçları vereceğim.
Uygulamanızı PiP için ayarlama
AndroidManifest.xml
dosyanızın etkinlik etiketinde aşağıdakileri yapın:
supportsPictureInPicture
adlı oyuncuyu ekleyiptrue
olarak ayarlayarak en iyi uygulamaları paylaşacağız.configChanges
ekle ve şu şekilde ayarla belirtmek içinorientation|screenLayout|screenSize|smallestScreenSize
etkinliğiniz düzen yapılandırma değişikliklerini nasıl işler? Böylece etkinliğiniz PiP modu geçişleri sırasında düzen değişiklikleri olduğunda yeniden başlatılmıyor.<activity android:name=".SnippetsActivity" android:exported="true" android:supportsPictureInPicture="true" android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize" android:theme="@style/Theme.Snippets">
Oluşturma kodunuzda aşağıdakileri yapın:
- Bu uzantıyı
Context
sitesinde ekleyin. Etkinliğe erişmek için rehber boyunca bu uzantıyı birden çok kez kullanacaksınız.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 12 öncesi için izinde PiP uygulamasını ekleyin
Android 12 öncesi sürümlerde PIP eklemek için addOnUserLeaveHintProvider
uygulamasını kullanın. Takip et
aşağıdaki adımları uygulayarak Android 12 öncesi için PiP ekleyebilirsiniz:
- Bu koda yalnızca O sürümlerinden R tarihine kadar erişilebilmesi için bir sürüm kapısı ekleyin.
- Anahtar olarak
Context
içeren birDisposableEffect
kullanın. DisposableEffect
içindeonUserLeaveHintProvider
bir lambda kullanılarak tetiklendi. Lambda'da şunu ara:enterPictureInPictureMode()
(findActivity()
) ve geçip geçmePictureInPictureParams.Builder().build()
.findActivity()
kullanarakaddOnUserLeaveHintListener
ekleyip lambda'yı geçin.onDispose
uygulamasındafindActivity()
ileremoveOnUserLeaveHintListener
ekleyin ve lambda'dan iniyoruz.
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: () -> Unit = { 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 sonrası için izin verilen uygulamaya PiP'yi ekleme
Android 12'den sonra PictureInPictureParams.Builder
,
değiştiricisi kullanabilirsiniz.
- Bir
modifier
oluşturuponGloballyPositioned
'i çağırın. Düzen koordinatlar sonraki bir adımda kullanılacaktır. PictureInPictureParams.Builder()
için bir değişken oluşturun.- SDK'nın S veya daha yeni bir sürüm olup olmadığını kontrol etmek için bir
if
beyanı ekleyin. Varsa PiP'ye girmek için oluşturucuyasetAutoEnterEnabled
vetrue
olarak ayarlayın devam edebilir. Bu yöntem, ekran görüntüsüne kıyasla daha akıcı bir animasyon sağlar.enterPictureInPictureMode
. setPictureInPictureParams()
numaralı telefonu aramak içinfindActivity()
numarasını kullanın.builder
'dabuild()
'yi arayın ve bu numarayı iletin.
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)
Tek düğmeyle PIP ekleyin
PiP moduna düğme tıklamasıyla girmek için findActivity()
'da enterPictureInPictureMode()
'ı arayın.
Parametreler, PictureInPictureParams.Builder
için yapılan önceki çağrılarla zaten ayarlanmıştır. Bu nedenle, oluşturucuda yeni parametreler ayarlamanıza gerek yoktur. Ancak, düğmedeki parametreleri değiştirmek isterseniz
buradan ayarlayabilirsiniz.
val context = LocalContext.current Button(onClick = { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.findActivity().enterPictureInPictureMode( PictureInPictureParams.Builder().build() ) } else { Log.i(PIP_TAG, "API does not support PiP") } }) { Text(text = "Enter PiP mode!") }
PiP modunda kullanıcı arayüzünü yönetme
PiP moduna girdiğinizde uygulamanızın kullanıcı arayüzünün tamamı PiP penceresine girer. kullanıcı arayüzünüzün PiP modunda nasıl ve bu modda görünmesi gerektiğini belirtin.
Öncelikle, uygulamanızın PiP modunda olup olmadığını öğrenmeniz gerekir. Tekliflerinizi otomatikleştirmek ve optimize etmek için
Bunu başarmak için OnPictureInPictureModeChangedProvider
.
Aşağıdaki kod, uygulamanızın PiP modunda olup olmadığını gösterir.
@Composable fun rememberIsInPipMode(): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val activity = LocalContext.current.findActivity() var pipMode by remember { mutableStateOf(activity.isInPictureInPictureMode) } DisposableEffect(activity) { val observer = Consumer<PictureInPictureModeChangedInfo> { info -> pipMode = info.isInPictureInPictureMode } activity.addOnPictureInPictureModeChangedListener( observer ) onDispose { activity.removeOnPictureInPictureModeChangedListener(observer) } } return pipMode } else { return false } }
Artık hangi kullanıcı arayüzü öğelerinin gösterileceğini değiştirmek için rememberIsInPipMode()
öğesini kullanabilirsiniz
Uygulama PiP moduna girdiğinde:
val inPipMode = rememberIsInPipMode() Column(modifier = modifier) { // This text will only show up when the app is not in PiP mode if (!inPipMode) { Text( text = "Picture in Picture", ) } VideoPlayer() }
Uygulamanızın doğru zamanlarda PiP moduna girdiğinden emin olun
Uygulamanız aşağıdaki durumlarda PiP moduna girmemelidir:
- Video durdurulmuş veya duraklatılmışsa.
- Uygulamanın video oynatıcıdan farklı bir sayfasındaysanız.
Uygulamanızın PiP moduna ne zaman gireceğini kontrol etmek için durumu izleyen bir değişken ekleyin
mutableStateOf
kullanarak video oynatıcının.
Videonun oynatılıp oynatılmamasına göre durumu değiştirme
Durumu, video oynatıcının oynayıp oynamadığına göre değiştirmek için video oynatıcıya bir dinleyici ekleyin. Oynatıcının durumuna göre durum değişkeninizin durumunu değiştirin çalıp çalmıyor mu?
player.addListener(object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { shouldEnterPipMode = isPlaying } })
Oynatıcının serbest olup olmamasına göre durumu değiştir
Oynatıcı serbest bırakıldığında durum değişkeninizi false
olarak ayarlayın:
fun releasePlayer() { shouldEnterPipMode = false }
PiP moduna girilip girilmediğini tanımlamak için durumu kullanın (Android 12'den önceki sürümler)
- PiP'yi 12 sürümünden önce eklemek için
DisposableEffect
kullanılır. Bu nedenle,newValue
durum değişkeniniz olarak ayarlayarakrememberUpdatedState
ile yeni bir değişken oluşturmanız gerekir. Bu işlem, güncellenmiş sürümünDisposableEffect
içinde kullanılmasını sağlar. OnUserLeaveHintListener
çalıştırıldığında davranışı tanımlayan lambdada tetiklendiğinde, durum değişkenine sahip birif
ifadesi ekleyin.enterPictureInPictureMode()
:val currentShouldEnterPipMode by rememberUpdatedState(newValue = shouldEnterPipMode) 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: () -> Unit = { if (currentShouldEnterPipMode) { 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") }
PiP moduna girilip girilmediğini tanımlamak için durum özelliğini kullanma (Android 12'den sonra)
Uygulamanızın yalnızca şunu girmesi için durum değişkeninizi setAutoEnterEnabled
öğesine iletin:
Doğru zamanda PiP modu:
val pipModifier = modifier.onGloballyPositioned { layoutCoordinates -> val builder = PictureInPictureParams.Builder() // Add autoEnterEnabled for versions S and up if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { builder.setAutoEnterEnabled(shouldEnterPipMode) } context.findActivity().setPictureInPictureParams(builder.build()) } VideoPlayer(pipModifier)
Akıcı bir animasyon uygulamak için setSourceRectHint
kullanın
setSourceRectHint
API, PiP'ye girmek için daha akıcı bir animasyon oluşturur.
yatırım yapmanız önemlidir. Bu özellik, Android 12 ve sonraki sürümlerde PiP modundan çıkmak için daha akıcı bir animasyon oluşturur.
PiP oluşturma aracına bu API'yi ekleyerek etkinliğin ait olduğu alanı
görünür olması gerekir.
setSourceRectHint()
öğesini yalnızcabuilder
öğesine PiP moduna geçmelidir. Bu sayede, uygulamanın PiP'ye girmesi gerekmediğindesourceRect
hesaplanması önlenir.sourceRect
değerini ayarlamak için, verilenlayoutCoordinates
kullanın. değiştiricidekionGloballyPositioned
işlevinden seçebilirsiniz.builder
üzerindesetSourceRectHint()
adlı kişiyi arayın vesourceRect
içinde geçiş yapın değişkenine eklenmelidir.
val context = LocalContext.current val pipModifier = modifier.onGloballyPositioned { layoutCoordinates -> val builder = PictureInPictureParams.Builder() if (shouldEnterPipMode) { val sourceRect = layoutCoordinates.boundsInWindow().toAndroidRectF().toRect() builder.setSourceRectHint(sourceRect) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { builder.setAutoEnterEnabled(shouldEnterPipMode) } context.findActivity().setPictureInPictureParams(builder.build()) } VideoPlayer(pipModifier)
PiP penceresinin en boy oranını ayarlamak için setAspectRatio
tuşunu kullanın
PiP penceresinin en boy oranını ayarlamak için belirli bir
veya oynatıcının video boyutunun genişliğini ve yüksekliğini kullanabilirsiniz. Şu durumda:
medya 3 oynatıcı kullanarak, oynatıcının boş olmadığından ve oynatıcının
en boy oranı ayarlanmadan önce video boyutu VideoSize.UNKNOWN
değerine eşit değil
oranı.
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)
Özel bir oynatıcı kullanıyorsanız en boy oranını oynatıcının yüksekliğinde ayarlayın oynatıcınıza özel söz dizimini kullanarak genişliği ayarlayın. Oynatıcınızın yalnızca yeniden boyutlandırır. Başlatma sırasında, en boy oranı olursa uygulamanız kilitlenir. Media3 oynatıcıda olduğu gibi, en boy oranının ne zaman hesaplanabileceğiyle ilgili kontroller eklemeniz gerekebilir.
Uzaktan işlem ekle
PiP pencerenize kontroller (oynatma, duraklatma vb.) eklemek istiyorsanız bir
Eklemek istediğiniz her kontrol için RemoteAction
.
- Yayın kontrolleriniz için sabit değerler ekleyin:
// Constant for broadcast receiver const val ACTION_BROADCAST_CONTROL = "broadcast_control" // Intent extras for broadcast controls from Picture-in-Picture mode. const val EXTRA_CONTROL_TYPE = "control_type" const val EXTRA_CONTROL_PLAY = 1 const val EXTRA_CONTROL_PAUSE = 2
- PiP pencerenizdeki kontroller için
RemoteActions
listesi oluşturun. - Daha sonra, bir
BroadcastReceiver
ekleyin veonReceive()
otomatik olarak kontrol edilir. BirDisposableEffect
kullanarak uzaktan yapılan işlemlerdir. Oynatıcı atıldığında, kaydı iptal et alıcı.@RequiresApi(Build.VERSION_CODES.O) @Composable fun PlayerBroadcastReceiver(player: Player?) { val isInPipMode = rememberIsInPipMode() if (!isInPipMode || player == null) { // Broadcast receiver is only used if app is in PiP mode and player is non null return } val context = LocalContext.current DisposableEffect(player) { val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if ((intent == null) || (intent.action != ACTION_BROADCAST_CONTROL)) { return } when (intent.getIntExtra(EXTRA_CONTROL_TYPE, 0)) { EXTRA_CONTROL_PAUSE -> player.pause() EXTRA_CONTROL_PLAY -> player.play() } } } ContextCompat.registerReceiver( context, broadcastReceiver, IntentFilter(ACTION_BROADCAST_CONTROL), ContextCompat.RECEIVER_NOT_EXPORTED ) onDispose { context.unregisterReceiver(broadcastReceiver) } } }
- Uzaktan gerçekleştirdiğiniz işlemlerin listesini
PictureInPictureParams.Builder
:val context = LocalContext.current val pipModifier = modifier.onGloballyPositioned { layoutCoordinates -> val builder = PictureInPictureParams.Builder() builder.setActions( listOfRemoteActions() ) 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(modifier = pipModifier)
Sonraki adımlar
Bu kılavuzda, Compose'da PIP eklemeyle ilgili en iyi uygulamaları Android 12 öncesi ve Android 12 sonrası sürümler.
- En iyi uygulamaları görmek için Socialite uygulamasına bakın PIP oluşturun.
- Daha fazla bilgi için PiP tasarım kılavuzuna göz atın.