เมื่อฉันเขียนบทความล่าสุดเกี่ยวกับ Jetpack Composer ฉันระบุไว้ที่นั่นว่า Jetpack Compose ขาดองค์ประกอบพื้นฐานบางส่วน (ในความคิดของฉัน) และหนึ่งในนั้นคือคำแนะนำเครื่องมือ
ในขณะนั้น ไม่มีองค์ประกอบในตัวที่จะแสดงคำแนะนำเครื่องมือ และมีโซลูชันทางเลือกมากมายในโลกออนไลน์ ปัญหาของโซลูชันเหล่านั้นคือเมื่อ Jetpack Compose เปิดตัวเวอร์ชันที่ใหม่กว่า โซลูชันเหล่านั้นอาจใช้งานไม่ได้ ดังนั้นมันจึงไม่เหมาะและชุมชนก็หวังกันว่าในอนาคตจะมีการเพิ่มการสนับสนุนสำหรับคำแนะนำเครื่องมือ
ฉันดีใจที่จะบอกว่าตั้งแต่เวอร์ชัน 1.1.0 ของ Compose Material 3 ตอนนี้เราได้สนับสนุนคำแนะนำเครื่องมือแล้ว 👏
แม้ว่าสิ่งนี้จะยอดเยี่ยมในตัวมันเอง แต่เวลาผ่านไปกว่าหนึ่งปีแล้วนับตั้งแต่เวอร์ชันนั้นเปิดตัว และในเวอร์ชันต่อๆ ไป API ที่เกี่ยวข้องกับคำแนะนำเครื่องมือก็เปลี่ยนไปอย่างมากเช่นกัน
หากคุณอ่านบันทึกการเปลี่ยนแปลง คุณจะเห็นว่า API สาธารณะและ API ภายในมีการเปลี่ยนแปลงอย่างไร ดังนั้น โปรดจำไว้ว่า เมื่อคุณอ่านบทความนี้ สิ่งต่างๆ อาจมีการเปลี่ยนแปลงอย่างต่อเนื่อง เนื่องจากทุกสิ่งที่เกี่ยวข้องกับคำแนะนำเครื่องมือยังคงถูกทำเครื่องหมายด้วยคำอธิบายประกอบ ExperimentalMaterial3Api::class .
❗️ เวอร์ชันของเนื้อหา 3 ที่ใช้สำหรับบทความนี้คือ 1.2.1 ซึ่งเผยแพร่เมื่อวันที่ 6 มีนาคม 2024
ขณะนี้เรามีการสนับสนุนคำแนะนำเครื่องมือสองประเภทที่แตกต่างกัน:
-
คำแนะนำเครื่องมือธรรมดา
-
เคล็ดลับเครื่องมือสื่อสมบูรณ์
คำแนะนำเครื่องมือธรรมดา
คุณสามารถใช้ประเภทแรกเพื่อให้ข้อมูลเกี่ยวกับปุ่มไอคอนที่ไม่ชัดเจน ตัวอย่างเช่น คุณสามารถใช้คำแนะนำเครื่องมือธรรมดาเพื่อแจ้งให้ผู้ใช้ทราบว่าปุ่มไอคอนหมายถึงอะไร

