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 को रैप करता है. |
Artwork |
ऐसा कंपोज़ेबल जो मौजूदा मीडिया आइटम का आर्टवर्क दिखाता है, जैसे कि एल्बम कवर. |
Player कंपोज़ेबल को पसंद के मुताबिक बनाना
media3-ui-compose-material3 लाइब्रेरी का इस्तेमाल करते समय, Player कंपोज़ेबल, मीडिया चलाने का पूरा अनुभव देता है. इसके लेआउट को पसंद के मुताबिक बनाने के लिए, इसके कॉन्टेंट स्लॉट में अपने कंपोज़ेबल उपलब्ध कराएं. इसके अलावा, यूज़र इंटरफ़ेस (यूआई) के कुछ हिस्सों को पसंद के मुताबिक बनाने के लिए, PlayerDefaults ऑब्जेक्ट का इस्तेमाल किया जा सकता है. जैसे, TopControls, CenterControls, BottomControls, और ErrorOverlay.
इमेज 1 में दिखाया गया है कि Player कंपोज़ेबल, अपने स्लॉट कहां रखता है. दूसरी इमेज में, लेयर बनाने का क्रम दिखाया गया है.
Player कंपोज़ेबल के स्लॉट.
बीच और सबसे नीचे मौजूद कंट्रोल, PlayerDefaults.CenterControls और PlayerDefaults.BottomControls के स्लॉट दिखाते हैं. topControls, above, और below स्लॉट, डिफ़ॉल्ट रूप से खाली होते हैं.Player कंपोज़ेबल की लेयर.
इन्हें ड्रॉ किए जाने के क्रम में नंबर दिया जाता है.उदाहरण के लिए, PlayerDefaults.CenterControls का इस्तेमाल करके, बीच में मौजूद कंट्रोल को वैसे ही रखते हुए, सिर्फ़ बीच में मौजूद वीडियो चलाने/रोकने वाले बटन को बदला जा सकता है.
आपके पास topControls जैसे पैरामीटर के लिए, पूरी तरह से कस्टम कंपोज़ेबल पास करने का विकल्प भी होता है. साथ ही, bottomControls जैसे अन्य पैरामीटर को पूरी तरह से छोड़ा जा सकता है, ताकि वे डिफ़ॉल्ट तौर पर सेट रहें.
@Composable fun CustomPlayerSlots(player: Player, showControls: Boolean, modifier: Modifier = Modifier) { Player( player = player, modifier = modifier, showControls = showControls, topControls = { // Fully custom top controls Text("My custom title") }, centerControls = { // Use default CenterControls but override the central button PlayerDefaults.CenterControls( player = player, central = { // A custom play/pause button Material3PlayPauseButton(player, modifier = Modifier.size(64.dp)) }, ) }, // bottomControls are left as default ) }
Player कंपोज़ेबल, सबटाइटल भी दिखाता है. साथ ही, जब दिखाने के लिए कोई वीडियो नहीं होता, तो यह मौजूदा मीडिया आइटम का आर्टवर्क दिखाता है. इनके दिखने के तरीके को अपनी पसंद के मुताबिक बनाने के लिए, subtitles और artwork स्लॉट में अपने कंपोज़ेबल पास करें. इन्हें छिपाने के लिए, subtitles को खाली कंपोज़ेबल और null को artwork पास करें.
यूज़र इंटरफ़ेस (यूआई) स्टेट होल्डर
अगर कोई भी स्कैफ़ोल्ड कॉम्पोनेंट आपकी ज़रूरतों को पूरा नहीं करता है, तो सीधे तौर पर स्टेट ऑब्जेक्ट का भी इस्तेमाल किया जा सकता है. आम तौर पर, हमारा सुझाव है कि रीकंपोज़िशन के बीच अपने यूज़र इंटरफ़ेस (यूआई) के लुक को बनाए रखने के लिए, उससे जुड़े remember तरीकों का इस्तेमाल करें.
यूज़र इंटरफ़ेस (यूआई) के स्टेट होल्डर और कंपोज़ेबल के इस्तेमाल में मिलने वाली सुविधाओं को बेहतर तरीके से समझने के लिए, Compose में स्टेट मैनेज करने के तरीके के बारे में पढ़ें.
बटन के स्टेट होल्डर
यूआई की कुछ स्थितियों के लिए, लाइब्रेरी यह मान लेती है कि उन्हें बटन जैसे कॉम्पोज़ेबल इस्तेमाल करेंगे.
| राज्य | याद रखें*राज्य | प्रकार |
|---|---|---|
PlayPauseButtonState |
rememberPlayPauseButtonState |
दो-टॉगल या एन-टॉगल |
PreviousButtonState |
rememberPreviousButtonState |
कॉन्स्टेंट |
NextButtonState |
rememberNextButtonState |
कॉन्स्टेंट |
RepeatButtonState |
rememberRepeatButtonState |
3-Toggle |
ShuffleButtonState |
rememberShuffleButtonState |
2-टॉगल |
PlaybackSpeedState |
rememberPlaybackSpeedState |
मेन्यू या N-Toggle |
वीडियो चलाने और रोकने की स्थितियों के अलावा, PlayPauseButtonState, showBuffering, showReplay, और showRetry को दिखाता है. इनका इस्तेमाल करके, प्लेयर के बफ़र होने के दौरान बफ़रिंग इंडिकेटर, वीडियो खत्म होने के बाद फिर से चलाने का आइकॉन, और वीडियो चलाने में गड़बड़ी होने के बाद फिर से कोशिश करने का आइकॉन दिखाया जा सकता है. showReplay और showRetry को बेहतर बनाएं
showPlay: जब भी इनमें से कोई एक true हो, तो showPlay भी true होता है. इसलिए, showPlay से पहले इनकी जांच करें. onClick मेथड इन स्थितियों को भी हैंडल करता है. उदाहरण के लिए, गड़बड़ी के बाद प्लेयर को फिर से तैयार करना.
इनमें से कौनसी स्थितियां दिखानी हैं, यह चुनने के लिए SHOW_BUFFERING_WHEN_PLAYING, SHOW_BUFFERING_ALWAYS, SHOW_REPLAY_ON_ENDED, और SHOW_RETRY_ON_ERROR के कॉम्बिनेशन को rememberPlayPauseButtonState के displayMode पैरामीटर में पास करें. डिफ़ॉल्ट रूप से, displayMode, SHOW_BUFFERING_ALWAYS, SHOW_REPLAY_ON_ENDED, और SHOW_RETRY_ON_ERROR को जोड़ता है. इससे तीनों स्थितियां दिखती हैं. साथ ही, वीडियो के रुकने पर भी बफ़रिंग की स्थिति दिखती है.
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, प्लेयर की मौजूदा गड़बड़ी की स्थिति के बारे में जानकारी देता है. इसका इस्तेमाल, गड़बड़ी का ओवरले दिखाने के लिए किया जा सकता है.
PlaybackState, प्लेयर के मुख्य प्लेबैक स्टेटस को दिखाता है. जैसे, playbackState, isPlaying, और playerError. यह किसी खास यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट से अलग होता है. इसका इस्तेमाल ओवरले या कस्टम लेआउट को चलाने के लिए करें. उदाहरण के लिए, प्लेयर के बफ़र होने के दौरान लोड होने का इंडिकेटर दिखाने के लिए. पहले से बने Material3 इंडिकेटर के लिए, BufferingIndicator का इस्तेमाल करें.
PresentationState में यह जानकारी होती है कि PlayerSurface में वीडियो आउटपुट कब दिखाया जा सकता है या इसे प्लेसहोल्डर यूज़र इंटरफ़ेस (यूआई) एलिमेंट से कवर किया जाना चाहिए.
ContentFrame composable, आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) को मैनेज करने के साथ-साथ, उस प्लैटफ़ॉर्म पर शटर दिखाने का ध्यान रखता है जो अभी तैयार नहीं है.
@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 को चुने गए आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) के हिसाब से स्केल कर सकते हैं. ज़्यादा टाइप के लिए, कॉन्टेंट स्केल देखें. साथ ही, presentationState.coverSurface का इस्तेमाल करके यह पता लगा सकते हैं कि प्लैटफ़ॉर्म दिखाने का सही समय कब नहीं है. इस मामले में, अपारदर्शी शटर को प्लैटफ़ॉर्म के ऊपर रखा जा सकता है. जब प्लैटफ़ॉर्म तैयार हो जाएगा, तब यह शटर गायब हो जाएगा.
ContentFrame की मदद से, शटर को ट्रेलिंग लैम्डा के तौर पर पसंद के मुताबिक बनाया जा सकता है. हालांकि, डिफ़ॉल्ट रूप से यह काला @Composable Box होता है और पैरंट कंटेनर के साइज़ में दिखता है.
PresentationState, showArtwork को भी एक्सपोज़ करता है. यह true होता है, जब मौजूदा मीडिया आइटम को बिना वीडियो ट्रैक के चलाया जाता है. जैसे, सिर्फ़ ऑडियो वाला कॉन्टेंट. ऐसे मामले में, ContentFrame, Artwork कंपोज़ेबल दिखाता है. यह आइटम के MediaMetadata से आर्टवर्क दिखाता है. आर्टवर्क को पसंद के मुताबिक बनाने या छिपाने के लिए, ContentFrame के artwork स्लॉट में अपना कंपोज़ेबल या null पास करें.
डिफ़ॉल्ट रूप से, Artwork सिर्फ़ मेटाडेटा में एम्बेड किया गया आर्टवर्क डेटा लोड करता है. इसके अलावा, यह content, file या android.resource यूआरआई से भी आर्टवर्क डेटा लोड करता है. रिमोट यूआरआई या अन्य सोर्स से आर्टवर्क लोड करने के लिए, इनमें से कोई एक विकल्प चुनें:
ArtworkमेंBitmapLoaderपास करें, जैसे किDataSourceBitmapLoader.Artworkओवरलोड का इस्तेमाल करें, जो आपकी पसंद की लाइब्रेरी के साथ इमेज लोड करने के लिएpainterलेता है.
Player कंपोज़ेबल, डिफ़ॉल्ट रूप से सबटाइटल रेंडर करता है. हालांकि, ContentFrame अपने subtitles स्लॉट को खाली छोड़ देता है. ContentFrame में सबटाइटल रेंडर करने या Player में सबटाइटल रेंडरिंग को पसंद के मुताबिक बनाने के लिए, CuesState की मदद से प्लेयर के मौजूदा क्यू देखें और उन्हें subtitles स्लॉट में रेंडर करें. ContentFrame इस स्लॉट को स्केल किए गए वीडियो की सीमाओं के अंदर रखता है. साथ ही, इसे शटर और आर्टवर्क के ऊपर दिखाता है, ताकि वीडियो न होने पर भी सबटाइटल दिखते रहें.
ट्रैक चुनने के लिए स्टेट होल्डर
TrackSelectionState की मदद से, किसी एक ट्रैक टाइप के लिए मेन्यू बनाया जा सकता है. जैसे, ऑडियो की भाषा या सबटाइटल मेन्यू. इसे rememberTrackSelectionState के साथ बनाएं. इसके लिए, ट्रैक टाइप और फ़िल्टर का कॉम्बिनेशन पास करें. जैसे, TRACK_FILTER_LANGUAGE या TRACK_FILTER_RESOLUTION. इससे यह तय होता है कि ट्रैक की कौनसी प्रॉपर्टी, विकल्पों को अलग करती हैं. ट्रैक टाइप को बंद करने वाला विकल्प जोड़ने के लिए, hasOffOption या hasAutoOption को true पर सेट करें. इसके अलावा, ऐसा विकल्प भी जोड़ा जा सकता है जिससे प्लेयर अपने-आप ट्रैक चुन सके.
स्टेट, उपलब्ध selectionOptions और selectedOption को एक्सपोज़ करता है.
हर TrackSelectionOption से पता चलता है कि डिवाइस पर यह सुविधा काम करती है या नहीं और इसे चुना गया है या नहीं. selectOption को कोई विकल्प पास करने पर, प्लेयर के ट्रैक चुनने के पैरामीटर उसी हिसाब से अपडेट हो जाते हैं. मेन्यू दिखाने से पहले, canSetTrackSelectionParameters की जांच करें. साथ ही, यह भी देखें कि चुनने के लिए ज़रूरत के मुताबिक विकल्प मौजूद हैं या नहीं. उदाहरण के लिए, यहां दिए गए सबटाइटल मेन्यू में, सबटाइटल की हर भाषा के लिए एक विकल्प और सबटाइटल बंद करने का विकल्प दिया गया है:
@Composable fun SubtitleMenu(player: Player, modifier: Modifier = Modifier) { val state = rememberTrackSelectionState( player, trackType = C.TRACK_TYPE_TEXT, trackFilters = TRACK_FILTER_LANGUAGE, hasOffOption = true, ) // Hide the menu if the player can't change tracks or has no subtitles to choose from if (!state.canSetTrackSelectionParameters || state.selectionOptions.size < 2) return Column(modifier.selectableGroup()) { for (option in state.selectionOptions) { Row( Modifier.fillMaxWidth() .heightIn(min = 48.dp) .selectable( selected = option.isSelected, enabled = option.isSupported, role = Role.RadioButton, onClick = { state.selectOption(option) }, ) .padding(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically, ) { RadioButton(selected = option.isSelected, onClick = null, enabled = option.isSupported) Text( text = if (option.onOffState == TRACK_SELECTION_OFF) "Off" else option.language?.let { Locale.forLanguageTag(it).displayName } ?: "Unknown", color = if (option.isSupported) Color.Unspecified else LocalContentColor.current.copy(alpha = 0.38f), ) } } } }
अगर आपको खिलाड़ी के Tracks और TrackSelectionParameters का डायरेक्ट ऐक्सेस चाहिए, तो इसके बजाय TrackSelectionParametersState का इस्तेमाल करें. प्लेयर, ट्रैक कैसे चुनता है, इस बारे में ज़्यादा जानने के लिए, ट्रैक चुनना लेख पढ़ें.
Flows कहां हैं?
कई 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 का इस्तेमाल करके पकड़ा जा सकता है. यह एक suspend fun है, जो आपको कोरूटीन की दुनिया में जाने और Player.Events को अनिश्चित समय तक सुनने की सुविधा देता है. Media3 में यूज़र इंटरफ़ेस (यूआई) की अलग-अलग स्थितियों को लागू करने से, डेवलपर को Player.Events के बारे में सीखने की ज़रूरत नहीं पड़ती.