media3-ui-compose लाइब्रेरी, Jetpack Compose में मीडिया यूज़र इंटरफ़ेस बनाने के लिए बुनियादी कॉम्पोनेंट उपलब्ध कराती है. इसे उन डेवलपर के लिए बनाया गया है जिन्हें media3-ui-compose-material3 लाइब्रेरी के मुकाबले, ज़्यादा बदलाव करने की ज़रूरत होती है. इस पेज पर, कस्टम मीडिया प्लेयर यूज़र इंटरफ़ेस (यूआई) बनाने के लिए, मुख्य कॉम्पोनेंट और स्टेट होल्डर इस्तेमाल करने का तरीका बताया गया है.
Material3 और कस्टम Compose कॉम्पोनेंट को एक साथ इस्तेमाल करना
media3-ui-compose-material3 लाइब्रेरी को इस तरह से डिज़ाइन किया गया है कि इसे आसानी से इस्तेमाल किया जा सके. ज़्यादातर यूज़र इंटरफ़ेस (यूआई) के लिए, पहले से बने कॉम्पोनेंट का इस्तेमाल किया जा सकता है. हालांकि, अगर आपको ज़्यादा कंट्रोल चाहिए, तो किसी एक कॉम्पोनेंट को कस्टम कॉम्पोनेंट से बदला जा सकता है. ऐसे में, media3-ui-compose लाइब्रेरी काम आती है.
उदाहरण के लिए, मान लें कि आपको Material3 लाइब्रेरी से स्टैंडर्ड PreviousButton और NextButton का इस्तेमाल करना है, लेकिन आपको पूरी तरह से कस्टम PlayPauseButton की ज़रूरत है. इसके लिए, कोर media3-ui-compose लाइब्रेरी से PlayPauseButton का इस्तेमाल करें और इसे पहले से बने कॉम्पोनेंट के साथ रखें.
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) }
उपलब्ध कॉम्पोनेंट
media3-ui-compose लाइब्रेरी, प्लेयर कंट्रोल के लिए पहले से बनाए गए कंपोज़ेबल का सेट उपलब्ध कराती है. यहां कुछ ऐसे कॉम्पोनेंट दिए गए हैं जिनका इस्तेमाल सीधे तौर पर अपने ऐप्लिकेशन में किया जा सकता है:
| कॉम्पोनेंट | ब्यौरा |
|---|---|
PlayPauseButton |
यह उस बटन के लिए एक स्टेट कंटेनर है जो टॉगल करके चलाने और रोकने की सुविधा देता है. |
SeekBackButton |
यह एक स्टेट कंटेनर है. इसका इस्तेमाल, तय की गई इंक्रीमेंट वैल्यू के हिसाब से पीछे ले जाने वाले बटन के लिए किया जाता है. |
SeekForwardButton |
यह एक स्टेट कंटेनर है. इसका इस्तेमाल, तय की गई इंक्रीमेंट वैल्यू के हिसाब से आगे बढ़ाने वाले बटन के लिए किया जाता है. |
NextButton |
यह एक स्टेट कंटेनर है. इसका इस्तेमाल, अगले मीडिया आइटम पर जाने वाले बटन के लिए किया जाता है. |
PreviousButton |
यह एक स्टेट कंटेनर है. इसका इस्तेमाल, पिछले मीडिया आइटम पर जाने वाले बटन के लिए किया जाता है. |
RepeatButton |
यह उस बटन के लिए एक स्टेट कंटेनर है जो रिपीट मोड के बीच साइकल करता है. |
ShuffleButton |
यह उस बटन के लिए एक स्टेट कंटेनर है जो शफ़ल मोड को टॉगल करता है. |
MuteButton |
यह उस बटन के लिए एक स्टेट कंटेनर है जो प्लेयर को म्यूट और अनम्यूट करता है. |
TimeText |
यह एक स्टेट कंटेनर है. इसका इस्तेमाल, प्लेयर की प्रोग्रेस दिखाने वाले कंपोज़ेबल के लिए किया जाता है. |
ContentFrame |
मीडिया कॉन्टेंट दिखाने के लिए एक प्लैटफ़ॉर्म, जो आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) को मैनेज करता है, इमेज का साइज़ बदलता है, और शटर को कंट्रोल करता है |
PlayerSurface |
रॉ प्लैटफ़ॉर्म, जो AndroidView में SurfaceView और TextureView को रैप करता है. |
Player कंपोज़ेबल को पसंद के मुताबिक बनाना
media3-ui-compose-material3 लाइब्रेरी का इस्तेमाल करते समय, Player कंपोज़ेबल, मीडिया चलाने का पूरा अनुभव देता है. इसके लेआउट को पसंद के मुताबिक बनाया जा सकता है. इसके लिए, इसके कॉन्टेंट स्लॉट में अपने कंपोज़ेबल डालें. इसके अलावा, यूज़र इंटरफ़ेस (यूआई) के कुछ हिस्सों को पसंद के मुताबिक बनाने के लिए, PlayerDefaults ऑब्जेक्ट का इस्तेमाल किया जा सकता है. जैसे, TopControls, CenterControls, BottomControls, और ErrorOverlay.
उदाहरण के लिए, PlayerDefaults.CenterControls का इस्तेमाल करके, सिर्फ़ बीच में मौजूद 'चलाएं/रोकें' बटन को बदला जा सकता है. हालांकि, बीच में मौजूद बाकी कंट्रोल में कोई बदलाव नहीं होगा.
topControls जैसे किसी पैरामीटर के लिए, पूरी तरह से कस्टम कंपोज़ेबल भी पास किया जा सकता है. साथ ही, bottomControls जैसे अन्य पैरामीटर को पूरी तरह से हटाया जा सकता है, ताकि वे डिफ़ॉल्ट वैल्यू पर बने रहें.
@Composable fun CustomPlayerSlots(player: Player, modifier: Modifier = Modifier) { Player( player = player, modifier = modifier, topControls = { p, visible -> // Fully custom top controls AnimatedVisibility(visible) { Text("My custom title") } }, centerControls = { p, visible -> // Use default CenterControls but override the central button PlayerDefaults.CenterControls( player = p, visible = visible, central = { // A custom play/pause button Material3PlayPauseButton(it, modifier = Modifier.size(64.dp)) }, ) }, // bottomControls are left as default ) }
यूज़र इंटरफ़ेस (यूआई) की स्थिति को मैनेज करने वाले क्लास
अगर कोई भी स्केफ़ोल्डिंग कॉम्पोनेंट आपकी ज़रूरतों के मुताबिक नहीं है, तो सीधे तौर पर स्टेट ऑब्जेक्ट का इस्तेमाल भी किया जा सकता है. आम तौर पर, यह सलाह दी जाती है कि रेंडरिंग के बीच अपने यूज़र इंटरफ़ेस (यूआई) को बनाए रखने के लिए, उससे जुड़े remember तरीकों का इस्तेमाल करें.
यूज़र इंटरफ़ेस (यूआई) स्टेट होल्डर और कंपोज़ेबल के बीच के अंतर को बेहतर तरीके से समझने के लिए, Compose में स्टेट को मैनेज करने के तरीके के बारे में पढ़ें.
बटन के स्टेट होल्डर
यूज़र इंटरफ़ेस (यूआई) की कुछ स्थितियों के लिए, लाइब्रेरी यह मानती है कि उन्हें बटन जैसे कंपोज़ेबल इस्तेमाल करेंगे.
| राज्य | remember*State | प्रकार |
|---|---|---|
PlayPauseButtonState |
rememberPlayPauseButtonState |
2-टॉगल |
PreviousButtonState |
rememberPreviousButtonState |
कॉन्स्टेंट |
NextButtonState |
rememberNextButtonState |
कॉन्स्टेंट |
RepeatButtonState |
rememberRepeatButtonState |
3-टॉगल करें |
ShuffleButtonState |
rememberShuffleButtonState |
2-टॉगल |
PlaybackSpeedState |
rememberPlaybackSpeedState |
मेन्यू या N-टॉगल |
PlayPauseButtonState के इस्तेमाल का उदाहरण:
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), ) }
विज़ुअल आउटपुट स्टेट होल्डर
CurrentMediaItemState से, फ़िलहाल चल रहे MediaItem के बारे में जानकारी मिलती है. वहीं, PlaylistState से, प्लेयर पर सेट किए गए MediaItems के बारे में जानकारी मिलती है. ये आपके कस्टम यूज़र इंटरफ़ेस (यूआई) में मेटाडेटा की जानकारी दिखाने के लिए फ़ायदेमंद होते हैं.
ErrorState से, प्लेयर की मौजूदा गड़बड़ी की स्थिति के बारे में जानकारी मिलती है. इसका इस्तेमाल, गड़बड़ी का ओवरले दिखाने के लिए किया जा सकता है.
PresentationState में यह जानकारी होती है कि PlayerSurface में वीडियो आउटपुट कब दिखाया जा सकता है या इसे प्लेसहोल्डर यूज़र इंटरफ़ेस (यूआई) एलिमेंट से कवर किया जाना चाहिए.
ContentFrame कंपोज़ेबल, आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) को मैनेज करने के साथ-साथ, उस प्लैटफ़ॉर्म पर शटर दिखाने का ध्यान रखता है जो अभी तैयार नहीं है.
@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.videoAspectRatio) // Always leave PlayerSurface to be part of the Compose tree because it will be initialized 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() } }
यहां, हम presentationState.videoAspectRatio का इस्तेमाल करके, Surface को चुने गए आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) के हिसाब से स्केल कर सकते हैं. ज़्यादा टाइप के लिए, Content scale देखें. साथ ही, presentationState.coverSurface का इस्तेमाल करके यह पता लगा सकते हैं कि प्लैटफ़ॉर्म दिखाने का सही समय कब नहीं है. इस मामले में, अपारदर्शी शटर को प्लैटफ़ॉर्म के ऊपर रखा जा सकता है. जब प्लैटफ़ॉर्म तैयार हो जाएगा, तब यह शटर गायब हो जाएगा.
ContentFrame की मदद से, शटर को ट्रेलिंग लैम्डा के तौर पर पसंद के मुताबिक बनाया जा सकता है. हालांकि, डिफ़ॉल्ट रूप से यह काला @Composable Box होता है और पैरंट कंटेनर के साइज़ में दिखता है.
फ़्लो कहाँ हैं?
कई Android डेवलपर, Kotlin Flow ऑब्जेक्ट का इस्तेमाल करके, लगातार बदलते यूज़र इंटरफ़ेस (यूआई) डेटा को इकट्ठा करने के बारे में जानते हैं. उदाहरण के लिए, आपको Player.isPlaying फ़्लो की ज़रूरत हो सकती है, जिसे लाइफ़साइकल के हिसाब से collect किया जा सकता है. या फिर Player.eventsFlow जैसा कुछ, ताकि आपको Flow<Player.Events> मिल सके. इसे अपनी पसंद के हिसाब से filter किया जा सकता है.
हालांकि, Player यूज़र इंटरफ़ेस (यूआई) की स्थिति के लिए फ़्लो का इस्तेमाल करने के कुछ नुकसान हैं. डेटा ट्रांसफ़र एसिंक्रोनस तरीके से होता है. यह एक मुख्य समस्या है. हमारा मकसद, 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(COMMAND_ACTION_A)) private set var someFieldValue by mutableStateOf(someFieldDefault) private set fun onClick() { player.actionA() } suspend fun observe(): Nothing = player.listen { events -> if (events.containsAny(EVENT_B_CHANGED, EVENT_C_CHANGED, EVENT_AVAILABLE_COMMANDS_CHANGED)) { someFieldValue = this.someField isEnabled = this.isCommandAvailable(COMMAND_ACTION_A) } } }
अपने Player.Events पर प्रतिक्रिया देने के लिए, Player.listen का इस्तेमाल करके उन्हें पकड़ा जा सकता है. Player.listen एक suspend fun है, जो आपको कोराटीन की दुनिया में ले जाता है और Player.Events को अनिश्चित काल तक सुनने की सुविधा देता है. Media3 में अलग-अलग यूज़र इंटरफ़ेस (यूआई) स्टेट लागू करने से, डेवलपर को Player.Events के बारे में जानने की ज़रूरत नहीं पड़ती.