ฮาวทู

ขอแนะนำ Cahier: ตัวอย่าง GitHub ใหม่ของ Android สำหรับประสิทธิภาพการทำงานและความคิดสร้างสรรค์บนหน้าจอขนาดใหญ่

ใช้เวลาอ่าน 11 นาที
Chris Assigbe
วิศวกรนักพัฒนาซอฟต์แวร์สัมพันธ์

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

แอป Google เช่น Google เอกสาร, Pixel สตูดิโอ, Google Photos, Chrome PDF, Youtube Effect Maker และฟีเจอร์เฉพาะใน Android เช่น วงเพื่อค้นหา ทั้งหมดใช้ API ล่าสุด 

เพื่อเป็นการเฉลิมฉลองเหตุการณ์สำคัญนี้ เรายินดีที่จะประกาศเปิดตัว Cahier ซึ่งเป็นตัวอย่างแอปจดบันทึกที่ครอบคลุมซึ่งได้รับการเพิ่มประสิทธิภาพสำหรับอุปกรณ์ Android ทุกขนาด โดยเฉพาะแท็บเล็ตและโทรศัพท์แบบพับได้

Cahier คืออะไร

Cahier ("สมุดบันทึก" ในภาษาฝรั่งเศส) เป็นแอปตัวอย่างที่ออกแบบมาเพื่อแสดงให้เห็นวิธีสร้างแอปพลิเคชันที่ช่วยให้ผู้ใช้บันทึกและจัดระเบียบความคิดของตนเองได้โดยการรวมข้อความ ภาพวาด และรูปภาพ 

ตัวอย่างนี้สามารถใช้เป็นข้อมูลอ้างอิงหลักในการเพิ่มประสิทธิภาพการทำงานและความคิดสร้างสรรค์ของผู้ใช้บนหน้าจอขนาดใหญ่ โดยจะแสดงแนวทางปฏิบัติแนะนำในการสร้างประสบการณ์ดังกล่าว ซึ่งจะช่วยเร่งความเข้าใจของนักพัฒนาแอปและการนำ API และเทคนิคที่มีประสิทธิภาพที่เกี่ยวข้องมาใช้ โพสต์นี้จะอธิบายฟีเจอร์หลักของ Cahier, API ที่สำคัญ และการตัดสินใจด้านสถาปัตยกรรมที่ทำให้ตัวอย่างนี้เป็นข้อมูลอ้างอิงที่ยอดเยี่ยมสำหรับแอปของคุณเอง

