Jetpack Compose-এ মিডিয়া UI তৈরি করার জন্য media3-ui-compose লাইব্রেরি
প্রাথমিক কম্পোনেন্ট প্রদান করে। এটি সেইসব ডেভেলপারদের জন্য ডিজাইন করা হয়েছে যাদের media3-ui-compose-material3
লাইব্রেরির
মাধ্যমে অফার করা কাস্টমাইজেশনের চেয়ে আরও বেশি কাস্টমাইজেশন প্রয়োজন। এই পৃষ্ঠায় কীভাবে কাস্টম মিডিয়া প্লেয়ার UI তৈরি করতে
কোর কম্পোনেন্ট ও স্টেট হোল্ডার ব্যবহার করতে হয় তা ব্যাখ্যা করা হয়েছে।
Material3 ও কাস্টম Compose কম্পোনেন্ট মিশিয়ে ব্যবহার করা
media3-ui-compose-material3 লাইব্রেরি নমনীয় হওয়ার জন্য ডিজাইন করা হয়েছে। আপনি নিজের বেশিরভাগ UI-এর জন্য আগে থেকে তৈরি কম্পোনেন্ট ব্যবহার করতে পারবেন, তবে আরও কন্ট্রোল
প্রয়োজন হলে কাস্টম ইমপ্লিমেন্টেশনের জন্য একটি কম্পোনেন্ট
বদল করে নিতে পারবেন। এই সময়
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 কম্পোজ করার উপযুক্ত আইটেম সম্পূর্ণ মিডিয়া প্লেব্যাক
অভিজ্ঞতা প্রদান করে। এর কন্টেন্ট স্লটে আপনার নিজস্ব কম্পোজেবল প্রদান করে
আপনি এর লেআউট কাস্টমাইজ করতে পারেন অথবা UI-এর নির্দিষ্ট অংশ কাস্টমাইজ করতে
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 ) }
UI স্টেট হোল্ডার
স্ক্যাফোল্ডিং কম্পোনেন্টগুলির মধ্যে কোনওটি আপনার প্রয়োজন পূরণ করতে না পারলে, আপনি সরাসরি
স্টেট অবজেক্টও ব্যবহার করতে পারেন। সাধারণত, রিকম্পোজিশনের মধ্যে আপনার UI লুক বজায় রাখতে, সংশ্লিষ্ট
remember পদ্ধতি ব্যবহার করার পরামর্শ দেওয়া হয়।
আপনি কীভাবে কম্পোজ করার যোগ্য আইটেমের তুলনায় UI স্টেট হোল্ডারদের নমনীয়তা ব্যবহার করতে পারবেন তা আরও ভালোভাবে বুঝতে, Compose কীভাবে স্টেট ম্যানেজ করে সেই সম্পর্কে পড়ুন।
বোতাম স্টেট হোল্ডার
কিছু UI স্টেটের ক্ষেত্রে, লাইব্রেরি ধরে নেয় যে সেগুলি বেশিরভাগ ক্ষেত্রে বাটন-লাইক কম্পোজ করার উপযুক্ত আইটেম দ্বারা ব্যবহার করা হবে।
| রাজ্য | মনে রেখো*স্টেট | ধরন |
|---|---|---|
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
সেট করা সম্পর্কে তথ্য দেখায়। আপনার কাস্টম UI-তে মেটাডেটা সংক্রান্ত তথ্য দেখানোর জন্য এগুলি
উপযোগী।
ErrorState প্লেয়ারের বর্তমান সমস্যার অবস্থা সম্পর্কে তথ্য প্রদান করে,
যা সমস্যার ওভারলে দেখানোর জন্য ব্যবহার করা যেতে পারে।
PresentationState-এ সেইসব তথ্য থাকে, যখন ভিডিও আউটপুট
PlayerSurface দেখানো যেতে পারে বা প্লেসহোল্ডার UI এলিমেন্ট দিয়ে ঢেকে দেওয়া উচিত।
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-কে বেছে নেওয়া অ্যাস্পেক্ট রেশিওতে স্কেল করতে পারি (আরও ধরনের জন্য কন্টেন্ট স্কেল দেখুন) এবং
presentationState.coverSurface ব্যবহার করে জানতে পারি যে Surface দেখানোর
জন্য কখন সময় সঠিক নয়। এই ক্ষেত্রে, আপনি সারফেসের উপরে
একটি অস্বচ্ছ শাটার পজিশন করতে পারেন, যা সারফেস রেডি হয়ে গেলে অদৃশ্য হয়ে যাবে।
ContentFrame আপনাকে ট্রেলিং ল্যাম্বডা হিসেবে শাটার কাস্টমাইজ করতে দেয়, কিন্তু
ডিফল্ট হিসেবে এটি একটি কালো @Composable Box যা প্যারেন্ট
কন্টেনারের সাইজ পূরণ করে।
Flow কোথায় আছে?
Android ডেভেলপারদের মধ্যে অনেকেই, সবসময় পরিবর্তন হতে থাকা UI ডেটা সংগ্রহ করার জন্য Kotlin Flow অবজেক্ট ব্যবহার করার ব্যাপারে
পরিচিত। যেমন, আপনি এমন
Player.isPlaying ফ্লো খুঁজতে পারেন যা লাইফসাইকেল-সচেতন পদ্ধতিতে collect যায়। অথবা
Player.eventsFlow-এর মতো কিছু যা আপনাকে Flow<Player.Events>
প্রদান করবে যা আপনি যেভাবে চান filter করতে পারবেন।
তবে, Player UI স্টেটের জন্য ফ্লো ব্যবহার করার কিছু অসুবিধা আছে। ডেটা ট্রান্সফারের অ্যাসিঙ্ক্রোনাস নেচার হল অন্যতম প্রধান
চিন্তার বিষয়। আমরা Player.Event এবং UI-এর
দিকে সেটির ব্যবহারের মধ্যে যতটা সম্ভব কম
লেটেন্সি রাখতে চাই, যাতে Player-এর সাথে সিঙ্ক না হওয়া UI এলিমেন্ট দেখানো এড়ানো যায়।
অন্যান্য পয়েন্টের মধ্যে রয়েছে:
- সব
Player.Eventsসহ ফ্লো একটিমাত্র দায়িত্বের নীতি মেনে চলে না, প্রতিটি কনজিউমারকে প্রাসঙ্গিক ইভেন্ট ফিল্টার করতে হবে। - প্রতিটি
Player.Event-এর জন্য ফ্লো তৈরি করতে, আপনাকে প্রতিটি UI এলিমেন্টের জন্য সেগুলিকে (combine-এর সাথে) একত্রিত করতে হবে। Player.Event এবং UI এলিমেন্ট পরিবর্তনের মধ্যে অনেক-থেকে-অনেক ম্যাপিং আছে।combineব্যবহার করতে হলে UI সম্ভাব্য বেআইনি স্টেটে চলে যেতে পারে।
কাস্টম UI স্টেট তৈরি করা
আগে থেকে থাকা UI স্টেট আপনার প্রয়োজন পূরণ না করলে, আপনি কাস্টম UI স্টেট যোগ করতে পারবেন। প্যাটার্ন কপি করতে, আগে থেকে থাকা স্টেটের সোর্স কোড চেক করে দেখুন। একটি সাধারণ UI স্টেট হোল্ডার ক্লাস নিম্নলিখিত কাজগুলি করে:
Playerঅবজেক্ট নেয়।- করুটিন ব্যবহার করে
Player-এ সাবস্ক্রাইব করে। আরও বিবরণের জন্যPlayer.listenদেখুন। - এর ইন্টার্নাল স্টেট আপডেট করে নির্দিষ্ট
Player.Events-এর উত্তর দেয়। - বিজনেস-লজিক কমান্ড গ্রহণ করে যা উপযুক্ত
Playerআপডেটে রূপান্তরিত হবে। - UI ট্রি জুড়ে একাধিক জায়গায় তৈরি করা যেতে পারে এবং সবসময় প্লেয়ারের স্ট্যাটাসের একটি সামঞ্জস্যপূর্ণ ভিউ বজায় রাখবে।
- পরিবর্তনের ব্যাপারে ডাইনামিক উত্তর দেওয়ার জন্য কম্পোজ করার উপযুক্ত আইটেম দ্বারা ব্যবহার করা যেতে পারে এমন 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 শুনতে দেয়। বিভিন্ন UI
স্টেটের Media3 প্রয়োগ, এন্ড ডেভেলপারকে
Player.Events সম্পর্কে জানার ব্যাপারে চিন্তা করতে দেয় না।