ในบทความสั้นและใช้งานได้จริงนี้ เราจะพูดถึงวิธีจัดการกับเหตุการณ์ UI ใน Jetpack Compose
ในระบบเก่า เราใช้ OnClickListeners และอินเทอร์เฟซอื่นๆ ใน Compose เราสามารถใช้ประโยชน์จาก คลาสที่ปิดผนึกของ Kotlin ได้อย่างเต็มที่ , ประเภทฟังก์ชัน และ นิพจน์แลมบ์ดา .
หากคุณไม่ทราบว่าการเรียบเรียงคืออะไร ให้ลองอ่านบทความนี้ซึ่งอธิบายพื้นฐานต่างๆ
วิธีสร้างโมเดลเหตุการณ์ UI ด้วยคลาสที่ปิดผนึก
อันดับแรก เราต้องเรียนรู้ความหมายของเหตุการณ์ UI และวิธีสร้างโมเดลด้วยคลาสที่ปิดผนึก
ฉันได้อธิบายกระบวนการเดียวกันนี้สำหรับ Java และ Kotlin (ด้วยระบบการดูแบบเก่า) มาก่อน ดังนั้นฉันจะเก็บบทสรุปนี้ไว้
กระบวนการ
สำหรับแต่ละหน้าจอหรือหน้าจอย่อยของ UI ของคุณ ให้ถามตัวเองว่า:ผู้ใช้โต้ตอบกับหน้าจอด้วยวิธีต่างๆ ได้อย่างไร
มาดูตัวอย่างจากแอปแรกของฉันที่สร้างขึ้นในการเขียนแบบสมบูรณ์ Graph Sudoku:
คลาสปิดผนึกที่ฉันใช้แสดงการโต้ตอบ 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()
}
อธิบายสั้นๆ:
- OnInput แสดงถึงผู้ใช้ที่แตะปุ่มป้อนข้อมูล (เช่น 0, 1, 2, 3, 4)
- OnTileFocused แสดงถึงผู้ใช้ที่เลือกไทล์ (เช่น สีเหลืองที่ไฮไลต์)
- OnNewGameClicked เป็นตัวอธิบาย
- OnStart และ OnStop เป็นเหตุการณ์วงจรชีวิตที่ composables ของฉันไม่สนใจ แต่มันถูกใช้ในกิจกรรมซึ่งทำหน้าที่เป็นคอนเทนเนอร์สำหรับ composables
เมื่อคุณตั้งค่าคลาสที่ปิดผนึกแล้ว ตอนนี้คุณสามารถจัดการกับเหตุการณ์ที่หลากหลายโดยใช้ฟังก์ชันตัวจัดการเหตุการณ์เดียว บางครั้งอาจเหมาะสมกว่าที่จะมีฟังก์ชันตัวจัดการเหตุการณ์หลายฟังก์ชัน ดังนั้นโปรดทราบว่า วิธีการนี้ต้องปรับให้เข้ากับข้อกำหนดเฉพาะของโครงการของคุณ .
วิธีเชื่อมต่อสถาปัตยกรรมซอฟต์แวร์ของคุณ
สิ่งที่คุณจัดการกับเหตุการณ์เหล่านี้ขึ้นอยู่กับคุณโดยสิ้นเชิง บางคนคิดว่า MVVM เป็นมาตรฐานทองคำของสถาปัตยกรรมซอฟต์แวร์ แต่ดูเหมือนว่าผู้คนจำนวนมากขึ้นตระหนักว่าไม่มีสถาปัตยกรรมใดที่ทำงานได้ดีที่สุดสำหรับทุกสถานการณ์ .
สำหรับ Android ที่มี Compose แนวทางปัจจุบันของฉันคือการใช้แนวทางมินิมอลของบุคคลที่สามซึ่งโดยทั่วไปจะมีสิ่งเหล่านี้ในแต่ละฟีเจอร์ (หน้าจอ):
- คลาสลอจิก A (การนำเสนอ) เป็นตัวจัดการเหตุการณ์
- ViewModel เพื่อเก็บข้อมูลที่จำเป็นในการแสดงผล View (ตามที่ชื่อบอก)
- กิจกรรมที่ทำหน้าที่เป็นภาชนะ (ไม่ใช่วัตถุของพระเจ้า)
- คอมโพสิทเพื่อสร้างมุมมอง
ฉันไม่สนใจสิ่งที่คุณใช้ตราบเท่าที่คุณใช้การแยกข้อกังวล นี่คือวิธีที่ฉันมาถึงสถาปัตยกรรมนี้ โดยเพียงแค่ถามว่าอะไรควรและไม่ควรรวมไว้ในคลาสเดียวกัน
ไม่ว่าคุณจะต้องการให้ ViewModel, Fragment หรือ Activity เป็นตัวจัดการเหตุการณ์ ทั้งหมดก็สามารถตั้งค่าได้ในลักษณะเดียวกัน:ประเภทฟังก์ชัน!
ภายในคลาสที่คุณเลือก ให้ตั้งค่าฟังก์ชันตัวจัดการเหตุการณ์ซึ่งยอมรับคลาสที่ปิดผนึกเป็นอาร์กิวเมนต์:
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
, ถึง Composables ของเรา เราสามารถทำได้โดยใช้ การอ้างอิงฟังก์ชัน :
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)
}
//...
}
ฉันแน่ใจว่าพวกคุณบางคนสงสัยว่าทำไมฉันถึงใช้กิจกรรม คุณสามารถถามฉันในช่วงถาม &ตอบในการสตรีมแบบสดเพื่อรับคำตอบโดยละเอียดได้
กล่าวโดยย่อ Fragments ดูเหมือนจะไม่มีจุดหมายเล็กน้อยกับ Compose กับแนวทางสถาปัตยกรรมของฉัน (ฉันไม่ได้ใช้ Jetpack Navigation) และไม่มีอะไรผิดปกติกับการใช้กิจกรรมเป็นคอนเทนเนอร์เฉพาะของฟีเจอร์ แค่หลีกเลี่ยงการเขียนกิจกรรมของพระเจ้าเป็นหลัก
เพื่อให้เฉพาะเจาะจง วิธีที่คุณอ้างอิงถึงฟังก์ชันใน Kotlin คือการให้ชื่อคลาส/อินเทอร์เฟซ (หรือ ข้ามไป ถ้าเป็นฟังก์ชันระดับบนสุด ) ตามด้วย สองทวิภาค และ ชื่อของฟังก์ชันที่ไม่มีอาร์กิวเมนต์หรือวงเล็บ :
onEventHandler = logic::onEvent
วิธีการแทนที่ onClickListener ด้วย Jetpack Compose onClick Modifier
เมื่อพร้อมแล้ว เราสามารถดูว่าสิ่งนี้ทำงานอย่างไรในคอมโพสิเอเบิล โดยปกติ รูทที่คอมไพล์ได้ของคุณจะต้องใช้ฟังก์ชันตัวจัดการเหตุการณ์เป็นพารามิเตอร์:
@Composable
fun ActiveGameScreen(
onEventHandler: (ActiveGameEvent) -> Unit,
viewModel: ActiveGameViewModel
) {
//...
}
การรับไวยากรณ์ประเภทฟังก์ชันอาจเป็นเรื่องยากเล็กน้อย แต่เข้าใจว่าเป็นการอ้างอิงถึงฟังก์ชันจริงๆ ซึ่งไม่ต่างจากการอ้างอิงถึงคลาสมากนัก
เช่นเดียวกับที่คุณไม่ควรสร้างวัตถุของพระเจ้า คุณไม่ควรสร้างส่วนประกอบขนาดยักษ์:
- แบ่ง UI ของคุณออกเป็น ส่วนที่เล็กที่สุดที่เหมาะสม
- ห่อไว้ในฟังก์ชันที่ประกอบได้
- สำหรับคอมโพสิทแต่ละรายการที่มีการโต้ตอบ UI ที่เกี่ยวข้อง ต้องมีการอ้างอิงถึงฟังก์ชันตัวจัดการเหตุการณ์ของคุณ
นี่คือส่วนประกอบที่แสดงถึงปุ่มอินพุตของแอป Sudoku ซึ่งได้รับตัวจัดการเหตุการณ์โดยการอ้างอิง:
@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 ใน Kotlin (เขียนหรือไม่) โดยการใช้ประโยชน์จากภาษาการเขียนโปรแกรมที่สวยงามและทันสมัยนี้อย่างเต็มที่
หากคุณชอบบทความนี้ แชร์บนโซเชียลมีเดียและพิจารณาดูแหล่งข้อมูลด้านล่างเพื่อสนับสนุนโปรแกรมเมอร์อิสระและผู้สร้างเนื้อหา
โซเชียล
คุณสามารถพบฉันบน Instagram ที่นี่และบน Twitter ที่นี่
ต่อไปนี้คือบทเรียนและหลักสูตรบางส่วนของฉัน
https://youtube.com/wiseass https://www.freecodecamp.org/news/author/ryan-michael-kay/ https://skl.sh/35IdKsj (แนะนำ Android พร้อม Android Studio)