ฟีเจอร์หลักที่แสดงในตัวอย่างมีดังนี้

  • การสร้างโน้ตที่หลากหลาย: แสดงวิธีใช้ระบบการสร้างเนื้อหาที่ยืดหยุ่นซึ่งรองรับหลายรูปแบบภายในโน้ตเดียว รวมถึงข้อความ ภาพวาดอิสระ และไฟล์แนบรูปภาพ
  • เครื่องมือการเขียนที่สร้างสรรค์: มอบประสบการณ์การวาดภาพที่มีประสิทธิภาพสูงและเวลาในการตอบสนองต่ำโดยใช้ Ink API ตัวอย่างนี้แสดงให้เห็นถึงการผสานรวมแปรงต่างๆ เครื่องมือเลือกสี ฟังก์ชันเลิกทำ/ทำซ้ำ และเครื่องมือยางลบ
  • การผสานรวมเนื้อหาแบบลื่นไหลด้วยการลากและวาง: แสดงวิธีจัดการทั้งเนื้อหาที่เข้ามาและออกไปโดยใช้การลากและวาง ซึ่งรวมถึงการยอมรับรูปภาพที่วางจากแอปอื่นๆ และการอนุญาตให้ผู้ใช้ลากเนื้อหาออกจากแอปเพื่อแชร์ได้อย่างราบรื่น
  • การจัดระเบียบโน้ต: ทำเครื่องหมายโน้ตเป็นรายการโปรดเพื่อให้เข้าถึงได้อย่างรวดเร็ว กรองมุมมองเพื่อจัดระเบียบ
  • สถาปัตยกรรมแบบออฟไลน์ก่อน: สร้างขึ้นด้วยสถาปัตยกรรมแบบออฟไลน์ก่อนโดยใช้ Room เพื่อให้มั่นใจว่าระบบจะบันทึกข้อมูลทั้งหมดไว้ในเครื่องและแอปจะยังคงทำงานได้อย่างเต็มที่โดยไม่ต้องเชื่อมต่ออินเทอร์เน็ต
  • การรองรับหลายหน้าต่างและอินสแตนซ์หลายรายการที่มีประสิทธิภาพ: แสดงวิธีรองรับอินสแตนซ์หลายรายการ ซึ่งช่วยให้เปิดแอปในหลายหน้าต่างได้ ผู้ใช้จึงทำงานกับโน้ตต่างๆ ไปพร้อมๆ กันได้ ซึ่งจะช่วยเพิ่มประสิทธิภาพการทำงานและความคิดสร้างสรรค์บนหน้าจอขนาดใหญ่
  • UI ที่ปรับเปลี่ยนได้สำหรับทุกหน้าจอ: อินเทอร์เฟซผู้ใช้จะปรับให้เข้ากับขนาดและการวางแนวหน้าจอต่างๆ ได้อย่างราบรื่นโดยใช้ ListDetailPaneScaffold และ NavigationSuiteScaffold เพื่อมอบประสบการณ์การใช้งานที่ปรับให้เหมาะสมบนโทรศัพท์ แท็บเล็ต และอุปกรณ์พับได้
  • การผสานรวมระบบอย่างลึกซึ้ง: ให้คำแนะนำเกี่ยวกับวิธีทำให้แอปของคุณเป็นแอปจดบันทึกเริ่มต้นใน Android 14 ขึ้นไปโดยการตอบสนองต่อ Intent ของโน้ตทั่วทั้งระบบ ซึ่งช่วยให้บันทึกเนื้อหาได้อย่างรวดเร็วจากจุดแรกเข้าต่างๆ ของระบบ

สร้างมาเพื่อประสิทธิภาพการทำงานและความคิดสร้างสรรค์บนหน้าจอขนาดใหญ่

สำหรับการเปิดตัวครั้งแรกนี้ เราจะมุ่งเน้นการประกาศเกี่ยวกับฟีเจอร์หลักๆ 2-3 รายการที่ทำให้ Cahier เป็นแหล่งข้อมูลการเรียนรู้ที่สำคัญสำหรับทั้งกรณีการใช้งานด้านประสิทธิภาพการทำงานและความคิดสร้างสรรค์

รากฐานของการปรับตัว

Cahier สร้างขึ้นมาให้ปรับเปลี่ยนได้ตั้งแต่ต้น ตัวอย่างใช้ไลบรารี material3-adaptive โดยเฉพาะ ListDetailPaneScaffold และ NavigationSuiteScaffold เพื่อปรับเลย์เอาต์ของแอปให้เข้ากับขนาดและการวางแนวหน้าจอต่างๆ ได้อย่างราบรื่น ซึ่งเป็นองค์ประกอบสำคัญสำหรับแอป Android สมัยใหม่ และ Cahier เป็นตัวอย่างที่ชัดเจนเกี่ยวกับวิธีนำไปใช้อย่างมีประสิทธิภาพ

Cahier adaptive UI built with Material 3 Adaptive library..gif

UI แบบปรับอัตโนมัติของ Cahier สร้างขึ้นด้วยไลบรารีแบบปรับอัตโนมัติของ Material 3

การแสดง API และการผสานรวมที่สำคัญ

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

เจาะลึก API ที่สำคัญ

มาเจาะลึก API ที่สำคัญ 2 รายการที่ Cahier ผสานรวมเพื่อมอบประสบการณ์การจดบันทึกชั้นยอดกัน

