แนวคิดและการใช้งาน Jetpack Compose
บทบาทของ UI คือการแสดงข้อมูลแอปพลิเคชันบนหน้าจอ รวมถึงเป็นจุดหลักของการโต้ตอบของผู้ใช้ เมื่อใดก็ตามที่ข้อมูลมีการเปลี่ยนแปลง ไม่ว่าจะเป็นเนื่องจากการโต้ตอบของผู้ใช้ (เช่น การกดปุ่ม) หรืออินพุตภายนอก (เช่น การตอบสนองของเครือข่าย) UI ควรได้รับการอัปเดตเพื่อแสดงการเปลี่ยนแปลงเหล่านั้น กล่าวคือ UI เป็นการแสดงภาพของสถานะแอปพลิเคชันที่ดึงมาจากชั้นข้อมูล
อย่างไรก็ตาม ข้อมูลแอปพลิเคชันที่คุณได้รับจากชั้นข้อมูลมักจะอยู่ในรูปแบบที่แตกต่างจากข้อมูลที่คุณต้องแสดง ตัวอย่างเช่น คุณอาจต้องการข้อมูลเพียงบางส่วนสำหรับ UI หรืออาจต้องผสานแหล่งข้อมูล 2 แหล่งที่แตกต่างกันเพื่อนำเสนอข้อมูลที่เกี่ยวข้องกับผู้ใช้ ไม่ว่าคุณจะใช้ตรรกะใดก็ตาม คุณต้องส่งข้อมูลทั้งหมดที่ UI ต้องการเพื่อแสดงผลอย่างสมบูรณ์ เลเยอร์ UI เป็นไปป์ไลน์ที่แปลงการเปลี่ยนแปลงข้อมูลแอปพลิเคชันให้อยู่ในรูปแบบที่ UI สามารถนำเสนอได้ แล้วจึงแสดงข้อมูลนั้น
แสดงสถานะ UI
หลังจากกำหนดสถานะ UI และกำหนดวิธีจัดการการสร้างสถานะดังกล่าวแล้ว ขั้นตอนถัดไปคือการนำเสนอสถานะที่สร้างขึ้นต่อ UI เนื่องจากคุณใช้ UDF เพื่อจัดการการสร้างสถานะ คุณจึงพิจารณาสถานะที่สร้างขึ้นเป็นสตรีมได้ ซึ่งหมายความว่าจะมีการสร้างสถานะหลายเวอร์ชันขึ้นเมื่อเวลาผ่านไป ด้วยเหตุนี้ คุณจึงควรแสดงสถานะ UI ในที่เก็บข้อมูลที่สังเกตได้ เช่น LiveData หรือ StateFlow เหตุผลก็คือเพื่อให้ UI สามารถตอบสนองต่อการเปลี่ยนแปลงใดๆ ที่เกิดขึ้นในสถานะได้โดยไม่ต้องดึงข้อมูลจาก ViewModel ด้วยตนเองโดยตรง นอกจากนี้ ประเภทเหล่านี้ยังมีประโยชน์ในการแคชสถานะ UI เวอร์ชันล่าสุดไว้เสมอ ซึ่งมีประโยชน์สำหรับการกู้คืนสถานะอย่างรวดเร็วหลังจากการเปลี่ยนแปลงการกำหนดค่า
class NewsViewModel(...) : ViewModel() {
val uiState: StateFlow<NewsUiState> = …
}
วิธีทั่วไปในการสร้างสตรีม UiState คือการแสดงสตรีมที่เปลี่ยนแปลงได้
เป็นสตรีมที่เปลี่ยนแปลงไม่ได้จาก ViewModel เช่น การแสดง
MutableStateFlow<UiState> เป็น StateFlow<UiState>
class NewsViewModel(...) : ViewModel() {
private val _uiState = MutableStateFlow(NewsUiState())
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
...
}
จากนั้น ViewModel จะแสดงเมธอดที่เปลี่ยนแปลงสถานะภายใน โดยเผยแพร่การอัปเดตเพื่อให้ UI ใช้ ตัวอย่างเช่น ในกรณีที่ต้องดำเนินการแบบอะซิงโครนัส คุณสามารถเปิดใช้โครูทีนได้โดยใช้
viewModelScope และ
อัปเดตสถานะที่เปลี่ยนแปลงได้เมื่อดำเนินการเสร็จสมบูรณ์
class NewsViewModel(
private val repository: NewsRepository,
...
) : ViewModel() {
private val _uiState = MutableStateFlow(NewsUiState())
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
private var fetchJob: Job? = null
fun fetchArticles(category: String) {
fetchJob?.cancel()
fetchJob = viewModelScope.launch {
try {
val newsItems = repository.newsItemsForCategory(category)
_uiState.update {
it.copy(newsItems = newsItems)
}
} catch (ioe: IOException) {
// Handle the error and notify the UI when appropriate.
_uiState.update {
val messages = getMessagesFromThrowable(ioe)
it.copy(userMessages = messages)
}
}
}
}
}
ใช้สถานะ UI
เมื่อใช้ที่เก็บข้อมูลที่สังเกตได้ใน UI โปรดคำนึงถึงวงจรการทำงานของ UI ซึ่งเป็นสิ่งสำคัญเนื่องจาก UI ไม่ควรสังเกตสถานะ UI เมื่อไม่ได้แสดงมุมมองต่อผู้ใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ได้ใน บล็อก
โพสต์นี้
เมื่อใช้ LiveData นั้น LifecycleOwner จะจัดการข้อกังวลเกี่ยวกับวงจรการทำงานโดยนัย เมื่อใช้โฟลว์ วิธีที่ดีที่สุดคือการจัดการเรื่องนี้ด้วยขอบเขตโครูทีนที่เหมาะสมและ API repeatOnLifecycle ดังนี้
class NewsActivity : AppCompatActivity() {
private val viewModel: NewsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Update UI elements
}
}
}
}
}
แสดงการดำเนินการที่อยู่ระหว่างดำเนินการ
วิธีง่ายๆ ในการแสดงสถานะการโหลดในคลาส UiState คือการใช้ฟิลด์บูลีน ดังนี้
data class NewsUiState(
val isFetchingArticles: Boolean = false,
...
)
ค่าของแฟล็กนี้แสดงถึงการมีหรือไม่มีแถบความคืบหน้าใน UI
class NewsActivity : AppCompatActivity() {
private val viewModel: NewsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Bind the visibility of the progressBar to the state
// of isFetchingArticles.
viewModel.uiState
.map { it.isFetchingArticles }
.distinctUntilChanged()
.collect { progressBar.isVisible = it }
}
}
}
}
ภาพเคลื่อนไหว
หากต้องการให้การเปลี่ยนผ่านการนำทางระดับบนสุดเป็นไปอย่างราบรื่น คุณอาจต้องรอให้หน้าจอที่ 2 โหลดข้อมูลก่อนที่จะเริ่มภาพเคลื่อนไหว
เฟรมเวิร์กมุมมอง Android มีฮุกเพื่อหน่วงเวลาการเปลี่ยนผ่านระหว่างปลายทางของ Fragment
ด้วย
postponeEnterTransition()
และ
startPostponedEnterTransition()
API API เหล่านี้ช่วยให้มั่นใจได้ว่าองค์ประกอบ UI ในหน้าจอที่ 2 (โดยปกติจะเป็นรูปภาพที่ดึงมาจากเครือข่าย) พร้อมที่จะแสดงก่อนที่ UI จะแสดงภาพเคลื่อนไหวการเปลี่ยนผ่านไปยังหน้าจอนั้น
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- การสร้างสถานะ UI
- ที่เก็บสถานะและสถานะ UI {:#mad-arch}
- คู่มือสถาปัตยกรรมแอป