Bağımlılığı ekleme
Media3 kitaplığı, Jetpack Compose tabanlı bir kullanıcı arayüzü modülü içerir. Bu özelliği kullanmak için aşağıdaki bağımlılığı ekleyin:
Kotlin
implementation("androidx.media3:media3-ui-compose:1.8.0")
Groovy
implementation "androidx.media3:media3-ui-compose:1.8.0"
Uygulamanızı Compose'u öncelikli tutarak geliştirmenizi veya View'ları kullanmaktan geçiş yapmanızı önemle tavsiye ederiz.
Tamamen Compose ile geliştirilmiş demo uygulaması
media3-ui-compose kitaplığı, kullanıma hazır Composables (ör. düğmeler, göstergeler, resimler veya iletişim kutuları) içermese de PlayerView öğesini AndroidView içine sarmalama gibi birlikte çalışabilirlik çözümlerinden kaçınan, tamamen Compose ile yazılmış bir demo uygulaması bulabilirsiniz. Demo uygulaması, media3-ui-compose modülündeki kullanıcı arayüzü durumu tutucu sınıflarını kullanır ve Compose Material3 kitaplığından yararlanır.
Kullanıcı arayüzü durum koruyucuları
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ı
Bazı kullanıcı arayüzü durumlarında, bunların büyük olasılıkla düğme benzeri Composables tarafından kullanılacağı varsayılır.
| 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:
@Composable
fun PlayPauseButton(player: Player, modifier: Modifier = Modifier) {
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),
)
}
}
state öğesinin, oynatma veya duraklatma için kullanılacak simge gibi tema bilgisi içermediğini unutmayın. Tek sorumluluğu, Player öğesini kullanıcı arayüzü durumuna dönüştürmektir.
Ardından, tercih ettiğiniz düzende düğmeleri karıştırıp eşleştirebilirsiniz:
Row(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
PreviousButton(player)
PlayPauseButton(player)
NextButton(player)
}
Görsel çıkış durum koruyucuları
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.
val presentationState = rememberPresentationState(player)
val scaledModifier = Modifier.resizeWithContentScale(ContentScale.Fit, presentationState.videoSizeDp)
Box(modifier) {
// 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 = player,
surfaceType = SURFACE_TYPE_SURFACE_VIEW,
modifier = scaledModifier,
)
if (presentationState.coverSurface) {
// Cover the surface that is being prepared with a shutter
Box(Modifier.background(Color.Black))
}
Burada, hem presentationState.videoSizeDp ile yüzeyi istenen en-boy oranına ölçekleyebiliriz (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ığını anlayabiliriz. Bu durumda, yüzeyin üzerine opak bir deklanşör yerleştirebilirsiniz. Bu deklanşör, yüzey hazır olduğunda kaybolur.
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 yazarak Flow<Player.Events> elde edebilirsiniz. Bu filter istediğiniz şekilde kullanabilirsiniz.
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. Deseni kopyalamak için mevcut durumun kaynak kodunu inceleyin. Tipik bir kullanıcı arayüzü durumu tutucu sınıfı şunları yapar:
Playeralır.- Coroutine'ları kullanarak
Playerdizisine abone olur. Daha fazla bilgi içinPlayer.listenbölümüne bakın. - İç durumunu güncelleyerek belirli
Player.Eventsyanıt verir. - Uygun bir
Playergüncellemesine dönüştürülecek iş mantığı komutlarını kabul edin. - 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, suspend fun olan ve eş yordam dünyasına girmenize ve Player.Events'yi süresiz olarak dinlemenize olanak tanıyan bir işlevdir. Ç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 kalmaz.