ฟีเจอร์สไตลัสขั้นสูง

Android และ ChromeOS มี API มากมายที่จะช่วยคุณสร้างแอปที่นำเสนอ ผู้ใช้ได้รับประสบการณ์ที่ยอดเยี่ยมสำหรับสไตลัส การเปิดเผยชั้นเรียน MotionEvent รายการ ข้อมูลเกี่ยวกับการโต้ตอบของสไตลัสกับหน้าจอ รวมถึงแรงกดของสไตลัส การวางแนว การเอียง การวางเมาส์ และฝ่ามือ กราฟิกและการเคลื่อนไหวที่มีเวลาในการตอบสนองต่ำ ไลบรารีการคาดการณ์จะเพิ่มประสิทธิภาพการแสดงผลบนหน้าจอของสไตลัสเพื่อมอบ ที่เป็นธรรมชาติเหมือนใช้ปากกาและกระดาษ

MotionEvent

คลาส MotionEvent แสดงการโต้ตอบอินพุตของผู้ใช้ เช่น ตำแหน่ง และการเคลื่อนไหวของตัวชี้แบบสัมผัสบนหน้าจอ สำหรับอินพุตสไตลัส MotionEvent นอกจากนี้ยังจะแสดงข้อมูลความดัน การวางแนว การเอียง และการวางเมาส์เหนือ

ข้อมูลเหตุการณ์

วันที่

หากต้องการเข้าถึงข้อมูล MotionEvent ให้เพิ่มตัวแก้ไข pointerInput ลงในคอมโพเนนต์ดังนี้

@Composable
fun Greeting() {
    Text(
        text = "Hello, Android!", textAlign = TextAlign.Center, style = TextStyle(fontSize = 5.em),
        modifier = Modifier
            .pointerInput(Unit) {
                awaitEachGesture {
                    while (true) {
                        val event = awaitPointerEvent()
                        event.changes.forEach { println(it) }
                    }
                }
            },
    )
}

ออบเจ็กต์ MotionEvent ให้ข้อมูลที่เกี่ยวข้องกับด้านต่างๆ ต่อไปนี้ของ UI กิจกรรม:

  • การทำงาน: การโต้ตอบทางกายภาพกับอุปกรณ์ เช่น การแตะหน้าจอ การย้ายตัวชี้เหนือพื้นผิวหน้าจอ การเลื่อนตัวชี้เหนือหน้าจอ แพลตฟอร์ม
  • เคอร์เซอร์: ตัวระบุของวัตถุที่โต้ตอบกับหน้าจอ เช่น นิ้ว สไตลัส, เมาส์
  • แกน: ประเภทข้อมูล เช่น พิกัด x และ y, ความดัน, การเอียง, การวางแนว และลอยอยู่ (ระยะทาง)

การดำเนินการ

หากต้องการใช้การรองรับสไตลัส คุณต้องเข้าใจสิ่งที่ผู้ใช้ทำ ที่มีประสิทธิภาพสูง

MotionEvent มีค่าคงที่ ACTION ที่หลากหลายซึ่งเป็นตัวกำหนดการเคลื่อนไหว กิจกรรม การดำเนินการที่สำคัญที่สุดสำหรับสไตลัสมีดังนี้

การทำงาน คำอธิบาย
ACTION_DOWN
ACTION_POINTER_DOWN
เคอร์เซอร์สัมผัสหน้าจอแล้ว
การดำเนินการ เคอร์เซอร์กำลังเคลื่อนไหวบนหน้าจอ
ACTION_UP
ACTION_POINTER_UP
เคอร์เซอร์ไม่สัมผัสกับหน้าจออีกต่อไป
การดำเนินการยกเลิก ควรยกเลิกชุดการเคลื่อนไหวก่อนหน้าหรือปัจจุบันเมื่อใด

แอปของคุณจะทำงานต่างๆ ได้ เช่น การเริ่มเส้นโครงร่างใหม่เมื่อ ACTION_DOWN เกิดขึ้น โดยวาดเส้นโครงร่างด้วย ACTION_MOVE, และสิ้นสุดเส้นโครงร่างเมื่อ ทริกเกอร์ ACTION_UP แล้ว

ชุดของการกระทำ MotionEvent จาก ACTION_DOWN เป็น ACTION_UP สำหรับ เรียกว่าชุดการเคลื่อนไหว

เคอร์เซอร์

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