สร้างประสบการณ์การเขียนที่สมจริงด้วย Ink API

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

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

  • โมดูลการเขียน (Composeviews): จัดการอินพุตการเขียนด้วยหมึกแบบเรียลไทม์เพื่อสร้างเส้นที่ราบรื่นโดยมีเวลาในการตอบสนองต่ำที่สุดเท่าที่อุปกรณ์จะทำได้
    • ใน DrawingSurface Cahier ใช้ Composable InProgressStrokes ที่เพิ่งเปิดตัวเพื่อจัดการอินพุตสไตลัสหรือการสัมผัสแบบเรียลไทม์ โมดูลนี้มีหน้าที่จับภาพเหตุการณ์ของเคอร์เซอร์และแสดงผลเส้นหมึกเปียกโดยมีเวลาในการตอบสนองต่ำที่สุดเท่าที่จะเป็นไปได้
  • โมดูลเส้น: แสดงถึงข้อมูลหมึกที่ป้อนและการแสดงภาพ  เมื่อผู้ใช้วาดเส้นเสร็จแล้ว การเรียกกลับ onStrokesFinished จะระบุออบเจ็กต์ Stroke ที่เสร็จสมบูรณ์/แห้งให้กับแอป จากนั้นระบบจะจัดการออบเจ็กต์ที่ไม่เปลี่ยนแปลงนี้ ซึ่งแสดงถึงเส้นหมึกที่วาดเสร็จแล้วใน DrawingCanvasViewModel
  • โมดูลการแสดงผล: แสดงลายเส้นหมึกอย่างมีประสิทธิภาพ ทำให้สามารถรวมเข้ากับ Jetpack Compose หรือ Android Views ได้
    • Cahier ใช้ CanvasStrokeRenderer ใน DrawingSurface สำหรับการวาดภาพที่ใช้งานอยู่ และใน DrawingDetailPanePreview สำหรับการแสดงตัวอย่างแบบคงที่ของโน้ตเพื่อแสดงทั้งเส้นที่มีอยู่และเส้นที่เพิ่งวาด โมดูลนี้จะวาดออบเจ็กต์ Stroke ลงใน Canvas อย่างมีประสิทธิภาพ
  • โมดูลแปรง (Composeviews): ระบุวิธีประกาศเพื่อกำหนดรูปแบบภาพของเส้น การอัปเดตล่าสุด (ตั้งแต่รุ่นอัลฟ่า 03) มีแปรงเส้นประใหม่ ซึ่งมีประโยชน์อย่างยิ่งสำหรับฟีเจอร์ต่างๆ เช่น การเลือกแบบ Lasso DrawingCanvasViewModel จะเก็บสถานะของ currentBrush กล่องเครื่องมือใน DrawingCanvas ช่วยให้ผู้ใช้เลือกตระกูลแปรงต่างๆ (เช่น StockBrushes.pressurePen() หรือ StockBrushes.highlighter()) และเปลี่ยนสีได้ ViewModel จะอัปเดตออบเจ็กต์ Brush ซึ่งคอมโพสเซเบิล InProgressStrokes จะใช้สำหรับเส้นขีดใหม่
  • โมดูลเรขาคณิต (Composeมุมมอง): รองรับการจัดการและการวิเคราะห์ลายเส้นสำหรับฟีเจอร์ต่างๆ เช่น การลบและการเลือก
    • เครื่องมือยางลบภายในกล่องเครื่องมือและฟังก์ชันการทำงานใน DrawingCanvasViewModel จะขึ้นอยู่กับโมดูลเรขาคณิต เมื่อยางลบทำงานอยู่ ระบบจะสร้าง MutableParallelogram รอบเส้นทางการสัมผัสของผู้ใช้ จากนั้นยางลบจะตรวจสอบจุดตัดระหว่างรูปร่างกับกรอบล้อมของเส้นที่มีอยู่เพื่อพิจารณาว่าจะลบเส้นใด ทำให้ยางลบใช้งานได้ง่ายและแม่นยำ
  • โมดูลพื้นที่เก็บข้อมูล: มีความสามารถในการซีเรียลไลซ์และดีซีเรียลไลซ์ข้อมูลหมึกอย่างมีประสิทธิภาพ ซึ่งช่วยประหยัดพื้นที่ดิสก์และขนาดเครือข่ายได้อย่างมาก หากต้องการบันทึกภาพวาด Cahier จะบันทึกออบเจ็กต์ Stroke ในฐานข้อมูล Room ใน Converters ตัวอย่างจะใช้ฟังก์ชัน encode ของโมดูลพื้นที่เก็บข้อมูลเพื่อจัดรูปแบบ StrokeInputBatch (ข้อมูลจุดดิบ) เป็น ByteArray ระบบจะบันทึกอาร์เรย์ไบต์พร้อมกับพร็อพเพอร์ตี้ของแปรงเป็นสตริง JSON ฟังก์ชัน decode ใช้เพื่อสร้างเส้นขีดใหม่เมื่อโหลดโน้ต
