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 yapması gereken 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. İşte 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 ileri sarmayı sağlayan 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 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 |
AndroidView içinde SurfaceView ve TextureView öğelerini saran ham yüzey. |
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 state holder'ların esnekliğini Composables'a 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 depolayıcıları
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ğı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ış durumu bilgisi depolayıcıları
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.videoSizeDp) // Always leave PlayerSurface to be part of the Compose tree because it will be initialised 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.videoSizeDp ile yüzeyi seçilen en-boy oranına göre ölçeklendirebiliriz (daha fazla tür için ContentScale belgelerine bakın) hem de presentationState.coverSurface ile yüzeyin gösterilmesi için zamanlamanın uygun olmadığı anları öğrenebiliriz. 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 Player.isPlaying akışını arıyor olabilirsiniz. Veya Player.eventsFlow gibi bir şey yazarak Flow<Player.Events> elde edebilirsiniz. Bu Flow<Player.Events>, istediğiniz şekilde filter yapmanıza olanak tanır.
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ı istiyoruz. Bu nedenle, Player ile senkronize olmayan kullanıcı arayüzü öğelerini göstermiyoruz.
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:
Playeralı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. - Besteler arasında ö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(Player.COMMAND_ACTION_A))
private set
var someField by mutableStateOf(someFieldDefault)
private set
fun onClick() {
player.actionA()
}
suspend fun observe() =
player.listen { events ->
if (
events.containsAny(
Player.EVENT_B_CHANGED,
Player.EVENT_C_CHANGED,
Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
)
) {
someField = this.someField
isEnabled = this.isCommandAvailable(Player.COMMAND_ACTION_A)
}
}
}
Kendi Player.Events'nize tepki vermek için Player.listen'ı kullanarak onları yakalayabilirsiniz. Player.listen, sizi coroutine dünyasına sokan ve Player.Events'ı süresiz olarak dinlemenize olanak tanıyan bir suspend fun'dir. Çeşitli kullanıcı arayüzü durumlarının Media3'te uygulanması, son geliştiricinin Player.Events hakkında bilgi edinmesiyle ilgilenmemesine yardımcı olur.