ช่วงดัชนีเคอร์เซอร์มีตั้งแต่ 0 จนถึงจำนวนเคอร์เซอร์ที่แสดงผลโดย MotionEvent#pointerCount() ลบ 1

คุณเข้าถึงค่าแกนของเคอร์เซอร์ได้ด้วยเมธอด getAxisValue(axis, pointerIndex) เมื่อละเว้นดัชนีตัวชี้ ระบบจะแสดงค่าสำหรับดัชนีแรก ตัวชี้, ตัวชี้ศูนย์ (0)

ออบเจ็กต์ MotionEvent มีข้อมูลเกี่ยวกับประเภทของตัวชี้ที่ใช้อยู่ คุณ รับประเภทตัวชี้ได้โดยทำซ้ำผ่านดัชนีตัวชี้และการเรียก เวลา getToolType(pointerIndex)

ดูข้อมูลเพิ่มเติมเกี่ยวกับเคอร์เซอร์ได้ที่จัดการมัลติทัช ท่าทางสัมผัส

อินพุตสไตลัส

คุณกรองหาอินพุตสไตลัสได้ด้วย TOOL_TYPE_STYLUS:

val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)

สไตลัสยังสามารถรายงานได้ว่ามีการใช้เป็นยางลบ TOOL_TYPE_ERASER:

val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)

ข้อมูลแกนสไตลัส

ACTION_DOWN และ ACTION_MOVE ให้ข้อมูลแกนเกี่ยวกับสไตลัส ซึ่งได้แก่ x และ พิกัด y ความดัน การวางแนว การเอียง และการลอยตัว

MotionEvent API จะเตรียมการเข้าถึงข้อมูลนี้ไว้ให้ getAxisValue(int), โดยที่พารามิเตอร์เป็นตัวระบุแกนต่อไปนี้

Axis ผลลัพธ์ getAxisValue()
AXIS_X พิกัด X ของเหตุการณ์การเคลื่อนไหว
AXIS_Y พิกัด Y ของเหตุการณ์การเคลื่อนไหว
AXIS_PRESSURE สำหรับหน้าจอสัมผัสหรือทัชแพด แรงกดที่ใช้นิ้ว สไตลัส หรือตัวชี้อื่นๆ สำหรับเมาส์หรือแทร็กบอล ให้ใช้ 1 หากกดปุ่มหลัก มิฉะนั้นจะเป็น 0
AXIS_ORIENTATION สำหรับหน้าจอสัมผัสหรือทัชแพด จะมีการวางแนวของนิ้วมือ สไตลัส หรือตัวชี้อื่นๆ ที่สัมพันธ์กับระนาบแนวตั้งของอุปกรณ์
AXIS_TILT มุมเอียงของสไตลัสเป็นเรเดียน
AXIS_DISTANCE ระยะห่างของสไตลัสจากหน้าจอ

ตัวอย่างเช่น MotionEvent.getAxisValue(AXIS_X) จะแสดงผลพิกัด x ของ ตัวชี้แรก

โปรดดูหัวข้อจัดการมัลติทัช ท่าทางสัมผัส

ตำแหน่ง

คุณสามารถเรียกพิกัด x และ y ของตัวชี้ได้ด้วยการเรียกต่อไปนี้:

สไตลัสกำลังวาดบนหน้าจอโดยแมปพิกัด x และ y ไว้
รูปที่ 1 พิกัด X และ y บนหน้าจอของตัวชี้สไตลัส

ความกดอากาศ

คุณสามารถดูความกดดันตัวชี้ได้ด้วย MotionEvent#getAxisValue(AXIS_PRESSURE) หรือสำหรับตัวชี้แรก MotionEvent#getPressure()

ค่าแรงดันสำหรับหน้าจอสัมผัสหรือทัชแพดคือค่าระหว่าง 0 (no กด) และ 1 แต่อาจแสดงผลค่าที่สูงกว่า ทั้งนี้ขึ้นอยู่กับหน้าจอ การเทียบมาตรฐาน

วันที่ เส้นสไตลัสแสดงถึงความดันต่ำไปสูงอย่างต่อเนื่อง โรคหลอดเลือดสมองแคบและจางทางด้านซ้ายแสดงถึงความกดอากาศต่ำ เส้นโครงร่างกว้างขึ้นและมืดจากซ้ายไปขวาจนกว่าจะกว้างที่สุดและมืดที่สุดฝั่งขวาสุด แสดงถึงความกดอากาศสูงสุด
รูปที่ 2 การแสดงด้วยแรงดัน - ความกดอากาศต่ำด้านบนซ้าย ความกดอากาศสูงด้านขวา