orion.png

นอกเหนือจากโมดูลหลักเหล่านี้ การอัปเดตล่าสุดได้ขยายความสามารถของ Ink API ดังนี้

  • API เวอร์ชันทดลองใหม่สำหรับออบเจ็กต์ BrushFamily ที่กำหนดเองช่วยให้นักพัฒนาแอปสร้างประเภทแปรงที่สร้างสรรค์และไม่ซ้ำใครได้ ซึ่งจะช่วยให้เครื่องมือต่างๆ เช่น แปรงดินสอและตัวชี้เลเซอร์ทำงานได้

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

เลเซอร์สีรุ้งที่สร้างขึ้นด้วยแปรงที่กำหนดเองของ Ink API.gif

เลเซอร์สีรุ้งที่สร้างขึ้นด้วยแปรงที่กำหนดเองของ Ink API

notes.png

แปรงเพลงที่สร้างด้วยแปรงที่กำหนดเองของ Ink API

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

Ink API มีข้อดีหลายประการที่ทำให้เป็นตัวเลือกที่เหมาะสำหรับแอปเพื่อการทำงานและความคิดสร้างสรรค์มากกว่าการติดตั้งใช้งานที่กำหนดเอง ดังนี้

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

ปัจจุบัน Ink API ถูกนำไปใช้ในแอปต่างๆ ของ Google แล้ว เช่น การมาร์กอัปในเอกสารและฟีเจอร์วงเพื่อค้นหา รวมถึงแอปพาร์ทเนอร์อย่าง Orion Notes และ PDF Scanner

"Ink API เป็นตัวเลือกแรกของเราสำหรับฟีเจอร์วงเพื่อค้นหา (CtS) การผสานรวม Ink API เป็นเรื่องง่ายด้วยการใช้เอกสารประกอบที่ครอบคลุม ทำให้เราสามารถสร้างต้นแบบที่ใช้งานได้ครั้งแรกภายในเวลาเพียง 1 สัปดาห์ การรองรับพื้นผิวแปรงและภาพเคลื่อนไหวที่กำหนดเองของ Ink ช่วยให้เราสามารถทำซ้ำการออกแบบเส้นได้อย่างรวดเร็ว" - Jordan Komoda วิศวกรซอฟต์แวร์ - Google

การเป็นแอปการจดบันทึกเริ่มต้นที่มีบทบาทของโน้ต

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

การใช้งานใน Cahier

การใช้บทบาทของโน้ตมีขั้นตอนสำคัญ 2-3 ขั้นตอน ซึ่งทั้งหมดแสดงอยู่ในตัวอย่าง

  1. การประกาศในไฟล์ Manifest: ก่อนอื่น แอปต้องประกาศความสามารถในการจัดการ Intent สำหรับการจดบันทึก ใน AndroidManifest.xml Cahier มี <intent-filter> สำหรับการดำเนินการ android.intent.action.CREATE_NOTE ซึ่งจะเป็นสัญญาณให้ระบบทราบว่าแอปมีโอกาสที่จะเป็นแอปสำหรับจดบันทึก
  2. การตรวจสอบสถานะบทบาทSettingsViewModel ใช้ RoleManager ของ Android เพื่อกำหนดสถานะปัจจุบัน SettingsViewModel จะตรวจสอบว่าบทบาทของโน้ตพร้อมใช้งานในอุปกรณ์หรือไม่ (isRoleAvailable) และ Cahier มีบทบาทดังกล่าวในปัจจุบันหรือไม่ (isRoleHeld) สถานะนี้จะแสดงใน UI โดยใช้โฟลว์ Kotlin
  3. การขอรับบทบาท: ในไฟล์ Settings.kt ระบบจะแสดงปุ่มต่อผู้ใช้หากมีบทบาทที่พร้อมใช้งานแต่ผู้ใช้ยังไม่มีบทบาทดังกล่าว เมื่อคลิกปุ่ม ฟังก์ชัน requestNotesRole ใน ViewModel จะทำงาน ฟังก์ชันนี้จะสร้าง Intent เพื่อเปิดหน้าจอการตั้งค่าแอปเริ่มต้นที่ผู้ใช้เลือก Cahier ได้ กระบวนการนี้ได้รับการจัดการโดยใช้ API rememberLauncherForActivityResult ซึ่งจะจัดการการเปิด Intent และรับผลลัพธ์
  4. การอัปเดต UI: หลังจากที่ผู้ใช้กลับจากหน้าจอการตั้งค่าแล้ว การเรียกกลับ ActivityResultLauncher จะทริกเกอร์ฟังก์ชันใน ViewModel เพื่ออัปเดตสถานะบทบาท เพื่อให้มั่นใจว่า UI จะแสดงอย่างถูกต้องว่าตอนนี้แอปเป็นค่าเริ่มต้นหรือไม่

