डिपेंडेंसी जोड़ना
Media3 लाइब्रेरी में, Jetpack Compose पर आधारित यूज़र इंटरफ़ेस मॉड्यूल शामिल होता है. इसका इस्तेमाल करने के लिए, यह डिपेंडेंसी जोड़ें:
Kotlin
implementation("androidx.media3:media3-ui-compose:1.8.0")
Groovy
implementation "androidx.media3:media3-ui-compose:1.8.0"
हमारा सुझाव है कि आप अपने ऐप्लिकेशन को Compose-first फ़ैशन में डेवलप करें या Views का इस्तेमाल बंद करें.
पूरी तरह से कंपोज़ किया गया डेमो ऐप्लिकेशन
media3-ui-compose
लाइब्रेरी में, बॉक्स से बाहर के कंपोज़ेबल (जैसे कि बटन, इंडिकेटर, इमेज या डायलॉग) शामिल नहीं हैं. हालांकि, आपको पूरी तरह से Compose में लिखा गया डेमो ऐप्लिकेशन मिल सकता है. इसमें इंटरऑपरेबिलिटी के किसी भी समाधान का इस्तेमाल नहीं किया गया है. जैसे, PlayerView
को AndroidView
में रैप करना. डेमो ऐप्लिकेशन, media3-ui-compose
मॉड्यूल से यूज़र इंटरफ़ेस (यूआई) स्टेट होल्डर क्लास का इस्तेमाल करता है. साथ ही, Compose Material3 लाइब्रेरी का इस्तेमाल करता है.
यूज़र इंटरफ़ेस (यूआई) स्टेट होल्डर
यूज़र इंटरफ़ेस (यूआई) स्टेट होल्डर और कंपोज़ेबल के बीच अंतर को बेहतर तरीके से समझने के लिए, Compose में स्टेट को मैनेज करने का तरीका जानें.
बटन के स्टेट होल्डर
यूआई की कुछ स्थितियों के लिए, हम यह मान लेते हैं कि इनका इस्तेमाल बटन जैसे कंपोज़ेबल करेंगे.
राज्य | remember*State | टाइप |
---|---|---|
PlayPauseButtonState |
rememberPlayPauseButtonState |
2-टॉगल |
PreviousButtonState |
rememberPreviousButtonState |
कॉन्स्टेंट |
NextButtonState |
rememberNextButtonState |
कॉन्स्टेंट |
RepeatButtonState |
rememberRepeatButtonState |
3-टॉगल |
ShuffleButtonState |
rememberShuffleButtonState |
2-टॉगल |
PlaybackSpeedState |
rememberPlaybackSpeedState |
मेन्यू या N-टॉगल |
PlayPauseButtonState
के इस्तेमाल का उदाहरण:
@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
में थीम से जुड़ी कोई जानकारी नहीं है. जैसे, चलाने या रोकने के लिए इस्तेमाल किया जाने वाला आइकॉन. इसकी सिर्फ़ यह ज़िम्मेदारी है कि यह Player
को यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदल दे.
इसके बाद, अपनी पसंद के लेआउट में बटनों को मिक्स और मैच किया जा सकता है:
Row(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
PreviousButton(player)
PlayPauseButton(player)
NextButton(player)
}
विज़ुअल आउटपुट स्टेट होल्डर
PresentationState
में यह जानकारी होती है कि PlayerSurface
में वीडियो आउटपुट कब दिखाया जा सकता है या इसे प्लेसहोल्डर यूज़र इंटरफ़ेस (यूआई) एलिमेंट से कवर किया जाना चाहिए.
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))
}
यहां, हम presentationState.videoSizeDp
का इस्तेमाल करके, Surface को पसंद के मुताबिक आसपेक्ट रेशियो में बदल सकते हैं. ज़्यादा टाइप के लिए, ContentScale के दस्तावेज़ देखें. साथ ही, presentationState.coverSurface
का इस्तेमाल करके यह पता लगा सकते हैं कि Surface दिखाने का सही समय कब नहीं है. इस मामले में, अपारदर्शी शटर को सतह के ऊपर रखा जा सकता है. जब सतह तैयार हो जाएगी, तब यह शटर गायब हो जाएगा.
फ़्लो कहाँ हैं?
कई Android डेवलपर, हमेशा बदलते रहने वाले यूज़र इंटरफ़ेस (यूआई) डेटा को इकट्ठा करने के लिए, Kotlin Flow
ऑब्जेक्ट का इस्तेमाल करते हैं. उदाहरण के लिए, आपको Player.isPlaying
फ़्लो की ज़रूरत हो सकती है, जिसे लाइफ़साइकल के हिसाब से collect
किया जा सकता है. या फिर Player.eventsFlow
जैसा कुछ, ताकि आपको Flow<Player.Events>
मिल सके और आप उसे अपनी पसंद के हिसाब से filter
कर सकें.
हालांकि, Player
यूज़र इंटरफ़ेस (यूआई) की स्थिति के लिए फ़्लो का इस्तेमाल करने के कुछ नुकसान हैं. डेटा ट्रांसफ़र में समय लगने की वजह से, कई बार समस्याएं आ सकती हैं. हम चाहते हैं कि Player.Event
और यूज़र इंटरफ़ेस (यूआई) पर इसके इस्तेमाल के बीच कम से कम समय लगे. साथ ही, ऐसे यूआई एलिमेंट न दिखाए जाएं जो Player.Event
के साथ सिंक नहीं हैं.Player
अन्य बातों में ये शामिल हैं:
- सभी
Player.Events
वाले फ़्लो में, एक ही ज़िम्मेदारी के सिद्धांत का पालन नहीं किया जाएगा. हर उपभोक्ता को काम के इवेंट फ़िल्टर करने होंगे. - हर
Player.Event
के लिए फ़्लो बनाने के लिए, आपको उन्हें हर यूज़र इंटरफ़ेस (यूआई) एलिमेंट के लिएcombine
के साथ जोड़ना होगा. Player.Event और यूज़र इंटरफ़ेस (यूआई) एलिमेंट में कई-से-कई मैपिंग होती है.combine
का इस्तेमाल करने से, यूज़र इंटरफ़ेस (यूआई) की स्थिति गैर-कानूनी हो सकती है.
पसंद के मुताबिक यूज़र इंटरफ़ेस (यूआई) की स्थितियां बनाना
अगर मौजूदा यूज़र इंटरफ़ेस (यूआई) स्टेट आपकी ज़रूरतों को पूरा नहीं करती हैं, तो आपके पास कस्टम यूज़र इंटरफ़ेस (यूआई) स्टेट जोड़ने का विकल्प होता है. पैटर्न कॉपी करने के लिए, मौजूदा स्थिति का सोर्स कोड देखें. यूआई स्टेट होल्डर क्लास आम तौर पर ये काम करती है:
Player
में शामिल है.- यह कोरूटीन का इस्तेमाल करके,
Player
की सदस्यता लेता है. ज़्यादा जानकारी के लिए,Player.listen
देखें. - यह
Player.Events
के हिसाब से, अपनी इंटरनल स्थिति को अपडेट करता है. - कारोबार के लॉजिक से जुड़े ऐसे निर्देशों को स्वीकार करें जिन्हें सही
Player
अपडेट में बदला जा सके. - इसे यूज़र इंटरफ़ेस (यूआई) ट्री में कई जगहों पर बनाया जा सकता है. साथ ही, यह हमेशा प्लेयर की स्थिति को एक जैसा बनाए रखेगा.
- यह Compose
State
फ़ील्ड दिखाता है. इनका इस्तेमाल कंपोज़ेबल, बदलावों के हिसाब से डाइनैमिक तरीके से जवाब देने के लिए कर सकता है. - इसमें
remember*State
फ़ंक्शन होता है, जो कंपोज़िशन के बीच इंस्टेंस को याद रखता है.
बैकग्राउंड में क्या होता है:
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)
}
}
}
अपने Player.Events
पर प्रतिक्रिया देने के लिए, Player.listen
का इस्तेमाल किया जा सकता है. यह एक suspend fun
है, जिसकी मदद से को-रूटीन की दुनिया में शामिल हुआ जा सकता है और Player.Events
को अनिश्चित काल तक सुना जा सकता है. Media3 में अलग-अलग यूज़र इंटरफ़ेस (यूआई) स्टेट लागू करने से, डेवलपर को Player.Events
के बारे में जानने की ज़रूरत नहीं पड़ती.