หากต้องการเพิ่มคำแนะนำเครื่องมือให้กับแอปพลิเคชันของคุณ คุณใช้ TooltipBox ประกอบได้ การเรียบเรียงนี้ต้องใช้ข้อโต้แย้งหลายประการ:
fun TooltipBox(
positionProvider: PopupPositionProvider,
tooltip: @Composable TooltipScope.() -> Unit,
state: TooltipState,
modifier: Modifier = Modifier,
focusable: Boolean = true,
enableUserInput: Boolean = true,
content: @Composable () -> Unit,
)
สิ่งเหล่านี้บางส่วนน่าจะคุ้นเคยกับคุณหากคุณเคยใช้ Composables มาก่อน ฉันจะเน้นรายการที่มีกรณีการใช้งานเฉพาะที่นี่:
-
positionProvider - ของ PopupPositionProvider และใช้ในการคำนวณตำแหน่งของคำแนะนำเครื่องมือ
-
เคล็ดลับเครื่องมือ - นี่คือที่ที่คุณสามารถออกแบบ UI ว่าคำแนะนำเครื่องมือจะมีลักษณะอย่างไร
-
state - เก็บสถานะที่เกี่ยวข้องกับอินสแตนซ์คำแนะนำเครื่องมือเฉพาะ โดยเปิดเผยวิธีการต่างๆ เช่น การแสดง/ปิดคำแนะนำเครื่องมือ และเมื่อสร้างอินสแตนซ์ของคำแนะนำเครื่องมือนั้น คุณสามารถประกาศได้ว่าคำแนะนำเครื่องมือควรคงอยู่หรือไม่ (หมายความว่าควรแสดงต่อไปบนหน้าจอจนกว่าผู้ใช้จะดำเนินการคลิกนอกคำแนะนำเครื่องมือ)
-
เนื้อหา - นี่คือ UI ที่คำแนะนำเครื่องมือจะแสดงด้านบน/ด้านล่าง
นี่คือตัวอย่างการสร้าง BasicTooltipBox โดยกรอกอาร์กิวเมนต์ที่เกี่ยวข้องทั้งหมด:
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun BasicTooltip() {
val tooltipPosition = TooltipDefaults.rememberPlainTooltipPositionProvider()
val tooltipState = rememberBasicTooltipState(isPersistent = false)
BasicTooltipBox(positionProvider = tooltipPosition,
tooltip = { Text("Hello World") } ,
state = tooltipState) {
IconButton(onClick = { }) {
Icon(imageVector = Icons.Filled.Favorite,
contentDescription = "Your icon's description")
}
}
}

Jetpack Compose มีคลาสในตัวที่เรียกว่า TooltipDefaults คุณสามารถใช้คลาสนี้เพื่อช่วยยกตัวอย่างอาร์กิวเมนต์ที่ประกอบเป็น TooltipBox ตัวอย่างเช่น คุณสามารถใช้ TooltipDefaults.rememberPlainTooltipPositionProvider เพื่อวางตำแหน่งคำแนะนำเครื่องมือให้สัมพันธ์กับองค์ประกอบจุดยึดอย่างถูกต้อง
คำแนะนำเครื่องมือที่หลากหลาย
เคล็ดลับเครื่องมือสื่อสมบูรณ์ใช้พื้นที่มากกว่าคำแนะนำเครื่องมือธรรมดา และสามารถใช้เพื่อให้บริบทเพิ่มเติมเกี่ยวกับการทำงานของปุ่มไอคอน เมื่อคำแนะนำเครื่องมือแสดงขึ้น คุณสามารถเพิ่มปุ่มและลิงก์ไปยังคำแนะนำดังกล่าวเพื่อให้คำอธิบายหรือคำจำกัดความเพิ่มเติมได้
มันถูกสร้างอินสแตนซ์ในลักษณะเดียวกันกับคำแนะนำเครื่องมือธรรมดาภายใน TooltipBox แต่คุณใช้ RichTooltip ที่เขียนได้
TooltipBox(positionProvider = tooltipPosition,
tooltip = {
RichTooltip(
title = { Text("RichTooltip") },
caretSize = caretSize,
action = {
TextButton(onClick = {
scope.launch {
tooltipState.dismiss()
tooltipState.onDispose()
}
}) {
Text("Dismiss")
}
}
) {
Text("This is where a description would go.")
}
},
state = tooltipState) {
IconButton(onClick = {
/* Icon button's click event */
}) {
Icon(imageVector = tooltipIcon,
contentDescription = "Your icon's description",
tint = iconColor)
}
}
บางสิ่งที่ควรสังเกตเกี่ยวกับคำแนะนำเครื่องมือแบบสมบูรณ์:
-
คำแนะนำเครื่องมือแบบสมบูรณ์มีการรองรับคาเร็ต
-
คุณสามารถเพิ่มการดำเนินการ (ซึ่งก็คือปุ่ม) ลงในคำแนะนำเครื่องมือเพื่อให้ผู้ใช้มีทางเลือกในการค้นหาข้อมูลเพิ่มเติม
-
คุณสามารถเพิ่มตรรกะเพื่อยกเลิกคำแนะนำเครื่องมือได้


