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.6.0")
Groovy
implementation "androidx.media3:media3-ui-compose:1.6.0"
Uygulamanızı öncelikli olarak Compose ile geliştirmenizi veya Görünümler'den geçiş yapmanızı önemle tavsiye ederiz.
Tamamen Oluştur demo uygulaması
media3-ui-compose
kitaplığı, hazır bileşenler (ör. düğmeler, göstergeler, resimler veya iletişim kutuları) içermese de PlayerView
'i AndroidView
içine sarmalama gibi herhangi bir birlikte çalışabilirlik çözümünden kaçınan, tamamen Compose'da yazılmış bir demo uygulama bulabilirsiniz. Demo uygulaması, media3-ui-compose
modülündeki kullanıcı arayüzü durum tutucu sınıflarını ve Compose Material3 kitaplığını kullanır.
Kullanıcı arayüzü durum koruyucuları
Kullanılabilirlik durumu tutucularının esnekliğini, bileşenlere 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 koruyucuları
Bazı kullanıcı arayüzü durumlarının büyük olasılıkla düğme benzeri Composables tarafından kullanılacağını varsayıyoruz.
Eyalet | remember*Eyalet | 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
kullanımıyla ilgili örnek:
@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
değerini kullanıcı arayüzü durumuna dönüştürmektir.
Ardından, düğmeleri tercihinize göre düzenleyebilirsiniz:
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 kullanıcı arayüzü öğesiyle 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, yüzeyi istenen en boy oranına ölçeklendirmek için presentationState.videoSizeDp
'ü (daha fazla tür için ContentScale dokümanlarına bakın) ve yüzeyi göstermenin zamanlamasının ne zaman doğru olmadığını öğrenmek için presentationState.coverSurface
'ı kullanabiliriz. Bu durumda, yüzeyin üzerine opak bir perde yerleştirebilirsiniz. Bu perde, yüzey hazır olduğunda kaybolur.
Akışlar nerede bulunur?
Birçok Android geliştiricisi, sürekli değişen kullanıcı arayüzü verilerini toplamak için Kotlin Flow
nesnelerini kullanma konusunda bilgi sahibidir. Örneğin, yaşam döngüsü bilinciyle collect
yapabileceğiniz bir Player.isPlaying
akışı arayabilirsiniz. Dilerseniz Player.eventsFlow
gibi bir ifade kullanarak istediğiniz şekilde filter
edebileceğiniz bir Flow<Player.Events>
elde edebilirsiniz.
Ancak Player
kullanıcı arayüzü durumu için akışların kullanılmasını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ünde 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östermekten kaçınmak istiyoruz.
Diğer noktalar şunlardır:
- Tüm
Player.Events
'leri içeren bir akış tek bir sorumluluk ilkesine uymaz. Bu durumda her tüketicinin alakalı etkinlikleri filtrelemesi gerekir. - Her
Player.Event
için bir akış oluşturmak istiyorsanız her kullanıcı arayüzü öğesi için bunları (combine
ile) birleştirmeniz gerekir. Player.Event ile kullanıcı arayüzü öğesi değişikliği arasında çoklu eşleme vardır.combine
kullanmak zorunda kalmak, kullanıcı arayüzünün yasa dışı durumlara girmesine neden olabilir.
Özel kullanıcı arayüzü durumları oluşturma
Mevcut kullanıcı arayüzü durumları ihtiyaçlarınıza uygun değilse özel kullanıcı arayüzü durumları ekleyebilirsiniz. Kalıpları kopyalamak için mevcut durumun kaynak kodunu inceleyin. Tipik bir kullanıcı arayüzü durum tutucu sınıfı aşağıdakileri yapar:
Player
alır.Player
dizisine coroutine'leri kullanarak abone olur. Daha fazla bilgi içinPlayer.listen
bölümüne bakın.- Dahili durumunu güncelleyerek belirli
Player.Events
öğelerine 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ında birden fazla yerde oluşturulabilir ve her zaman oyuncunun durumunu tutarlı bir şekilde gösterir.
- Değişikliklere dinamik olarak yanıt vermek için bir Composable tarafından kullanılabilen Compose
State
alanlarını gösterir. - Kompozisyonlar arasındaki örneği hatırlamak için
remember*State
işlevi bulunur.
Kamera arkası:
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
'i kullanabilirsiniz. Bu, coroutine dünyasına girmenizi ve Player.Events
'yi süresiz olarak dinlemenizi sağlayan bir suspend fun
'dir. Çeşitli kullanıcı arayüzü durumlarının Media3 tarafından uygulanması, son geliştiricinin Player.Events
hakkında bilgi edinme konusunda endişelenmemesine yardımcı olur.