media3-ui-compose লাইব্রেরিটি Jetpack Compose-এ একটি মিডিয়া UI তৈরির জন্য মৌলিক উপাদানগুলি প্রদান করে। এটি এমন ডেভেলপারদের জন্য ডিজাইন করা হয়েছে যাদের media3-ui-compose-material3 লাইব্রেরির চেয়ে বেশি কাস্টমাইজেশন প্রয়োজন। এই পৃষ্ঠাটি ব্যাখ্যা করে যে কীভাবে একটি কাস্টম মিডিয়া প্লেয়ার UI তৈরি করতে মূল উপাদান এবং স্টেট হোল্ডার ব্যবহার করতে হয়।
ম্যাটেরিয়াল৩ এবং কাস্টম কম্পোজ উপাদানের মিশ্রণ
media3-ui-compose-material3 লাইব্রেরিটি নমনীয়ভাবে ডিজাইন করা হয়েছে। আপনি আপনার বেশিরভাগ UI এর জন্য পূর্বনির্মিত উপাদানগুলি ব্যবহার করতে পারেন, তবে যখন আপনার আরও নিয়ন্ত্রণের প্রয়োজন হয় তখন একটি একক উপাদানকে কাস্টম বাস্তবায়নের জন্য অদলবদল করুন। এই সময় media3-ui-compose লাইব্রেরি কার্যকর হয়।
উদাহরণস্বরূপ, কল্পনা করুন যে আপনি Material3 লাইব্রেরি থেকে স্ট্যান্ডার্ড PreviousButton এবং NextButton ব্যবহার করতে চান, কিন্তু আপনার একটি সম্পূর্ণ কাস্টম PlayPauseButton প্রয়োজন। আপনি core 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 কে মোড়ানো। |
UI স্টেট হোল্ডাররা
যদি কোনও স্ক্যাফোল্ডিং উপাদান আপনার চাহিদা পূরণ না করে, তাহলে আপনি সরাসরি স্টেট অবজেক্ট ব্যবহার করতে পারেন। রিকম্পোজিশনের মধ্যে আপনার UI লুক সংরক্ষণের জন্য সাধারণত সংশ্লিষ্ট remember পদ্ধতিগুলি ব্যবহার করা যুক্তিযুক্ত।
UI স্টেট হোল্ডার বনাম Composables-এর নমনীয়তা কীভাবে ব্যবহার করবেন তা আরও ভালোভাবে বুঝতে, Compose কীভাবে State পরিচালনা করে সে সম্পর্কে পড়ুন।
বোতাম স্টেট হোল্ডার
কিছু UI রাজ্যের জন্য, লাইব্রেরি অনুমান করে যে সেগুলি সম্ভবত বোতাম-সদৃশ কম্পোজেবল দ্বারা গ্রাস করা হবে।
| রাজ্য | মনে রাখবেন*রাষ্ট্র | আদর্শ |
|---|---|---|
PlayPauseButtonState | rememberPlayPauseButtonState | 2-টগল করুন |
PreviousButtonState | rememberPreviousButtonState | ধ্রুবক |
NextButtonState | rememberNextButtonState | ধ্রুবক |
RepeatButtonState | rememberRepeatButtonState | 3-টগল করুন |
ShuffleButtonState | rememberShuffleButtonState | 2-টগল করুন |
PlaybackSpeedState | rememberPlaybackSpeedState | মেনু অথবা এন-টগল |
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), ) }
ভিজ্যুয়াল আউটপুট স্টেট হোল্ডার
PresentationState কখন PlayerSurface এর ভিডিও আউটপুট দেখানো যেতে পারে বা কখন প্লেসহোল্ডার UI উপাদান দ্বারা আচ্ছাদিত করা উচিত তার তথ্য ধারণ করে। 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.videoSizeDp) // Always leave PlayerSurface to be part of the Compose tree because it will be initialised 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.videoSizeDp ব্যবহার করে সারফেসকে নির্বাচিত আকৃতির অনুপাতের সাথে স্কেল করতে পারি (আরও ধরণের জন্য ContentScale ডক্স দেখুন) এবং presentationState.coverSurface ব্যবহার করে জানতে পারি কখন সারফেস দেখানোর সময় সঠিক নয়। এই ক্ষেত্রে, আপনি সারফেসের উপরে একটি অস্বচ্ছ শাটার রাখতে পারেন, যা সারফেস প্রস্তুত হয়ে গেলে অদৃশ্য হয়ে যাবে। ContentFrame আপনাকে শাটারটিকে একটি ট্রেলিং ল্যাম্বডা হিসাবে কাস্টমাইজ করতে দেয়, তবে ডিফল্টরূপে এটি একটি কালো @Composable Box হবে যা মূল ধারকের আকার পূরণ করবে।
ফ্লোস কোথায়?
অনেক অ্যান্ড্রয়েড ডেভেলপারই Kotlin Flow অবজেক্ট ব্যবহার করে ক্রমবর্ধমান UI ডেটা সংগ্রহের সাথে পরিচিত। উদাহরণস্বরূপ, আপনি হয়তো 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 এলিমেন্টের মধ্যে many-to-many ম্যাপিং পরিবর্তন হয়।combineব্যবহার করার ফলে UI সম্ভাব্য অবৈধ অবস্থায় চলে যেতে পারে।
কাস্টম UI অবস্থা তৈরি করুন
যদি বিদ্যমান UI স্টেটগুলি আপনার চাহিদা পূরণ না করে তবে আপনি কাস্টম UI স্টেটগুলি যোগ করতে পারেন। প্যাটার্নটি অনুলিপি করতে বিদ্যমান স্টেটের সোর্স কোডটি দেখুন। একটি সাধারণ UI স্টেট হোল্ডার ক্লাস নিম্নলিখিত কাজগুলি করে:
- একজন
Playerদলে নেয়। - কোরোটিন ব্যবহার করে
Playerসাবস্ক্রাইব করুন। আরও বিস্তারিত জানার জন্যPlayer.listenদেখুন। - নির্দিষ্ট
Player.Eventsএর অভ্যন্তরীণ অবস্থা আপডেট করে সাড়া দেয়। - ব্যবসায়িক-লজিক কমান্ড গ্রহণ করে যা একটি উপযুক্ত
Playerআপডেটে রূপান্তরিত হবে। - UI ট্রি জুড়ে একাধিক জায়গায় তৈরি করা যেতে পারে এবং সর্বদা প্লেয়ারের অবস্থার একটি সামঞ্জস্যপূর্ণ দৃষ্টিভঙ্গি বজায় রাখবে।
- কম্পোজ
Stateক্ষেত্রগুলি প্রকাশ করে যা একটি কম্পোজেবল দ্বারা পরিবর্তনগুলিতে গতিশীলভাবে প্রতিক্রিয়া জানাতে ব্যবহার করা যেতে পারে। - কম্পোজিশনের মধ্যে উদাহরণ মনে রাখার জন্য একটি
remember*Stateফাংশনের সাথে আসে।
পর্দার আড়ালে কী ঘটে:
class SomeButtonState(private val player: Player) {
var isEnabled by mutableStateOf(player.isCommandAvailable(Player.COMMAND_ACTION_A))
private set
var someField by mutableStateOf(someFieldDefault)
private set
fun onClick() {
player.actionA()
}
suspend fun observe() =
player.listen { events ->
if (
events.containsAny(
Player.EVENT_B_CHANGED,
Player.EVENT_C_CHANGED,
Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
)
) {
someField = this.someField
isEnabled = this.isCommandAvailable(Player.COMMAND_ACTION_A)
}
}
}
আপনার নিজস্ব Player.Events এ প্রতিক্রিয়া জানাতে, আপনি Player.listen ব্যবহার করে তাদের ধরতে পারেন যা একটি suspend fun যা আপনাকে coroutine জগতে প্রবেশ করতে এবং অনির্দিষ্টকালের জন্য Player.Events শুনতে দেয়। বিভিন্ন UI অবস্থার Media3 বাস্তবায়ন শেষ ডেভেলপারকে Player.Events সম্পর্কে শেখার বিষয়ে উদ্বিগ্ন না হতে সাহায্য করে।