กรณีขอบ
เมื่อคุณเลือกที่จะทำเครื่องหมายสถานะคำแนะนำเครื่องมือเป็นแบบถาวร หมายความว่าเมื่อผู้ใช้โต้ตอบกับ UI ที่แสดงคำแนะนำเครื่องมือของคุณ UI ดังกล่าวจะยังคงมองเห็นได้จนกว่าผู้ใช้จะกดที่ใดก็ได้บนหน้าจอ
หากคุณดูตัวอย่างคำแนะนำเครื่องมือแบบสมบูรณ์จากด้านบน คุณอาจสังเกตว่าเราได้เพิ่มปุ่มเพื่อปิดคำแนะนำเครื่องมือทันทีที่มีการคลิก
มีปัญหาเกิดขึ้นเมื่อผู้ใช้กดปุ่มนั้น เนื่องจากการดำเนินการยกเลิกจะดำเนินการบนคำแนะนำเครื่องมือ หากผู้ใช้ต้องการกดรายการ UI ที่เรียกใช้คำแนะนำเครื่องมือนี้ค้างไว้อีกครั้ง คำแนะนำเครื่องมือจะไม่แสดงอีก ซึ่งหมายความว่าสถานะของคำแนะนำเครื่องมือจะคงอยู่เมื่อถูกละทิ้ง แล้วเราจะดำเนินการและแก้ไขปัญหานี้อย่างไร

ในการ “รีเซ็ต” สถานะของคำแนะนำเครื่องมือ เราต้องเรียก onDispose วิธีการที่แสดงผ่านสถานะคำแนะนำเครื่องมือ เมื่อเราทำเช่นนั้น สถานะคำแนะนำเครื่องมือจะถูกรีเซ็ต และคำแนะนำเครื่องมือจะแสดงอีกครั้งเมื่อผู้ใช้กดรายการ UI ค้างไว้
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RichTooltip() {
val tooltipPosition = TooltipDefaults.rememberRichTooltipPositionProvider()
val tooltipState = rememberTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
TooltipBox(positionProvider = tooltipPosition,
tooltip = {
RichTooltip(
title = { Text("RichTooltip") },
caretSize = TooltipDefaults.caretSize,
action = {
TextButton(onClick = {
scope.launch {
tooltipState.dismiss()
tooltipState.onDispose() /// <---- HERE
}
}) {
Text("Dismiss")
}
}
) {
}
},
state = tooltipState) {
IconButton(onClick = { }) {
Icon(imageVector = Icons.Filled.Call, contentDescription = "Your icon's description")
}
}
}

อีกสถานการณ์หนึ่งที่สถานะคำแนะนำเครื่องมือไม่รีเซ็ตคือถ้าแทนที่จะเรียกตัวเองสำหรับวิธีการปิดตามการกระทำของผู้ใช้ ผู้ใช้คลิกนอกคำแนะนำเครื่องมือ ทำให้มันถูกไล่ออก ซึ่งจะเรียกวิธีการยกเลิกการทำงานเบื้องหลังและสถานะคำแนะนำเครื่องมือถูกตั้งค่าเป็นปิด การกดองค์ประกอบ UI ค้างไว้เพื่อดูคำแนะนำเครื่องมือของเราอีกครั้งจะไม่ส่งผลใดๆ

ตรรกะของเราที่เรียกใช้เมธอด onDispose ของคำแนะนำเครื่องมือไม่ได้รับการกระตุ้น ดังนั้นเราจะรีเซ็ตสถานะของคำแนะนำเครื่องมือได้อย่างไร
ขณะนี้ฉันไม่สามารถเข้าใจสิ่งนี้ได้ อาจเกี่ยวข้องกับ MutatorMutex ของคำแนะนำเครื่องมือ บางทีในการเปิดตัวที่กำลังจะมาถึง ก็อาจมี API สำหรับสิ่งนี้ ฉันสังเกตเห็นว่าหากมีคำแนะนำเครื่องมืออื่นๆ ปรากฏบนหน้าจอและถูกกด สิ่งนี้จะรีเซ็ตคำแนะนำเครื่องมือที่คลิกก่อนหน้านี้

หากคุณต้องการดูโค้ดที่แสดงที่นี่ คุณสามารถไปที่พื้นที่เก็บข้อมูล GitHub นี้
หากคุณต้องการดูคำแนะนำเครื่องมือในแอปพลิเคชัน คุณสามารถดูได้ที่นี่
ข้อมูลอ้างอิง
-
ภาพรวมคำแนะนำเครื่องมือ Material3
-
คำแนะนำเครื่องมือเริ่มต้น
-
เคล็ดลับเครื่องมือซอร์สโค้ด
เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น