การวางแนว

การวางแนวจะระบุว่าสไตลัสกำลังชี้ไปในทิศทางใด

คุณดึงข้อมูลการวางแนวของเคอร์เซอร์ได้โดยใช้ getAxisValue(AXIS_ORIENTATION) หรือ getOrientation() (สำหรับตัวชี้ตัวแรก)

สำหรับสไตลัส การวางแนวจะส่งคืนค่าเป็นค่าเรเดียนระหว่าง 0 ถึงพาย (π) ตามเข็มนาฬิกา หรือ 0 ถึง -pi ทวนเข็มนาฬิกา

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

วันที่
รูปที่ 3 สไตลัสชี้ไปทางซ้ายประมาณ -.57 เรเดียน

ทิลท์

การเอียงจะวัดความเอียงของสไตลัสโดยสัมพันธ์กับหน้าจอ

การเอียงจะแสดงมุมบวกของสไตลัสเป็นเรเดียน โดยที่ 0 เท่ากับ ตั้งฉากกับหน้าจอ และ π/2 แบนบนพื้นผิว

สามารถดึงข้อมูลมุมเอียงได้โดยใช้ getAxisValue(AXIS_TILT) (ไม่มีทางลัดสำหรับ เคอร์เซอร์ตัวแรก)

การเอียงสามารถใช้เพื่อทำให้ได้เครื่องมือในชีวิตจริงที่ใกล้เคียงที่สุดเท่าที่จะทำได้ เช่น ที่เลียนแบบการลงสีด้วยดินสอเอียง

วันที่ สไตลัสเอียงจากพื้นผิวหน้าจอประมาณ 40 องศา
รูปที่ 4 สไตลัสเอียงที่ประมาณ 0.785 เรเดียน หรือ 45 องศาจากตั้งฉาก

วางเมาส์

ระยะห่างของสไตลัสจากหน้าจอสามารถวัดได้ด้วย getAxisValue(AXIS_DISTANCE) เมธอดแสดงค่าจาก 0.0 (ติดต่อกับ หน้าจอ) เป็นค่าที่สูงขึ้นเมื่อสไตลัสเลื่อนออกจากหน้าจอ โฮเวอร์ ระยะห่างระหว่างหน้าจอกับปลายนิ้ว (จุด) ของสไตลัสขึ้นอยู่กับ ผู้ผลิตทั้งหน้าจอและสไตลัส เนื่องจากการติดตั้งใช้งานอาจ ปรับเปลี่ยนได้ อย่าใช้ค่าที่แน่นอนสำหรับฟังก์ชันการทำงานที่สำคัญของแอป

การวางเมาส์เหนือสไตลัสสามารถใช้เพื่อดูตัวอย่างขนาดแปรงหรือบ่งชี้ว่า จะถูกเลือก

วันที่
รูปที่ 5 สไตลัสวางอยู่เหนือหน้าจอ แอปตอบสนองแม้ว่าสไตลัสจะไม่สัมผัสพื้นผิวหน้าจอ

หมายเหตุ: Compose มีตัวแก้ไขที่ส่งผลต่อสถานะแบบอินเทอร์แอกทีฟขององค์ประกอบ UI ดังนี้

  • hoverable: กำหนดค่าคอมโพเนนต์ให้วางเมาส์เหนือองค์ประกอบได้โดยใช้ตัวชี้ในเหตุการณ์การเข้าและออก
  • indication: วาดเอฟเฟกต์ภาพสำหรับคอมโพเนนต์นี้เมื่อมีการโต้ตอบ

การปฏิเสธฝ่ามือ การนำทาง และข้อมูลที่ไม่ต้องการ

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

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

ACTION_CANCEL และ FLAG_CANCELED

ACTION_CANCEL และ FLAG_CANCELED คือ ทั้งคู่ออกแบบมาเพื่อแจ้งให้คุณทราบว่า MotionEvent ชุดก่อนหน้าควร ยกเลิกตั้งแต่ ACTION_DOWN ที่แล้ว เพื่อให้คุณสามารถเลิกทำ เส้นสำหรับแอปพลิเคชันวาดภาพสำหรับตัวชี้ที่ระบุ

