Computer >> คอมพิวเตอร์ >  >> ระบบ >> Android

วิธีจัดการกับเหตุการณ์ UI ในการเขียน Jetpack

ในบทความสั้นและใช้งานได้จริงนี้ เราจะพูดถึงวิธีจัดการกับเหตุการณ์ UI ใน Jetpack Compose

ในระบบเก่า เราใช้ OnClickListeners และอินเทอร์เฟซอื่นๆ ใน Compose เราสามารถใช้ประโยชน์จาก คลาสที่ปิดผนึกของ Kotlin ได้อย่างเต็มที่ , ประเภทฟังก์ชัน และ นิพจน์แลมบ์ดา .

หากคุณไม่ทราบว่าการเรียบเรียงคืออะไร ให้ลองอ่านบทความนี้ซึ่งอธิบายพื้นฐานต่างๆ

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

วิธีสร้างโมเดลเหตุการณ์ UI ด้วยคลาสที่ปิดผนึก

อันดับแรก เราต้องเรียนรู้ความหมายของเหตุการณ์ UI และวิธีสร้างโมเดลด้วยคลาสที่ปิดผนึก

ฉันได้อธิบายกระบวนการเดียวกันนี้สำหรับ Java และ Kotlin (ด้วยระบบการดูแบบเก่า) มาก่อน ดังนั้นฉันจะเก็บบทสรุปนี้ไว้

กระบวนการ

สำหรับแต่ละหน้าจอหรือหน้าจอย่อยของ UI ของคุณ ให้ถามตัวเองว่า:ผู้ใช้โต้ตอบกับหน้าจอด้วยวิธีต่างๆ ได้อย่างไร

มาดูตัวอย่างจากแอปแรกของฉันที่สร้างขึ้นในการเขียนแบบสมบูรณ์ Graph Sudoku:

วิธีจัดการกับเหตุการณ์ UI ในการเขียน Jetpack
ภาพหน้าจอของแอป Sudoku Android

คลาสปิดผนึกที่ฉันใช้แสดงการโต้ตอบ 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 (ตามที่ชื่อบอก)
  • กิจกรรมที่ทำหน้าที่เป็นภาชนะ (ไม่ใช่วัตถุของพระเจ้า)
  • คอมโพสิทเพื่อสร้างมุมมอง
วิธีจัดการกับเหตุการณ์ UI ในการเขียน Jetpack
Model-View-Whatever

ฉันไม่สนใจสิ่งที่คุณใช้ตราบเท่าที่คุณใช้การแยกข้อกังวล นี่คือวิธีที่ฉันมาถึงสถาปัตยกรรมนี้ โดยเพียงแค่ถามว่าอะไรควรและไม่ควรรวมไว้ในคลาสเดียวกัน

ไม่ว่าคุณจะต้องการให้ 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
) {
//...
}

การรับไวยากรณ์ประเภทฟังก์ชันอาจเป็นเรื่องยากเล็กน้อย แต่เข้าใจว่าเป็นการอ้างอิงถึงฟังก์ชันจริงๆ ซึ่งไม่ต่างจากการอ้างอิงถึงคลาสมากนัก

เช่นเดียวกับที่คุณไม่ควรสร้างวัตถุของพระเจ้า คุณไม่ควรสร้างส่วนประกอบขนาดยักษ์:

  1. แบ่ง UI ของคุณออกเป็น ส่วนที่เล็กที่สุดที่เหมาะสม
  2. ห่อไว้ในฟังก์ชันที่ประกอบได้
  3. สำหรับคอมโพสิทแต่ละรายการที่มีการโต้ตอบ 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)