इस संक्षिप्त और व्यावहारिक लेख में, हम इस बारे में बात करेंगे कि Jetpack Compose में UI ईवेंट को कैसे हैंडल किया जाए।
पुराने सिस्टम में, हम OnClickListeners और अन्य इंटरफेस का उपयोग करते थे। कंपोज़ में, हम कोटलिन की सीलबंद कक्षाओं . का पूरा लाभ उठा सकते हैं , कार्य प्रकार और लैम्ब्डा एक्सप्रेशन ।
यदि आप नहीं जानते कि कंपोज़ेबल क्या है, तो इस लेख को पढ़ने पर विचार करें जो मूल सिद्धांतों की व्याख्या करता है।
यूआई इवेंट को एक सीलबंद क्लास के साथ कैसे मॉडल करें
सबसे पहले, हमें सीखना चाहिए कि UI ईवेंट का क्या अर्थ है और उन्हें सीलबंद कक्षाओं के साथ कैसे मॉडल किया जाए।
मैंने पहले जावा और कोटलिन (पुराने दृश्य प्रणाली के साथ) के लिए इसी प्रक्रिया का वर्णन किया है, इसलिए मैं इसे संक्षिप्त रखूंगा।
प्रक्रिया
अपने UI की प्रत्येक स्क्रीन या सब-स्क्रीन के लिए, अपने आप से यह प्रश्न पूछें:वे सभी विभिन्न तरीके क्या हैं जिनसे उपयोगकर्ता इसके साथ इंटरैक्ट कर सकता है?
आइए पूरी तरह से कंपोज़ में निर्मित मेरे पहले ऐप से एक उदाहरण लेते हैं, ग्राफ़ सुडोकू:
इस स्क्रीन के UI इंटरैक्शन का प्रतिनिधित्व करने के लिए मैं जिस सीलबंद वर्ग का उपयोग करता हूं वह इस तरह दिखता है:
sealed class ActiveGameEvent {
data class OnInput(val input: Int) : ActiveGameEvent()
data class OnTileFocused(val x: Int,
val y: Int) : ActiveGameEvent()
object OnNewGameClicked : ActiveGameEvent()
object OnStart : ActiveGameEvent()
object OnStop : ActiveGameEvent()
}
संक्षेप में समझाने के लिए:
- ऑनइनपुट एक इनपुट बटन को छूने वाले उपयोगकर्ता का प्रतिनिधित्व करता है (जैसे 0, 1, 2, 3, 4)
- OnTileFocused एक टाइल का चयन करने वाले उपयोगकर्ता का प्रतिनिधित्व करता है (जैसे एम्बर हाइलाइट किया गया)
- OnNewGameClicked स्व-व्याख्यात्मक है
- ऑनस्टार्ट और ऑनस्टॉप जीवनचक्र की घटनाएं हैं जिनकी मेरे कंपोज़ेबल्स परवाह नहीं करते हैं, लेकिन उनका उपयोग उस गतिविधि में किया जाता है जो कंपोज़ेबल्स के लिए एक कंटेनर के रूप में कार्य करती है
एक बार जब आप अपनी सीलबंद कक्षा स्थापित कर लेते हैं, तो अब आप एकल ईवेंट हैंडलर फ़ंक्शन का उपयोग करके विभिन्न प्रकार की घटनाओं को संभाल सकते हैं। कभी-कभी एक से अधिक ईवेंट हैंडलर फ़ंक्शन करना अधिक समझदारी भरा हो सकता है, इसलिए ध्यान रखें कि यह दृष्टिकोण आपके प्रोजेक्ट की विशिष्ट आवश्यकताओं के अनुकूल होना चाहिए ।
अपने सॉफ़्टवेयर आर्किटेक्चर को कैसे कनेक्ट करें
आप इन घटनाओं को क्या संभालते हैं, यह पूरी तरह आप पर निर्भर है। कुछ लोग सोचते हैं कि एमवीवीएम सॉफ्टवेयर आर्किटेक्चर का सुनहरा मानक है, लेकिन ऐसा लगता है कि अधिक से अधिक लोग यह महसूस कर रहे हैं कि कोई भी ऐसा आर्किटेक्चर नहीं है जो हर स्थिति के लिए सबसे अच्छा काम करता हो ।
कंपोज़ के साथ Android के लिए, मेरा वर्तमान दृष्टिकोण एक बहुत ही तृतीय पक्ष न्यूनतम दृष्टिकोण का उपयोग करना है जिसमें आमतौर पर प्रत्येक सुविधा (स्क्रीन) में ये चीजें होती हैं:
- ए (प्रस्तुति) तर्क वर्ग एक ईवेंट हैंडलर के रूप में
- दृश्य प्रस्तुत करने के लिए आवश्यक डेटा संग्रहीत करने के लिए एक व्यूमॉडल (जैसा कि नाम का तात्पर्य है)
- एक गतिविधि जो एक कंटेनर के रूप में कार्य करती है (भगवान की वस्तु नहीं)
- दृश्य बनाने के लिए कंपोज़ेबल
मुझे परवाह नहीं है कि आप क्या उपयोग करते हैं जब तक आप चिंताओं को अलग करने के लिए आवेदन कर रहे हैं। इस तरह से मैं इस वास्तुकला पर पहुंचा, बस यह पूछकर कि एक ही कक्षा में क्या रखा जाना चाहिए और क्या नहीं।
चाहे आप अपने व्यूमोडेल, एक फ्रैगमेंट, या एक गतिविधि को अपना ईवेंट हैंडलर बनाना चाहते हैं, उन सभी को एक ही तरह से सेट किया जा सकता है:फ़ंक्शन प्रकार!
अपनी पसंद के वर्ग के भीतर, एक ईवेंट हैंडलर फ़ंक्शन सेट करें जो आपके सीलबंद वर्ग को इसके तर्क के रूप में स्वीकार करता है:
class ActiveGameLogic(
private val container: ActiveGameContainer?,
private val viewModel: ActiveGameViewModel,
private val gameRepo: IGameRepository,
private val statsRepo: IStatisticsRepository,
dispatcher: DispatcherProvider
) : BaseLogic<ActiveGameEvent>(dispatcher),
CoroutineScope {
//...
override fun onEvent(event: ActiveGameEvent) {
when (event) {
is ActiveGameEvent.OnInput -> onInput(
event.input,
viewModel.timerState
)
ActiveGameEvent.OnNewGameClicked -> onNewGameClicked()
ActiveGameEvent.OnStart -> onStart()
ActiveGameEvent.OnStop -> onStop()
is ActiveGameEvent.OnTileFocused -> onTileFocused(event.x, event.y)
}
}
//...
}
यह दृष्टिकोण बहुत व्यवस्थित है और इस तृतीय पक्ष पुस्तकालय मुक्त कक्षा में प्रत्येक इकाई को एकल प्रवेश बिंदु के माध्यम से परीक्षण करना आसान बनाता है।
हालाँकि, हम अभी तक नहीं किए गए हैं। स्वाभाविक रूप से, हमें इस ईवेंट हैंडलर फ़ंक्शन का संदर्भ प्राप्त करने का एक तरीका चाहिए, onEvent
, हमारे कंपोज़ेबल्स के लिए। हम इसे फ़ंक्शन संदर्भ . का उपयोग करके कर सकते हैं :
class ActiveGameActivity : AppCompatActivity(), ActiveGameContainer {
private lateinit var logic: ActiveGameLogic
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = ActiveGameViewModel()
setContent {
ActiveGameScreen(
onEventHandler = logic::onEvent,
viewModel
)
}
logic = buildActiveGameLogic(this, viewModel, applicationContext)
}
//...
}
मुझे यकीन है कि आप में से कुछ लोग सोच रहे होंगे कि मैं गतिविधि का उपयोग क्यों कर रहा हूं। विस्तृत उत्तर के लिए आप कभी-कभी लाइवस्ट्रीम प्रश्नोत्तर के दौरान मुझसे पूछ सकते हैं।
संक्षेप में, संरचना के प्रति मेरे दृष्टिकोण के साथ रचना के साथ टुकड़े थोड़े व्यर्थ प्रतीत होते हैं (मैं जेटपैक नेविगेशन का उपयोग नहीं करता), और एक विशेषता विशिष्ट कंटेनर के रूप में गतिविधियों का उपयोग करने में कुछ भी गलत नहीं है। मूल रूप से ईश्वरीय गतिविधियों को लिखने से बचें।
विशिष्ट होने के लिए, जिस तरह से आप कोटलिन में किसी फ़ंक्शन का संदर्भ देते हैं, वह है वर्ग/इंटरफ़ेस नाम प्रदान करना (या इसे छोड़ दें यदि यह एक शीर्ष-स्तरीय फ़ंक्शन है ), उसके बाद दो कोलन , और बिना किसी तर्क या कोष्ठक के फ़ंक्शन का नाम :
onEventHandler = logic::onEvent
ऑनक्लिक लिस्टनर को जेटपैक कम्पोज़ ऑनक्लिक मॉडिफ़ायर से कैसे बदलें
उस सामान के तैयार होने के साथ, हम देख सकते हैं कि यह कंपोज़ेबल के भीतर कैसे काम करता है। स्वाभाविक रूप से, आपके रूट कंपोज़ेबल को एक पैरामीटर के रूप में ईवेंट हैंडलर फ़ंक्शन की आवश्यकता होगी:
@Composable
fun ActiveGameScreen(
onEventHandler: (ActiveGameEvent) -> Unit,
viewModel: ActiveGameViewModel
) {
//...
}
फ़ंक्शन प्रकार सिंटैक्स को सही ढंग से प्राप्त करना थोड़ा मुश्किल हो सकता है, लेकिन यह समझें कि यह वास्तव में एक फ़ंक्शन का संदर्भ है, जो किसी वर्ग के संदर्भ से इतना अलग नहीं है।
जिस तरह आपको ईश्वर की वस्तुओं का निर्माण नहीं करना चाहिए, उसी तरह आपको विशाल रचनाएँ नहीं बनानी चाहिए:
- अपने UI को सबसे छोटे उचित भागों में विभाजित करें
- उन्हें एक कंपोज़ेबल फंक्शन में लपेटें
- प्रत्येक कंपोज़ेबल के लिए जिसके साथ UI इंटरैक्शन जुड़ा हुआ है, इसे आपके ईवेंट हैंडलर फ़ंक्शन का संदर्भ दिया जाना चाहिए
यहाँ एक कंपोज़ेबल है जो सुडोकू ऐप के इनपुट बटन का प्रतिनिधित्व करता है, जिसे संदर्भ द्वारा ईवेंट हैंडलर दिया जाता है:
@Composable
fun SudokuInputButton(
onEventHandler: (ActiveGameEvent) -> Unit,
number: Int
) {
Button(
onClick = { onEventHandler.invoke(ActiveGameEvent.OnInput(number)) },
modifier = Modifier
.requiredSize(56.dp)
.padding(2.dp)
) {
Text(
text = number.toString(),
style = inputButton.copy(color = MaterialTheme.colors.onPrimary),
modifier = Modifier.fillMaxSize()
)
}
}
वास्तव में घटना को तर्क वर्ग में पास करने के लिए, हमें invoke
. का उपयोग करना चाहिए फ़ंक्शन, जो फ़ंक्शन प्रकार परिभाषा के अनुसार तर्क स्वीकार करेगा (जो एक ActiveGameEvent
. स्वीकार करता है इस मामले में)।
इस बिंदु पर, आप इस सुंदर और आधुनिक प्रोग्रामिंग भाषा का पूरा लाभ उठाकर कोटलिन में UI इंटरैक्शन ईवेंट (लिखें या नहीं) को संभालने के लिए तैयार हैं।
अगर आपको यह लेख पसंद आया है, तो इसे सोशल मीडिया पर साझा करें और एक स्वतंत्र प्रोग्रामर और सामग्री निर्माता का समर्थन करने के लिए नीचे दिए गए संसाधनों की जांच करने पर विचार करें।
सामाजिक
आप मुझे यहां इंस्टाग्राम पर और यहां ट्विटर पर ढूंढ सकते हैं।
यहां मेरे कुछ ट्यूटोरियल और कोर्स हैं
https://youtube.com/wiseass https://www.freecodecamp.org/news/author/ryan-michael-kay/ https://skl.sh/35IdKsj (एंड्रॉइड स्टूडियो के साथ एंड्रॉइड का परिचय)