การดำเนินการยกเลิก

เพิ่มใน Android 1.0 (API ระดับ 1) แล้ว

ACTION_CANCEL บ่งชี้ว่าควรยกเลิกเหตุการณ์การเคลื่อนไหวชุดก่อนหน้า

ACTION_CANCEL จะทริกเกอร์เมื่อตรวจพบสิ่งต่อไปนี้

  • ท่าทางสัมผัสสำหรับการนำทาง
  • การปฏิเสธปาล์ม

เมื่อ ACTION_CANCEL ทริกเกอร์ คุณควรระบุตัวชี้ที่ใช้งานอยู่ด้วย getPointerId(getActionIndex()) จากนั้นลบเส้นโครงร่างที่สร้างด้วยตัวชี้นั้นออกจากประวัติการป้อนข้อมูล แล้วแสดงผลฉากอีกครั้ง

FLAG_CANCELED

เพิ่มใน Android 13 (API ระดับ 33) แล้ว

FLAG_CANCELED ชี้ให้เห็นว่าตัวชี้ที่เพิ่มขึ้นเป็นการแตะโดยไม่ได้ตั้งใจ ธงคือ ซึ่งมักจะเกิดขึ้นเมื่อผู้ใช้แตะหน้าจอโดยไม่ตั้งใจ เช่น การจับ หรือวางฝ่ามือลงบนหน้าจอ

คุณเข้าถึงค่า Flag ดังนี้

val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED

หากตั้งค่าไว้ คุณจะต้องเลิกทำ MotionEvent จากชุดสุดท้าย ACTION_DOWN จากจุดนี้

ซึ่งเหมือนกับ ACTION_CANCEL คุณจะพบเครื่องหมายดังกล่าวได้ด้วย getPointerId(actionIndex)

รูปที่ 6 เส้นสไตลัสและการสัมผัสฝ่ามือสร้างชุดอุปกรณ์ MotionEvent ชุด การแตะแบบใช้ฝ่ามือถูกยกเลิกและการแสดงผลอีกครั้ง

ท่าทางสัมผัสแบบเต็มหน้าจอ ขอบต่อขอบ และการนำทาง

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

รูปที่ 7 ท่าทางสัมผัสด้วยการปัดเพื่อย้ายแอปไปยังพื้นหลัง

หากต้องการป้องกันไม่ให้ท่าทางสัมผัสทำให้เกิดการแตะที่ไม่ต้องการในแอป คุณสามารถทำดังนี้ ข้อได้เปรียบของตัวอย่างและ ACTION_CANCEL

ดูการปฏิเสธฝ่ามือ การนำทาง และการป้อนข้อมูลที่ไม่พึงประสงค์ด้วย

ใช้เมนู setSystemBarsBehavior() วิธีการและ BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE จาก WindowInsetsController วิธีป้องกันไม่ให้ท่าทางสัมผัสการนำทางก่อให้เกิดกิจกรรมการแตะที่ไม่พึงประสงค์

// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

ดูข้อมูลเพิ่มเติมเกี่ยวกับการจัดการด้านการตั้งค่าและท่าทางสัมผัสได้ที่

เวลาในการตอบสนองต่ำ

เวลาในการตอบสนองคือเวลาที่ฮาร์ดแวร์ ระบบ และแอปพลิเคชันต้องการในการประมวลผล และแสดงผลข้อมูลจากผู้ใช้

เวลาในการตอบสนอง = การประมวลผลอินพุตฮาร์ดแวร์และระบบปฏิบัติการ + การประมวลผลแอป + การจัดวางองค์ประกอบของระบบ

  • การแสดงภาพฮาร์ดแวร์
เวลาในการตอบสนองทำให้เส้นโครงร่างที่แสดงผลช้ากว่าตำแหน่งสไตลัส ช่องว่างระหว่างเส้นโครงร่างที่แสดงและตำแหน่งสไตลัสแสดงถึงเวลาในการตอบสนอง
รูปที่ 8 เวลาในการตอบสนองทำให้เส้นโครงร่างที่แสดงผลช้ากว่าตำแหน่งสไตลัส