ดูวิธีผสานรวมบทบาทของโน้ตในแอปของคุณได้ในคู่มือการสร้างแอปจดบันทึก

helloworld.png

Cahier เปิดขึ้นในหน้าต่างลอยเป็นแอปจดบันทึกเริ่มต้นในแท็บเล็ต Lenovo

ก้าวสำคัญ: Lenovo เปิดใช้บทบาทการจดบันทึก

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

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

อินสแตนซ์หลายรายการ หลายหน้าต่าง และการแสดงหน้าต่างเดสก์ท็อป

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

  • การทำงานแบบหลายหน้าต่าง: ความสามารถพื้นฐานในการทำงานควบคู่กับแอปอื่นในโหมดแยกหน้าจอหรือโหมดอิสระ ซึ่งจำเป็นสำหรับงานต่างๆ เช่น การอ้างอิงหน้าเว็บขณะจดบันทึกใน Cahier
  • การใช้อินสแตนซ์หลายรายการ: นี่คือจุดที่การทำงานหลายอย่างพร้อมกันอย่างแท้จริงจะโดดเด่น Cahier ช่วยให้ผู้ใช้เปิดหน้าต่างอิสระหลายหน้าต่างของแอปพร้อมกันได้ ลองนึกภาพการเปรียบเทียบโน้ต 2 รายการแบบเคียงข้างกัน หรือการอ้างอิงโน้ตข้อความในหน้าต่างหนึ่งขณะวาดรูปในอีกหน้าต่างหนึ่ง Cahier แสดงวิธีจัดการอินสแตนซ์ที่แยกกันเหล่านี้ ซึ่งแต่ละอินสแตนซ์มีสถานะของตัวเอง ทำให้แอปของคุณกลายเป็นเครื่องมือที่มีประสิทธิภาพและหลากหลาย
  • การแสดงหน้าต่างเดสก์ท็อป: เมื่อเชื่อมต่อกับจอแสดงผลภายนอก โหมดเดสก์ท็อปของ Android จะเปลี่ยนแท็บเล็ตหรืออุปกรณ์แบบพับได้ให้กลายเป็นเวิร์กสเตชัน เนื่องจาก Cahier สร้างขึ้นด้วย UI แบบปรับอัตโนมัติและรองรับอินสแตนซ์หลายรายการ แอปจึงทำงานได้อย่างยอดเยี่ยมในสภาพแวดล้อมนี้ ผู้ใช้สามารถเปิด ปรับขนาด และจัดตำแหน่งหน้าต่าง Cahier หลายหน้าต่างได้เหมือนบนเดสก์ท็อปแบบเดิม ซึ่งจะช่วยให้เวิร์กโฟลว์ที่ซับซ้อนซึ่งก่อนหน้านี้ไม่สามารถทำได้บนอุปกรณ์เคลื่อนที่สามารถทำได้
cahier-desktop-windowing.webp

Cahier ทำงานในโหมดหน้าต่างเดสก์ท็อปบน Pixel Tablet

เราได้นำฟีเจอร์เหล่านี้ไปใช้ใน Cahier ดังนี้

หากต้องการเปิดใช้หลายอินสแตนซ์ เราต้องส่งสัญญาณไปยังระบบก่อนว่าแอปนี้รองรับการเปิดใช้หลายครั้งโดยการเพิ่มพร็อพเพอร์ตี้ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI ลงในการประกาศของ MainActivity ใน AndroidManifest ดังนี้

