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.7.1")
Groovy
implementation "androidx.media3:media3-ui-compose:1.7.1"
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çinde 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.resize(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 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 doğru olmadığı zamanı öğrenebiliriz. 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 Player.isPlaying
akışını arıyor olabilirsiniz. Veya Player.eventsFlow
gibi bir şey yazarak Flow<Player.Events>
elde edebilirsiniz. Bu filter
, istediğiniz şekilde kullanabileceğiniz bir öğe olur.
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ı sağlamak ve Player
ile senkronize olmayan kullanıcı arayüzü öğelerini göstermemek 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.Event
için akış oluşturmak, bunları her kullanıcı arayüzü öğesi için birleştirmenizi (combine
ile) gerektirir. Player.Event ile kullanıcı arayüzü öğesi değişikliği arasında çoktan çoğa eşleme vardır.combine
kullanmak 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:
Player
alır.- Coroutine'ları kullanarak
Player
öğesine abone olur. Daha fazla bilgi içinPlayer.listen
bölümüne bakın. - İç durumunu güncelleyerek belirli
Player.Events
yanıt verir. - Uygun bir
Player
gü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
State
alanlarını kullanıma sunar. - Besteler arasında örneği hatırlamak için
remember*State
iş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 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.