แหล่งที่มาของเวลาในการตอบสนอง

  • กำลังลงทะเบียนสไตลัสด้วยหน้าจอสัมผัส (ฮาร์ดแวร์): การเชื่อมต่อไร้สายเริ่มต้น เมื่อสไตลัสและระบบปฏิบัติการสื่อสารกันเพื่อลงทะเบียนและซิงค์
  • อัตราการสุ่มตัวอย่างการแตะ (ฮาร์ดแวร์): จำนวนครั้งต่อวินาทีบนหน้าจอสัมผัส ตรวจสอบว่าตัวชี้สัมผัสพื้นผิวหรือไม่ มีค่าตั้งแต่ 60 ถึง 1000Hz
  • การประมวลผลอินพุต (แอป): การใช้สี เอฟเฟกต์กราฟิก และการเปลี่ยนรูปแบบ ในข้อมูลจากผู้ใช้
  • การแสดงผลกราฟิก (ระบบปฏิบัติการ + ฮาร์ดแวร์): การเปลี่ยนบัฟเฟอร์ การประมวลผลด้วยฮาร์ดแวร์

กราฟิกที่มีเวลาในการตอบสนองต่ำ

ไลบรารีกราฟิกที่มีเวลาในการตอบสนองต่ำของ Jetpack ซึ่งช่วยลดเวลาในการประมวลผลระหว่างการป้อนข้อมูลของผู้ใช้และการแสดงผลบนหน้าจอ

ไลบรารีจะลดเวลาในการประมวลผลโดยหลีกเลี่ยงการแสดงผลแบบบัฟเฟอร์หลายตัว ใช้เทคนิคการแสดงผลแบบ Front-Buffers ซึ่งหมายความว่าเขียน หน้าจอ

การแสดงภาพบัฟเฟอร์ด้านหน้า

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

วันที่ แอปเขียนไปยังบัฟเฟอร์หน้าจอและอ่านจากบัฟเฟอร์หน้าจอ
รูปที่ 9 การแสดงภาพบัฟเฟอร์ด้านหน้า
แอปเขียนไปยังโหมดบัฟเฟอร์หลายรายการ ซึ่งสลับกับบัฟเฟอร์หน้าจอ แอปอ่านจากบัฟเฟอร์หน้าจอ
รูปที่ 10 การแสดงภาพแบบ Multi-buffer

ขณะที่การแสดงผลแบบบัฟเฟอร์หน้าเป็นเทคนิคที่ยอดเยี่ยมในการแสดงผลพื้นที่เล็กๆ ของกราฟ ไม่ได้ออกแบบมาให้ใช้เพื่อรีเฟรชทั้งหน้าจอ ด้วย การแสดงผลแบบฟรอนท์-บัฟเฟอร์ โดยแอปกำลังแสดงเนื้อหาลงในบัฟเฟอร์ที่ จอแสดงผลกำลังอ่านอยู่ จึงมีโอกาสแสดงผล สิ่งประดิษฐ์หรือการฉีกขาด (ดูด้านล่าง)

ไลบรารีที่มีเวลาในการตอบสนองต่ำพร้อมใช้งานตั้งแต่ Android 10 (API ระดับ 29) ขึ้นไป และในอุปกรณ์ ChromeOS ที่ใช้ Android 10 (API ระดับ 29) ขึ้นไป

การขึ้นต่อกัน

ไลบรารีที่มีเวลาในการตอบสนองต่ำมอบคอมโพเนนต์สำหรับการแสดงภาพบัฟเฟอร์ด้านหน้า การใช้งานของคุณ เพิ่มไลบรารีเป็นทรัพยากร Dependency ในโมดูลของแอป build.gradle ไฟล์:

dependencies {
    implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}

Callback ของ GLFrontBufferRenderer

ไลบรารีที่มีเวลาในการตอบสนองต่ำประกอบด้วย GLFrontBufferRenderer.Callback ของอินเทอร์เฟซ ซึ่งจะกำหนดวิธีการดังต่อไปนี้

ไลบรารีที่มีเวลาในการตอบสนองต่ำไม่มีความคิดเห็นเกี่ยวกับประเภทข้อมูลที่คุณใช้ GLFrontBufferRenderer

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

Callback

หากต้องการเปิดใช้การแสดงผล Callback ให้ใช้ GLFrontBufferedRenderer.Callback และ ลบล้าง onDrawFrontBufferedLayer() และ onDrawDoubleBufferedLayer() GLFrontBufferedRenderer ใช้ Callback เพื่อแสดงผลข้อมูลของคุณใน ทางที่ดีที่สุดเท่าที่จะเป็นไปได้

