media3-ui-compose kitaplığı, Jetpack Compose'da medya kullanıcı arayüzü oluşturmak için temel bileşenleri sağlar. media3-ui-compose-material3 kitaplığının sunduğundan daha fazla özelleştirme isteyen geliştiriciler için tasarlanmıştır. Bu sayfada, özel bir medya oynatıcı kullanıcı arayüzü oluşturmak için temel bileşenlerin ve durum tutucuların nasıl kullanılacağı açıklanmaktadır.
Material3 ve özel Compose bileşenlerini karıştırma
media3-ui-compose-material3 kitaplığı esnek olacak şekilde tasarlanmıştır. Kullanıcı arayüzünüzün çoğu için önceden oluşturulmuş bileşenleri kullanabilirsiniz ancak daha fazla kontrole ihtiyacınız olduğunda tek bir bileşeni özel bir uygulamayla değiştirebilirsiniz. Bu noktada media3-ui-compose kitaplığı devreye girer.
Örneğin, Material3 kitaplığındaki standart PreviousButton ve NextButton öğelerini kullanmak istediğinizi ancak tamamen özel bir PlayPauseButton öğesine ihtiyacınız olduğunu varsayalım. Bunu, temel media3-ui-compose kitaplığındaki PlayPauseButton öğesini kullanarak ve önceden oluşturulmuş bileşenlerin yanına yerleştirerek yapabilirsiniz.
Row { // Use prebuilt component from the Media3 UI Compose Material3 library PreviousButton(player) // Use the scaffold component from Media3 UI Compose library PlayPauseButton(player) { // `this` is PlayPauseButtonState FilledTonalButton( onClick = { Log.d("PlayPauseButton", "Clicking on play-pause button") this.onClick() }, enabled = this.isEnabled, ) { Icon( imageVector = if (showPlay) Icons.Default.PlayArrow else Icons.Default.Pause, contentDescription = if (showPlay) "Play" else "Pause", ) } } // Use prebuilt component from the Media3 UI Compose Material3 library NextButton(player) }
Kullanılabilir bileşenler
media3-ui-compose kitaplığı, yaygın oynatıcı kontrolleri için önceden oluşturulmuş bir dizi composable sağlar. Uygulamanızda doğrudan kullanabileceğiniz bileşenlerden bazıları şunlardır:
| Bileşen | Açıklama |
|---|---|
PlayPauseButton |
Oynatma ve duraklatma arasında geçiş yapan bir düğme için durum kapsayıcısı. |
SeekBackButton |
Tanımlanmış bir artışla geriye doğru arama yapan bir düğme için durum kapsayıcısı. |
SeekForwardButton |
Belirli bir artışla ileriye doğru arama yapan bir düğme için durum kapsayıcısı. |
NextButton |
Sonraki medya öğesine gitmeyi sağlayan bir düğme için durum kapsayıcısı. |
PreviousButton |
Önceki medya öğesine gitmek için kullanılan bir düğmenin durum kapsayıcısı. |
RepeatButton |
Tekrarlama modları arasında geçiş yapan bir düğme için durum kapsayıcısı. |
ShuffleButton |
Karıştırma modunu açıp kapatan bir düğme için durum kapsayıcısı. |
MuteButton |
Oynatıcıyı sessize alan ve sessizliğini kaldıran bir düğme için durum kapsayıcısı. |
TimeText |
Oyuncunun ilerleme durumunu gösteren composable için durum kapsayıcısı. |
ContentFrame |
En boy oranı yönetimi, yeniden boyutlandırma ve deklanşör işlevlerini yerine getiren, medya içeriklerini görüntülemeye yarayan bir yüzey |
PlayerSurface |
SurfaceView ve TextureView öğelerini AndroidView içinde sarmalayan ham yüzey. |
Player composable'ı özelleştirme
media3-ui-compose-material3 kitaplığı kullanılırken Player composable'ı, eksiksiz bir medya oynatma deneyimi sunar. İçerik yuvalarına kendi composable'larınızı sağlayarak düzenini özelleştirebilir veya PlayerDefaults nesnesini kullanarak kullanıcı arayüzünün belirli bölümlerini (ör. TopControls, CenterControls, BottomControls ve ErrorOverlay) özelleştirebilirsiniz.
Örneğin, PlayerDefaults.CenterControls kullanarak yalnızca merkezi oynatma/duraklatma düğmesini geçersiz kılabilir ve diğer merkezi kontrolleri olduğu gibi bırakabilirsiniz.
Ayrıca, topControls gibi bir parametre için tamamen özel bir composable iletebilir ve bottomControls gibi diğerlerini varsayılan değerlerinde bırakmak için tamamen atlayabilirsiniz.
@Composable fun CustomPlayerSlots(player: Player, modifier: Modifier = Modifier) { Player( player = player, modifier = modifier, topControls = { p, visible -> // Fully custom top controls AnimatedVisibility(visible) { Text("My custom title") } }, centerControls = { p, visible -> // Use default CenterControls but override the central button PlayerDefaults.CenterControls( player = p, visible = visible, central = { // A custom play/pause button Material3PlayPauseButton(it, modifier = Modifier.size(64.dp)) }, ) }, // bottomControls are left as default ) }
Kullanıcı arayüzü durumu depolayıcıları
İskele bileşenlerinden hiçbiri ihtiyaçlarınızı karşılamıyorsa durum nesnelerini doğrudan da kullanabilirsiniz. Genel olarak, yeniden oluşturmalar arasında kullanıcı arayüzünüzün görünümünü korumak için ilgili remember yöntemlerini kullanmanız önerilir.
UI durumu tutucularının esnekliğini composable'lara kıyasla nasıl kullanabileceğinizi daha iyi anlamak için Compose'un durumu nasıl yönettiği hakkında bilgi edinin.
Düğme durumu tutucuları
Kitaplık, bazı kullanıcı arayüzü durumlarında bunların büyük olasılıkla düğme benzeri composable'lar tarafından kullanılacağı varsayımını yapar.
| Eyalet | hatırla*Durum | Tür |
|---|---|---|
PlayPauseButtonState |
rememberPlayPauseButtonState |
2-Toggle |
PreviousButtonState |
rememberPreviousButtonState |
Sabit |
NextButtonState |
rememberNextButtonState |
Sabit |
RepeatButtonState |
rememberRepeatButtonState |
3-Toggle |
ShuffleButtonState |
rememberShuffleButtonState |
2-Toggle |
PlaybackSpeedState |
rememberPlaybackSpeedState |
Menü veya N-Toggle |
PlayPauseButtonState için örnek kullanım:
val state = rememberPlayPauseButtonState(player) IconButton(onClick = state::onClick, modifier = modifier, enabled = state.isEnabled) { Icon( imageVector = if (state.showPlay) Icons.Default.PlayArrow else Icons.Default.Pause, contentDescription = if (state.showPlay) stringResource(R.string.playpause_button_play) else stringResource(R.string.playpause_button_pause), ) }
Görsel çıkış durumu bilgisi depolayıcıları
CurrentMediaItemState, o anda oynatılan MediaItem hakkında bilgi verirken PlaylistState, oynatıcıda ayarlanan MediaItems hakkında bilgi verir. Bunlar, meta veri bilgilerini özel kullanıcı arayüzünüzde göstermek için kullanışlıdır.
ErrorState, oynatıcının mevcut hata durumu hakkında bilgi sağlar. Bu bilgiler, hata yer paylaşımı göstermek için kullanılabilir.
PresentationState, PlayerSurface içindeki video çıkışının ne zaman gösterilebileceği veya yer tutucu bir kullanıcı arayüzü öğesiyle ne zaman kapatılması gerektiğiyle ilgili bilgileri tutar.
ContentFrame composable, en boy oranı işlemeyi henüz hazır olmayan bir yüzeyde deklanşörü göstermeyi ele alarak birleştirir.
@Composable fun ContentFrame( player: Player?, modifier: Modifier = Modifier, surfaceType: @SurfaceType Int = SURFACE_TYPE_SURFACE_VIEW, contentScale: ContentScale = ContentScale.Fit, keepContentOnReset: Boolean = false, shutter: @Composable () -> Unit = { Box(Modifier.fillMaxSize().background(Color.Black)) }, ) { val presentationState = rememberPresentationState(player, keepContentOnReset) val scaledModifier = modifier.resizeWithContentScale(contentScale, presentationState.videoAspectRatio) // Always leave PlayerSurface to be part of the Compose tree because it will be initialized in // the process. If this composable is guarded by some condition, it might never become visible // because the Player won't emit the relevant event, e.g. the first frame being ready. PlayerSurface(player, scaledModifier, surfaceType) if (presentationState.coverSurface) { // Cover the surface that is being prepared with a shutter shutter() } }
Burada, hem presentationState.videoAspectRatio ile yüzeyi seçilen en-boy oranına göre ölçeklendirebiliriz (daha fazla tür için İçerik ölçeği bölümüne bakın) hem de presentationState.coverSurface ile yüzeyin gösterilmesi için zamanlamanın uygun olmadığı anları belirleyebiliriz. Bu durumda, yüzeyin üzerine opak bir örtü yerleştirebilirsiniz. Bu örtü, yüzey hazır olduğunda kaybolur.
ContentFrame, deklanşörü sondaki bir lambda olarak özelleştirmenize olanak tanır ancak varsayılan olarak üst kapsayıcının boyutunu dolduran siyah bir @Composable Box olur.
Flows nerede bulunur?
Birçok Android geliştirici, sürekli değişen kullanıcı arayüzü verilerini toplamak için Kotlin Flow nesnelerini kullanmaya alışkındır. Örneğin, yaşam döngüsüne duyarlı bir şekilde collect yapabileceğiniz bir Player.isPlaying akışı arıyor olabilirsiniz. Veya Player.eventsFlow gibi bir şey, Flow<Player.Events> sağlamak için filter istediğiniz şekilde.
Ancak Player kullanıcı arayüzü durumu için akış kullanmanın bazı dezavantajları vardır. Temel endişelerden biri, veri aktarımının eşzamansız olmasıdır. Player.Event ile kullanıcı arayüzü tarafındaki tüketimi arasında mümkün olduğunca az gecikme olmasını ve Player ile senkronize olmayan kullanıcı arayüzü öğelerinin gösterilmemesini istiyoruz.
Diğer noktalar:
- Tüm
Player.Events'lerin bulunduğu bir akış, tek bir sorumluluk ilkesine uymaz. Her tüketicinin ilgili etkinlikleri filtrelemesi gerekir. - Her
Player.Eventiçin akış oluşturmak, bunları her kullanıcı arayüzü öğesi için birleştirmenizi (combineile) gerektirir. Player.Event ile kullanıcı arayüzü öğesi değişikliği arasında çoktan çoğa eşleme vardır.combinekullanmak zorunda kalmak, kullanıcı arayüzünün yasa dışı olabilecek durumlara girmesine neden olabilir.
Özel kullanıcı arayüzü durumları oluşturma
Mevcut kullanıcı arayüzü durumları ihtiyaçlarınızı karşılamıyorsa özel kullanıcı arayüzü durumları ekleyebilirsiniz. Mevcut durumun kaynak kodunu inceleyerek kalıbı kopyalayın. Tipik bir kullanıcı arayüzü durumu tutucu sınıfı şunları yapar:
Playernesnesi alır.- Coroutine'ları kullanarak
Playeröğesine abone olur. Daha fazla ayrıntı içinPlayer.listensayfasına bakın. - İç durumunu güncelleyerek belirli
Player.Eventsyanıt verir. - Uygun bir
Playergüncellemesine dönüştürülecek iş mantığı komutlarını kabul eder. - Kullanıcı arayüzü ağacının birden fazla yerinde oluşturulabilir ve her zaman oyuncunun durumunun tutarlı bir görünümünü korur.
- Değişikliklere dinamik olarak yanıt vermek için bir composable tarafından kullanılabilen Compose
Statealanlarını kullanıma sunar. - Kompozisyonlar arasındaki örneği hatırlamak için
remember*Stateişleviyle birlikte gelir.
Perde arkasında neler olur?
class SomeButtonState(private val player: Player) { var isEnabled by mutableStateOf(player.isCommandAvailable(COMMAND_ACTION_A)) private set var someFieldValue by mutableStateOf(someFieldDefault) private set fun onClick() { player.actionA() } suspend fun observe(): Nothing = player.listen { events -> if (events.containsAny(EVENT_B_CHANGED, EVENT_C_CHANGED, EVENT_AVAILABLE_COMMANDS_CHANGED)) { someFieldValue = this.someField isEnabled = this.isCommandAvailable(COMMAND_ACTION_A) } } }
Kendi Player.Events'nize tepki vermek için Player.listen kullanarak onları yakalayabilirsiniz. Player.listen, suspend fun olan ve eş yordam dünyasına girmenize ve Player.Events'yi süresiz olarak dinlemenize olanak tanıyan bir kitaplıktır. Çeşitli kullanıcı arayüzü durumlarının Media3'te uygulanması, son geliştiricinin Player.Events hakkında bilgi edinmesiyle ilgili endişelenmesine gerek kalmamasını sağlar.