Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> ระบบ >> Android

เชี่ยวชาญการจัดการ UI Events ใน Jetpack Compose

บทความสั้นและปฏิบัติจริงนี้จะพูดถึงการจัดการ UI events ใน Jetpack Compose

ในระบบเก่าใช้ OnClickListeners และ interfaces อื่นๆ ใน Compose ใช้ประโยชน์จาก Sealed Classes, Function Types และ Lambda Expressions ของ Kotlin เต็มที่

หากไม่รู้ composable คืออะไร อ่านบทความพื้นฐานก่อน

บทความนี้สรุปในวิดีโอสั้น 3 นาที

สร้างโมเดล UI Events ด้วย Sealed Class

ก่อนอื่น ต้องเข้าใจ UI Events และการสร้างโมเดลด้วย Sealed Classes

เคยอธิบายกระบวนการนี้สำหรับ Java และ Kotlin (ระบบ View เก่า) ไปแล้ว จะทำให้สั้นที่สุด

กระบวนการ

สำหรับแต่ละหน้าจอหรือ sub-screen ถามตัวเอง: ผู้ใช้โต้ตอบกับมันได้อย่างไรบ้าง?

ตัวอย่างจากแอปแรกของผมที่สร้างด้วย Compose ทั้งหมด Graph Sudoku:

เชี่ยวชาญการจัดการ UI Events ใน Jetpack Compose
Screenshot แอป Sudoku Android

Sealed class สำหรับ UI interactions ของหน้าจอนี้:

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: lifecycle events ที่ composables ไม่สนใจ แต่ใช้ใน Activity ที่เป็น Container

หลังมี sealed class แล้ว จัดการ events หลากหลายได้ด้วย event handler function เดียว บางครั้งอาจควรมี multiple handler functions ขึ้นอยู่กับ requirements ของโปรเจกต์

เชื่อมต่อ Software Architecture

สิ่งที่จัดการ events เป็นอะไรก็ได้ บางคนคิด MVVM เป็น gold standard แต่คนมากขึ้นตระหนักว่า ไม่มี architecture เดียวที่เหมาะกับทุกสถานการณ์

สำหรับ Android + Compose วิธีของผมคือ minimalist approach ที่มีในแต่ละ feature (screen):

  • (Presentation) Logic class เป็น event handler
  • ViewModel เก็บ data สำหรับ render View
  • Activity เป็น Container (ไม่ใช่ god object)
  • Composables สร้าง View
เชี่ยวชาญการจัดการ UI Events ใน Jetpack Compose
Model-View-Whatever

ไม่ว่าจะใช้ ViewModel, Fragment, หรือ Activity เป็น event handler ทั้งหมดตั้งค่าได้เหมือนกัน: Function Types!

ใน class ที่เลือก ตั้งค่า event handler function รับ sealed class เป็น argument:

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) } } //... }

วิธีนี้ organized และทดสอบทุก Unit ใน class ไม่มี library ได้ผ่าน single entry point

แต่ยังไม่จบ ต้องการ reference ของ event handler function onEvent ไปยัง Composables ใช้ function reference:

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) } //... }

บางคนถามทำไมใช้ Activity คำตอบรายละเอียดใน livestream Q&A

สรุป: Fragments ดูไร้ประโยชน์กับ Compose ใน architecture ของผม (ไม่ใช้ Jetpack Navigation) และไม่มีปัญหาใช้ Activities เป็น feature-specific container แค่หลีกเลี่ยง god activities

วิธีอ้างอิง function ใน Kotlin: ให้ class/interface name (หรือข้ามถ้าเป็น Top-Level function) ตามด้วย สอง colon และ ชื่อฟังก์ชันโดยไม่มี arguments หรือ brackets:

onEventHandler = logic::onEvent

แทนที่ onClickListener ด้วย Jetpack Compose onClick Modifier

พร้อมแล้ว มาดูใน composable Root composable ต้องมี event handler function เป็น parameter:

@Composable fun ActiveGameScreen( onEventHandler: (ActiveGameEvent) -> Unit, viewModel: ActiveGameViewModel ) { //... }

Function type syntax อาจ tricky แต่เข้าใจว่า นี่คือ reference ไปยังฟังก์ชัน ไม่ต่างจาก reference ไปยัง class มากนัก

เช่นกัน ไม่ควรสร้าง giant composables:

  1. แยก UI เป็น smallest reasonable parts
  2. ห่อใน composable function
  3. Composable ที่มี UI interaction ต้องได้รับ reference ของ event handler function

Composable สำหรับปุ่มป้อนข้อมูล Sudoku ที่ได้รับ event handler โดย reference:

@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() ) } }

เพื่อส่ง event ไป logic class ใช้ฟังก์ชัน invoke ซึ่งรับ arguments ตาม function type definition (รับ ActiveGameEvent ในกรณีนี้)

ตอนนี้คุณพร้อมจัดการ UI interaction events ใน Kotlin (Compose หรือไม่ก็ตาม) โดยใช้ประโยชน์จากภาษาโปรแกรมมิ่งทันสมัยและสวยงามนี้

ถ้าชอบบทความนี้ แชร์บนโซเชียลมีเดีย และดูทรัพยากรด้านล่างเพื่อสนับสนุนนักเขียนและ content creator อิสระ

Social

หาผมได้ที่ Instagram และ Twitter

บทช่วยสอนและคอร์สของผม

https://youtube.com/wiseass     https://www.freecodecamp.org/news/author/ryan-michael-kay/ https://skl.sh/35IdKsj (introduction to Android with Android Studio)