val callback = object: GLFrontBufferedRenderer.Callback<DATA_TYPE> {
   override fun onDrawFrontBufferedLayer(
       eglManager: EGLManager,
       bufferInfo: BufferInfo,
       transform: FloatArray,
       param: DATA_TYPE
   ) {
       // OpenGL for front buffer, short, affecting small area of the screen.
   }
   override fun onDrawMultiDoubleBufferedLayer(
       eglManager: EGLManager,
       bufferInfo: BufferInfo,
       transform: FloatArray,
       params: Collection<DATA_TYPE>
   ) {
       // OpenGL full scene rendering.
   }
}
ประกาศอินสแตนซ์ของ GLFrontBufferedRenderer

จัดเตรียม GLFrontBufferedRenderer ด้วยการระบุ SurfaceView และ Callback ที่คุณสร้างไว้ก่อนหน้านี้ GLFrontBufferedRenderer เพิ่มประสิทธิภาพการแสดงผล ข้างหน้าและบัฟเฟอร์คู่โดยใช้ Callback ของคุณ:

var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)
การแสดงภาพ

การแสดงผลบัฟเฟอร์ด้านหน้าจะเริ่มเมื่อคุณเรียก renderFrontBufferedLayer() ซึ่งจะทริกเกอร์ Callback onDrawFrontBufferedLayer()

การแสดงผลแบบบัฟเฟอร์คู่จะกลับมาทำงานอีกครั้งเมื่อคุณเรียกฟังก์ชัน commit() ซึ่งจะทริกเกอร์ Callback onDrawMultiDoubleBufferedLayer()

ในตัวอย่างต่อไปนี้ กระบวนการแสดงผลไปยังบัฟเฟอร์ด้านหน้า ( การแสดงผล) เมื่อผู้ใช้เริ่มวาดบนหน้าจอ (ACTION_DOWN) และเคลื่อนที่ ตัวชี้ไปรอบๆ (ACTION_MOVE) กระบวนการแสดงผลเป็นบัฟเฟอร์คู่ เมื่อตัวชี้ออกจากพื้นผิวของหน้าจอ (ACTION_UP)

คุณสามารถใช้ requestUnbufferedDispatch() ระบบอินพุตไม่ได้รวมกลุ่มเหตุการณ์การเคลื่อนไหว แต่ส่งแทน ทันทีที่พร้อมใช้งาน ให้ทำดังนี้

when (motionEvent.action) {
   MotionEvent.ACTION_DOWN -> {
       // Deliver input events as soon as they arrive.
       view.requestUnbufferedDispatch(motionEvent)
       // Pointer is in contact with the screen.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
   }
   MotionEvent.ACTION_MOVE -> {
       // Pointer is moving.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
   }
   MotionEvent.ACTION_UP -> {
       // Pointer is not in contact in the screen.
       glFrontBufferRenderer.commit()
   }
   MotionEvent.CANCEL -> {
       // Cancel front buffer; remove last motion set from the screen.
       glFrontBufferRenderer.cancel()
   }
}

สิ่งที่ควรและไม่ควรทำเกี่ยวกับการแสดงภาพ

✓ ทำ

พื้นที่เล็กๆ ของหน้าจอ การเขียนด้วยลายมือ การวาดภาพ การร่างภาพ

✗ อย่า

การอัปเดตแบบเต็มหน้าจอ การเลื่อน การซูม อาจทำให้น้ำตาไหล

น้ำตาไหล

การฉีกขาดเกิดขึ้นเมื่อหน้าจอรีเฟรชขณะบัฟเฟอร์หน้าจอ ที่ถูกแก้ไขในเวลาเดียวกัน หน้าจอบางส่วนแสดงข้อมูลใหม่ ส่วนอีกหน้าจอหนึ่ง แสดงข้อมูลเก่า

วันที่ ส่วนบนและล่างของรูปภาพ Android วางไม่ตรงแนวเนื่องจากมีการฉีกขาดเมื่อรีเฟรชหน้าจอ
รูปที่ 11 ฉีกขาดเมื่อหน้าจอรีเฟรชจากบนลงล่าง

การคาดการณ์การเคลื่อนไหว