<activity

    android:name="com.example.cahier.MainActivity"

    android:exported="true"

    android:label="@string/app_name"

    android:theme="@style/Theme.MyApplication"

    android:showWhenLocked="true"

    android:turnScreenOn="true"

    android:resizeableActivity="true"

    android:launchMode="singleInstancePerTask">


    <property

        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"

        android:value="true"/>

    ...

</activity>

จากนั้นเราได้ใช้ตรรกะในการเปิดตัวอินสแตนซ์ใหม่ของแอป ใน CahierHomeScreen.kt เมื่อผู้ใช้เลือกที่จะเปิดโน้ตในหน้าต่างใหม่ เราจะสร้าง Intent ใหม่ที่มี Flag เฉพาะซึ่งจะสั่งให้ระบบจัดการการเปิดใช้งานใหม่ การใช้ร่วมกันของ FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_MULTIPLE_TASK และ FLAG_ACTIVITY_LAUNCH_ADJACENT จะช่วยให้มั่นใจได้ว่าโน้ตจะเปิดในหน้าต่างใหม่แยกต่างหากข้างหน้าต่างที่มีอยู่

fun openNewWindow(activity: Activity?, note: Note) {

    val intent = Intent(activity, MainActivity::class.java)

    intent.putExtra(AppArgs.NOTE_TYPE_KEY, note.type)

    intent.putExtra(AppArgs.NOTE_ID_KEY, note.id)

    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or

        Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT


    activity?.startActivity(intent)

}

เราต้องส่งสัญญาณไปยังระบบว่าแอปของคุณรองรับการปรับขนาดได้โดยการตั้งค่าองค์ประกอบ <activity> หรือ <application> ของไฟล์ Manifest เพื่อรองรับโหมดหลายหน้าต่าง

<activity

    android:name="com.example.cahier.MainActivity"

    android:resizeableActivity="true"

    ...>

</activity>

UI สร้างขึ้นด้วยไลบรารีแบบปรับได้ของ Material 3 จึงปรับให้เข้ากับสถานการณ์แบบหลายหน้าต่างได้อย่างราบรื่น เช่น โหมดแยกหน้าจอของ Android 

เราได้เพิ่มการรองรับการลากและวางเพื่อปรับปรุงประสบการณ์ของผู้ใช้ ดูวิธีที่เราใช้ฟีเจอร์นี้ใน Cahier ด้านล่าง

