media3-ui-compose লাইব্রেরি Jetpack Compose-এ মিডিয়া UI তৈরি করার জন্য
প্রয়োজনীয় প্রাথমিক কম্পোনেন্ট প্রদান করে। এটি সেইসব ডেভেলপারদের জন্য ডিজাইন করা হয়েছে যাদের 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।
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 ) }
UI স্টেট হোল্ডার
স্ক্যাফোল্ডিং কম্পোনেন্টের কোনওটিই আপনার প্রয়োজন পূরণ না করলে, আপনি সরাসরি
স্টেট অবজেক্টও ব্যবহার করতে পারেন। রিকম্পোজিশনের মধ্যে আপনার UI-এর লুক বজায় রাখতে, সাধারণত সংশ্লিষ্ট
remember পদ্ধতি ব্যবহার করার পরামর্শ দেওয়া হয়।
কম্পোজেবলের তুলনায় UI স্টেট হোল্ডারের ফ্লেক্সিবিলিটি কীভাবে ব্যবহার করবেন তা আরও ভালোভাবে বুঝতে, Compose কীভাবে স্টেট ম্যানেজ করে সেই সম্পর্কে পড়ুন।
বোতামের স্টেট হোল্ডার
কিছু UI স্টেটের জন্য, লাইব্রেরি অনুমান করে যে সেগুলি সম্ভবত বোতামের মতো কম্পোজেবল দ্বারা ব্যবহৃত হবে।
| রাজ্য | মনে রাখবেন*স্টেট | ধরন |
|---|---|---|
PlayPauseButtonState |
rememberPlayPauseButtonState |
২-টগল |
PreviousButtonState |
rememberPreviousButtonState |
স্থির |
NextButtonState |
rememberNextButtonState |
স্থির |
RepeatButtonState |
rememberRepeatButtonState |
৩-টগল |
ShuffleButtonState |
rememberShuffleButtonState |
২-টগল |
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 সম্পর্কে তথ্য প্রকাশ করে। আপনার কাস্টম 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 (আরও ধরনের জন্য কন্টেন্ট স্কেল দেখুন) এবং
সারফেস দেখানোর জন্য কখন সময় সঠিক নয় তা জানতে
presentationState.coverSurface ব্যবহার করতে পারি। এই ক্ষেত্রে, আপনি সারফেসের উপরে একটি অস্বচ্ছ শাটার রাখতে পারেন, যা সারফেসটি প্রস্তুত হলে অদৃশ্য হয়ে যাবে।
ContentFrame আপনাকে ট্রেলিং ল্যাম্বডা হিসেবে শাটার কাস্টমাইজ করতে দেয়, তবে
ডিফল্ট হিসেবে এটি একটি কালো @Composable Box হবে যা পেরেন্ট
কন্টেনারের সাইজ পূরণ করবে।
Flows কোথায় আছে?
অনেক 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 সম্পর্কে জানার ব্যাপারে চিন্তা না করতে সাহায্য করে।