การคาดการณ์การเคลื่อนไหว Jetpack การลดไลบรารี เวลาในการตอบสนองที่รับรู้ได้โดยการประมาณเส้นทาง ของโรคหลอดเลือดสมองของผู้ใช้ และระบุชั่วคราว ไปยังตัวแสดงผล

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

ออบเจ็กต์ MotionEvent ที่คาดการณ์ไว้เป็นเพียงค่าประมาณเท่านั้น เหตุการณ์ที่คาดการณ์อาจลดจำนวน เวลาในการตอบสนองที่รับรู้ แต่ต้องแทนที่ข้อมูลที่คาดการณ์ไว้ด้วย MotionEvent จริง ข้อมูลทันทีที่ได้รับ

ไลบรารีการคาดการณ์การเคลื่อนไหวมีให้บริการใน Android 4.4 (API ระดับ 19) และ และในอุปกรณ์ ChromeOS ที่ใช้ Android 9 (API ระดับ 28) ขึ้นไป

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

การขึ้นต่อกัน

ไลบรารีการคาดการณ์การเคลื่อนไหวจะนำเสนอการใช้การคาดการณ์ เพิ่มไลบรารีเป็นทรัพยากร Dependency ในไฟล์โมดูล build.gradle ของแอปแล้ว:

dependencies {
    implementation "androidx.input:input-motionprediction:1.0.0-beta01"
}

การใช้งาน

ไลบรารีการคาดการณ์การเคลื่อนไหวประกอบด้วย MotionEventPredictor ของอินเทอร์เฟซ ซึ่งจะกำหนดวิธีการดังต่อไปนี้

  • record(): จัดเก็บออบเจ็กต์ MotionEvent รายการเป็นบันทึกการดำเนินการของผู้ใช้
  • predict(): แสดงผล MotionEvent ที่คาดการณ์ไว้
ประกาศอินสแตนซ์ของ MotionEventPredictor
var motionEventPredictor = MotionEventPredictor.newInstance(view)
ป้อนข้อมูลให้กับตัวคาดการณ์
motionEventPredictor.record(motionEvent)
คาดการณ์

when (motionEvent.action) {
   MotionEvent.ACTION_MOVE -> {
       val predictedMotionEvent = motionEventPredictor?.predict()
       if(predictedMotionEvent != null) {
            // use predicted MotionEvent to inject a new artificial point
       }
   }
}

สิ่งที่ควรและไม่ควรทำสำหรับการคาดการณ์การเคลื่อนไหว

✓ ทำ

นำจุดการคาดการณ์ออกเมื่อมีการเพิ่มจุดที่คาดการณ์ใหม่

✗ อย่า

อย่าใช้จุดการคาดการณ์สำหรับการแสดงผลขั้นสุดท้าย

แอปสำหรับจดโน้ต

ChromeOS จะช่วยให้แอปของคุณประกาศการดำเนินการจดโน้ตบางอย่าง

หากต้องการลงทะเบียนแอปเป็นแอปสำหรับจดโน้ตบน ChromeOS โปรดดูการป้อนข้อมูล ความเข้ากันได้

หากต้องการลงทะเบียนแอปเป็นการจดโน้ตใน Android โปรดดูสร้างการจดโน้ต แอป

Android 14 (API ระดับ 34) เปิดตัว ACTION_CREATE_NOTE Intent ซึ่งทำให้แอปเริ่มกิจกรรมการจดบันทึกบนล็อก บนหน้าจอ

การจดจำหมึกดิจิทัลด้วย ML Kit

ด้วยหมึกดิจิทัล ML Kit การจดจำเสียง แอปของคุณสามารถจดจำข้อความที่เขียนด้วยลายมือบนพื้นผิวดิจิทัลหลายร้อยรายการ ภาษา และจำแนกประเภทภาพร่างได้ด้วย

ML Kit มอบ Ink.Stroke.Builder เพื่อสร้างออบเจ็กต์ Ink รายการที่โมเดลแมชชีนเลิร์นนิงจะประมวลผลได้ เพื่อแปลงการเขียนด้วยลายมือเป็นข้อความ

นอกเหนือจากการจดจำลายมือแล้ว โมเดลยังรับรู้การ ท่าทางสัมผัส เช่น ลบและแวดวง

ดูหมึกดิจิทัล การจดจำ เพื่อดูข้อมูลเพิ่มเติม

แหล่งข้อมูลเพิ่มเติม

คู่มือนักพัฒนาซอฟต์แวร์

Codelab