کتابخانه media3-ui-compose مؤلفههای بنیادی را برای ساختن
میانای کاربر رسانه در Jetpack Compose فراهم میکند. این کتابخانه برای توسعهدهندگانی طراحی شده است که به سفارشیسازی بیشتری نسبت به آنچه کتابخانه media3-ui-compose-material3 ارائه میدهد نیاز دارند. این صفحه توضیح میدهد که چگونه از عناصر اصلی و نگهدارندههای حالت برای
ایجاد میانای کاربر پخشکننده رسانه سفارشی استفاده کنید.
ترکیب کردن Material3 و عناصر سفارشی Compose
کتابخانه media3-ui-compose-material3 بهگونهای طراحی شده است که انعطافپذیر باشد. میتوانید
از عناصر پیشساخته برای اکثر واسط کاربر خود استفاده کنید، اما وقتی به کنترل بیشتری نیاز دارید،
یک عنصر را با پیادهسازی سفارشی جایگزین کنید. این زمانی است که کتابخانه media3-ui-compose وارد عمل میشود.
برای مثال، تصور کنید میخواهید از
PreviousButton و NextButton استاندارد از کتابخانه
Material3 استفاده کنید، اما به PlayPauseButton کاملاً سفارشی نیاز دارید. بااستفاده از PlayPauseButton از کتابخانه اصلی media3-ui-compose
و قرار دادن آن در کنار عناصر ازپیش ساختهشده میتوانید به این هدف برسید.
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 |
سطح خام که SurfaceView و TextureView را در AndroidView میپیچد. |
سفارشیسازی عنصر ترکیبی Player
هنگام استفاده از کتابخانه media3-ui-compose-material3، عنصر
Player composable تجربه کاملی از پخش رسانه ارائه میدهد. میتوانید چیدمان آن را با ارائه عناصر ترکیبی خودتان به
جایگاههای محتوای آن سفارشیسازی کنید، یا میتوانید از شیء 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 بخوانید.
نگهدارندههای حالت دکمه
برای برخیاز وضعیتهای واسط کاربر، کتابخانه این فرض را میکند که احتمالاً توسط عناصر ترکیبی دکمهمانند مصرف خواهند شد.
| ایالت | بهیاد داشته باش*وضعیت | نوع |
|---|---|---|
PlayPauseButtonState |
rememberPlayPauseButtonState |
۲-تغییر وضعیت |
PreviousButtonState |
rememberPreviousButtonState |
ثابت |
NextButtonState |
rememberNextButtonState |
ثابت |
RepeatButtonState |
rememberRepeatButtonState |
۳-تغییر وضعیت |
ShuffleButtonState |
rememberShuffleButtonState |
۲-تغییر وضعیت |
PlaybackSpeedState |
rememberPlaybackSpeedState |
منو یا N-Toggle |
نمونه استفاده از 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 ترکیب مدیریت نسبت ابعادی با مراقبت از
نمایش شاتر روی سطحی است که هنوز آماده نیست.
@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 برای مقیاسبندی «سطح» به نسبت ابعادی انتخابشده استفاده کنیم (برای انواع بیشتر، به مقیاس محتوا مراجعه کنید) و هم از presentationState.coverSurface برای اینکه بدانیم زمان مناسب برای نمایش «سطح» نیست. در این حالت، میتوانید یک کرکره مات روی سطح قرار دهید که وقتی سطح آماده شد ناپدید میشود.
ContentFrame به شما امکان میدهد تا شاتر را بهعنوان یک لامبدای دنبالهدار سفارشیسازی کنید، اما بهطور پیشفرض، یک @Composable Box سیاه خواهد بود که اندازه ظرف والد را پر میکند.
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بهروزرسانی مناسب تبدیل میشوند. - میتواند در مکانهای مختلف در درخت واسط کاربر ایجاد شود و همیشه نمای ثابتی از وضعیت «بازیکن» را حفظ میکند.
- فیلدهای «نوشتن»
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 نباشد.