ลากและวาง

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

  • นำเข้าได้ง่ายๆ: ผู้ใช้สามารถลากรูปภาพจากแอปพลิเคชันอื่นๆ เช่น เว็บเบราว์เซอร์ แกลเลอรีรูปภาพ หรือเครื่องมือจัดการไฟล์ แล้ววางลงใน Canvas ของโน้ตได้โดยตรง โดย Cahier ใช้ตัวปรับแต่ง dragAndDropTarget เพื่อกำหนดโซนวาง ตรวจสอบเนื้อหาที่เข้ากันได้ (เช่น image/*) และประมวลผล URI ที่เข้ามา
  • การแชร์ที่ง่ายดาย: คุณแชร์เนื้อหาใน Cahier ได้ง่ายๆ เหมือนกับแชร์เนื้อหาจากแอปอื่นๆ ผู้ใช้สามารถกดรูปภาพในโน้ตข้อความค้างไว้ หรือกดทั้ง Canvas ของโน้ตภาพวาดและภาพคอมโพสิตค้างไว้ แล้วลากไปยังแอปพลิเคชันอื่น

การเจาะลึกทางเทคนิค: การลากจาก Canvas สำหรับวาด

การใช้ท่าทางสัมผัสการลากบน Canvas สำหรับวาดภาพเป็นความท้าทายที่ไม่เหมือนใคร ใน DrawingSurface คอมโพสเซเบิลที่จัดการอินพุตการวาดแบบเรียลไทม์ (InProgressStrokes ของ Ink API) และ Box ที่ตรวจหาท่าทางสัมผัสแบบกดค้างเพื่อเริ่มการลากคือคอมโพสเซเบิลที่อยู่ระดับเดียวกัน

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

เราจึงสร้างตัวแก้ไข pointerInputWithSiblingFallthrough ที่กำหนดเอง และใส่ Box โดยใช้ตัวแก้ไขนั้นก่อน InProgressStrokes ในโค้ดที่ประกอบได้ ยูทิลิตีนี้เป็น Wrapper แบบบางรอบระบบ pointerInput มาตรฐาน แต่มีการเปลี่ยนแปลงที่สำคัญอย่างหนึ่งคือการลบล้างฟังก์ชัน sharePointerInputWithSiblings() เพื่อให้แสดงผล true ซึ่งจะบอกเฟรมเวิร์ก Compose ให้ส่งต่อเหตุการณ์ของเคอร์เซอร์ไปยัง Composable ที่เป็นองค์ประกอบร่วม แม้ว่าจะมีการใช้เหตุการณ์แล้วก็ตาม

internal fun Modifier.pointerInputWithSiblingFallthrough(

    pointerInputEventHandler: PointerInputEventHandler

) = this then PointerInputSiblingFallthroughElement(pointerInputEventHandler)


private class PointerInputSiblingFallthroughModifierNode(

    pointerInputEventHandler: PointerInputEventHandler

) : PointerInputModifierNode, DelegatingNode() {


    var pointerInputEventHandler: PointerInputEventHandler

        get() = delegateNode.pointerInputEventHandler

        set(value) {

            delegateNode.pointerInputEventHandler = value

        }


    val delegateNode = delegate(

        SuspendingPointerInputModifierNode(pointerInputEventHandler)

    )


    override fun onPointerEvent(

        pointerEvent: PointerEvent,

        pass: PointerEventPass,

        bounds: IntSize

    ) {

        delegateNode.onPointerEvent(pointerEvent, pass, bounds)

    }


    override fun onCancelPointerInput() {

        delegateNode.onCancelPointerInput()

    }


    override fun sharePointerInputWithSiblings() = true

}


private data class PointerInputSiblingFallthroughElement(

    val pointerInputEventHandler: PointerInputEventHandler

) : ModifierNodeElement<PointerInputSiblingFallthroughModifierNode>() {


    override fun create() = PointerInputSiblingFallthroughModifierNode(pointerInputEventHandler)


    override fun update(node: PointerInputSiblingFallthroughModifierNode) {

        node.pointerInputEventHandler = pointerInputEventHandler

    }


    override fun InspectorInfo.inspectableProperties() {

        name = "pointerInputWithSiblingFallthrough"

        properties["pointerInputEventHandler"] = pointerInputEventHandler

    }

}

DrawingSurface ใช้ฟีเจอร์นี้ดังนี้

Box(

    modifier = Modifier

        .fillMaxSize()

        // Our custom modifier enables this gesture to coexist with the drawing input.

        .pointerInputWithSiblingFallthrough {

            detectDragGesturesAfterLongPress(

                onDragStart = { onStartDrag() },

                onDrag = { _, _ -> /* consume drag events */ },

                onDragEnd = { /* No action needed */ }

            )

        }

) 

// The Ink API's composable for live drawing sits here as a sibling.

InProgressStrokes(...)

เมื่อเปิดใช้แล้ว ระบบจะตรวจจับทั้งเส้นที่วาดและท่าทางสัมผัสลากด้วยการกดค้างได้อย่างถูกต้องพร้อมกัน เมื่อเริ่มการลากแล้ว เราจะสร้าง content:// URI ที่แชร์ได้ด้วย FileProvider และส่ง URI ไปยังเฟรมเวิร์กลากและวางของระบบโดยใช้ view.startDragAndDrop() โซลูชันนี้ช่วยให้มั่นใจได้ถึงประสบการณ์ของผู้ใช้ที่แข็งแกร่งและใช้งานง่าย ซึ่งแสดงให้เห็นวิธีแก้ไขปัญหาความขัดแย้งของท่าทางสัมผัสที่ซับซ้อนใน UI แบบเลเยอร์

สร้างขึ้นด้วยสถาปัตยกรรมสมัยใหม่

