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ştirmeye ihtiyaç duyan 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 bilgisi depolayıcıları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 kullanabilir, daha fazla kontrole ihtiyaç duyduğunuzda 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 bileşenlerini kullanmak istediğinizi ancak tamamen özel bir PlayPauseButton bileşenine ihtiyacınız olduğunu varsayalım. Bunu, temel media3-ui-compose kitaplığındaki PlayPauseButton bileşenini 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. Doğrudan uygulamanızda kullanabileceğiniz bileşenlerden bazıları şunlardır:
| Bileşen | Açıklama |
|---|---|
PlayPauseButton |
Oynatma ve duraklatma arasında geçiş yapan bir düğmenin durum kapsayıcısı. |
SeekBackButton |
Tanımlanmış bir artışla geri sarmayı sağlayan bir düğmenin durum kapsayıcısıdır. |
SeekForwardButton |
Belirli bir artışla ileri saran bir düğmenin durum kapsayıcısı. |
NextButton |
Sonraki medya öğesine saran bir düğme için durum kapsayıcısı. |
PreviousButton |
Önceki medya öğesine saran bir düğmenin durum kapsayıcısı. |
RepeatButton |
Tekrar modları arasında geçiş yapan bir düğme için durum kapsayıcısı. |
ShuffleButton |
Karışık modunu açıp kapatan bir düğme için durum kapsayıcısı. |
MuteButton |
Oynatıcının sesini kapatıp açan bir düğme için durum kapsayıcısı. |
TimeText |
Oynatıcının ilerleme durumunu gösteren bir composable için durum kapsayıcısı. |
ContentFrame |
En boy oranı yönetimini, yeniden boyutlandırmayı ve deklanşörü işleyen medya içeriğini görüntülemek için kullanılan bir yüzey |
PlayerSurface |
AndroidView içinde SurfaceView ve TextureView öğelerini sarmalayan ham yüzey. |
Player composable'ını özelleştirme
media3-ui-compose-material3 kitaplığını kullanırken Player composable'ı, eksiksiz bir medya oynatma deneyimi sunar. İçerik alanlarına kendi composable'larınızı ekleyerek düzenini özelleştirebilir veya kullanıcı arayüzünün TopControls, CenterControls, BottomControls ve ErrorOverlay gibi belirli bölümlerini özelleştirmek için PlayerDefaults nesnesinden yararlanabilirsiniz.
Şekil 1'de, Player composable'ının yuvalarını nereye yerleştirdiği gösterilmektedir. Şekil 2'de, katmanların çizilme sırası gösterilmektedir.
Player composable'ının alanları.
Ortadaki ve alttaki kontroller, PlayerDefaults.CenterControls ve PlayerDefaults.BottomControls için ayrılan alanları gösterir. topControls, above ve below yuvaları varsayılan olarak boştur.Player composable bileşeninin katmanları, çizildikleri sıraya göre numaralandırılır.Örneğin, PlayerDefaults.CenterControls kullanarak yalnızca ortadaki oynat/duraklat düğmesini geçersiz kılabilir ve diğer orta kontrolleri olduğu gibi bırakabilirsiniz.
topControls gibi bir parametre için tamamen özel bir composable da iletebilir ve bottomControls gibi diğerlerini tamamen atlayarak varsayılan değerlerinde bırakabilirsiniz.
@Composable fun CustomPlayerSlots(player: Player, showControls: Boolean, modifier: Modifier = Modifier) { Player( player = player, modifier = modifier, showControls = showControls, topControls = { // Fully custom top controls Text("My custom title") }, centerControls = { // Use default CenterControls but override the central button PlayerDefaults.CenterControls( player = player, central = { // A custom play/pause button Material3PlayPauseButton(player, modifier = Modifier.size(64.dp)) }, ) }, // bottomControls are left as default ) }
Kullanıcı arayüzü durum bilgisi depolayıcıları
İskelet bileşenlerinden hiçbiri ihtiyaçlarınızı karşılamıyorsa durum nesnelerini doğrudan da kullanabilirsiniz. Yeniden oluşturma işlemleri arasında kullanıcı arayüzü görünümünüzü korumak için genellikle ilgili remember yöntemlerini kullanmanız önerilir.
Kullanıcı arayüzü durum bilgisi depolayıcıları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 durum bilgisi depolayıcıları
Kitaplık, bazı kullanıcı arayüzü durumlarının büyük olasılıkla düğme benzeri composable'lar tarafından kullanılacağını varsayar.
| Eyalet | remember*State | 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ış durum bilgisi depolayıcıları
CurrentMediaItemState, oynatılmakta olan MediaItem hakkında bilgi verirken PlaylistState, oynatıcıda ayarlanan MediaItems hakkında bilgi verir. Bunlar, özel kullanıcı arayüzünüzde yayınlanan içerik meta verisi bilgilerini göstermek için kullanışlıdır.
ErrorState, oynatıcının mevcut hata durumu hakkında bilgi sağlar. Bu bilgi, hata yer paylaşımı görüntülemek için kullanılabilir.
PresentationState, PlayerSurface içindeki video çıkışının ne zaman gösterilebileceği veya yer tutucu kullanıcı arayüzü öğesiyle ne zaman kapatılması gerektiğiyle ilgili bilgileri içerir.
ContentFrame composable, en boy oranı işlemeyi henüz hazır olmayan bir yüzeyde deklanşörün gösterilmesiyle 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, Surface'ı seçilen en boy oranına ölçeklendirmek için presentationState.videoAspectRatio (daha fazla tür için İçerik ölçeği bölümüne bakın) ve Surface'ı göstermek için doğru zamanın ne zaman olmadığını öğrenmek için presentationState.coverSurface kullanabiliriz. Bu durumda, yüzeyin üzerine opak bir deklanşör yerleştirebilirsiniz. Yüzey hazır olduğunda bu deklanşör kaybolur.
ContentFrame, deklanşörü sondaki 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?
Birçok Android geliştirici, sürekli değişen kullanıcı arayüzü verilerini toplamak için Kotlin Flow nesnelerini kullanmaya aşinadır. Örneğin, yaşam döngüsüne duyarlı bir şekilde collect yapabileceğiniz bir Player.isPlaying akışı arıyor olabilirsiniz. Ya da Player.eventsFlow gibi bir şey kullanarak istediğiniz gibi filter yapabileceğiniz bir Flow<Player.Events> oluşturabilirsiniz.
Ancak Player kullanıcı arayüzü durumu için akışları kullanmanın bazı dezavantajları vardır. En önemli endişelerden biri, veri aktarımının eşzamansız olmasıdır. Player.Event ile kullanıcı arayüzü tarafında tüketimi arasında mümkün olduğunca az gecikme olmasını ve Player ile senkronize olmayan kullanıcı arayüzü öğelerinin gösterilmesini önlemek istiyoruz.
Diğer noktalar şunlardır:
- Tüm
Player.Eventsiçeren bir akış, tek sorumluluk ilkesine uymaz. Her tüketicinin ilgili etkinlikleri filtrelemesi gerekir. - Her
Player.Eventiçin bir akış oluşturmak, her kullanıcı arayüzü öğesi için bunları 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ü yasa dışı olabilecek durumlara yönlendirebilir.
Ö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ü durum bilgisi depolayıcı sınıfı şunları yapar:
Playernesnesi alır.- Eş yordamları kullanarak
Playerakışına abone olur. Daha fazla bilgi içinPlayer.listensayfasına bakın. - Dahili durumunu güncelleyerek belirli
Player.Eventsyanıt verir. - Uygun bir güncellemeye dönüştürülecek iş mantığı komutlarını kabul eder.
Player - Kullanıcı arayüzü ağacında birden fazla yerde oluşturulabilir ve her zaman oynatıcının durumunun tutarlı bir görünümünü korur.
- Değişikliklere dinamik olarak yanıt vermek için bir composable tarafından kullanılabilecek Compose
Statealanlarını gösterir. - Oluşturmalar arasında örneği hatırlamak için
remember*Stateişleviyle birlikte gelir.
Arka planda 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 akışınıza tepki vermek için Player.listen kullanarak akışı yakalayabilirsiniz.
Bu, eş yordam dünyasına girmenize ve Player.Events akışını süresiz olarak dinlemenize olanak tanıyan bir suspend fun işlevidir. Media3'ün çeşitli kullanıcı arayüzü durumlarını uygulaması, son geliştiricinin Player.Events hakkında bilgi edinme konusunda endişelenmemesine yardımcı olur.