นอกจาก API ที่เฉพาะเจาะจงแล้ว Cahier ยังแสดงรูปแบบสถาปัตยกรรมที่สำคัญสำหรับการสร้างแอปพลิเคชันคุณภาพสูงที่ปรับเปลี่ยนได้

เลเยอร์การนำเสนอ: Jetpack Compose และความสามารถในการปรับตัว

เลเยอร์การนำเสนอสร้างขึ้นด้วย Jetpack Compose ทั้งหมด ดังที่กล่าวไว้ Cahier ใช้ไลบรารี material3-adaptive เพื่อให้ UI ปรับเปลี่ยนได้ การจัดการสถานะเป็นไปตามรูปแบบการไหลของข้อมูลแบบทิศทางเดียว (UDF) อย่างเคร่งครัด โดยใช้อินสแตนซ์ ViewModel เป็นคอนเทนเนอร์ข้อมูลที่เก็บข้อมูลโน้ตและสถานะ UI

เลเยอร์ข้อมูล: ที่เก็บและ Room

สำหรับเลเยอร์ข้อมูล Cahier ใช้อินเทอร์เฟซ NoteRepository เพื่อแยกการดำเนินการข้อมูลทั้งหมด การเลือกการออกแบบนี้ช่วยให้แอปสลับระหว่างแหล่งข้อมูลในเครื่อง (Room) กับแบ็กเอนด์ระยะไกลในอนาคตได้อย่างราบรื่น โฟลว์ข้อมูลสำหรับการดำเนินการ เช่น การแก้ไขโน้ต นั้นตรงไปตรงมา ดังนี้

  1. UI ของ Jetpack Compose จะทริกเกอร์เมธอดใน ViewModel
  2. ViewModel จะดึงข้อมูลโน้ตจาก NoteRepository จัดการตรรกะ และส่งโน้ตที่อัปเดตแล้วกลับไปยังที่เก็บ
  3. NoteRepository จะบันทึกการอัปเดตไปยังฐานข้อมูล Room

รองรับการป้อนข้อมูลอย่างครอบคลุม

แอปต้องรองรับวิธีการป้อนข้อมูลที่หลากหลายได้อย่างราบรื่นจึงจะถือเป็นแอปเพิ่มประสิทธิภาพที่แท้จริง Cahier สร้างขึ้นเพื่อให้เป็นไปตามหลักเกณฑ์การป้อนข้อมูลในหน้าจอขนาดใหญ่ และรองรับสิ่งต่อไปนี้

  • สไตลัส: การผสานรวมกับ Ink API, การปฏิเสธฝ่ามือ, การลงทะเบียนสำหรับบทบาทการจดบันทึก, การป้อนข้อมูลด้วยสไตลัสในช่องข้อความ และโหมดสมจริง
  • แป้นพิมพ์: รองรับแป้นพิมพ์ลัดและการผสมแป้นพิมพ์ที่ใช้กันโดยทั่วไปส่วนใหญ่ (เช่น Ctrl+คลิก, Meta+คลิก) และระบุโฟกัสของแป้นพิมพ์อย่างชัดเจน
  • เมาส์และแทร็กแพด: รองรับสถานะคลิกขวาและวางเมาส์เหนือ

การรองรับการโต้ตอบขั้นสูงของแป้นพิมพ์ เมาส์ และแทร็กแพดเป็นจุดสนใจหลักในการปรับปรุงเพิ่มเติม 

เริ่มต้นใช้งานวันนี้เลย

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

พร้อมเจาะลึกแล้วหรือยัง

  • สำรวจโค้ด: ไปที่ที่เก็บ GitHub เพื่อสำรวจฐานของโค้ดของ Cahier และดูหลักการออกแบบที่ใช้งานจริง
  • สร้างของคุณเอง: ใช้ Cahier เป็นรากฐานสำหรับการจดบันทึก การมาร์กอัปเอกสาร หรือแอปพลิเคชันสร้างสรรค์ของคุณเอง
  • ร่วมให้ข้อมูล: เรายินดีรับข้อมูลจากคุณ ช่วยเราปรับปรุง Cahier ให้เป็นแหล่งข้อมูลที่ดียิ่งขึ้นสำหรับชุมชนนักพัฒนาแอป Android

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

เขียนโดย

อ่านต่อ