ผู้ใช้ชอบรูปภาพ วิดีโอ และเนื้อหาอื่นๆ ที่สื่อความหมาย แต่การแทรกและย้ายเนื้อหานี้ในแอปอาจไม่ใช่เรื่องง่ายเสมอไป Android 12 (ระดับ API 31) เปิดตัว API แบบรวมที่ช่วยให้แอปยอมรับเนื้อหาจากแหล่งที่มาใดก็ได้ ไม่ว่าจะเป็นคลิปบอร์ด แป้นพิมพ์ หรือการลาก เพื่อให้แอปได้รับข้อมูลอย่างละเอียดได้ง่ายขึ้น
คุณสามารถแนบอินเทอร์เฟซ เช่น OnReceiveContentListener กับคอมโพเนนต์ UI และรับ Callback เมื่อมีการแทรกเนื้อหาผ่านกลไกใดก็ได้ Callback จะกลายเป็นที่เดียวที่โค้ดของคุณจะจัดการการรับเนื้อหาทั้งหมด ตั้งแต่ข้อความธรรมดาและข้อความที่มีการจัดรูปแบบ ไปจนถึงมาร์กอัป รูปภาพ วิดีโอ ไฟล์เสียง และอื่นๆ
เพื่อให้เข้ากันได้แบบย้อนหลังกับ Android เวอร์ชันก่อนหน้า API นี้จึงพร้อมใช้งานใน AndroidX ด้วย โดยเริ่มตั้งแต่ Core 1.7 และ Appcompat 1.4 ซึ่งเราขอแนะนำให้คุณใช้เมื่อใช้ฟังก์ชันนี้
ภาพรวม
สำหรับ API อื่นๆ ที่มีอยู่ กลไก UI แต่ละรายการ เช่น เมนูแตะค้างไว้หรือการลาก จะมี API ที่เกี่ยวข้องของตัวเอง ซึ่งหมายความว่าคุณต้องผสานรวมกับ API แต่ละรายการแยกกัน โดยเพิ่มโค้ดที่คล้ายกันสำหรับกลไกแต่ละรายการที่แทรกเนื้อหา ดังนี้
OnReceiveContentListener API จะรวมเส้นทางโค้ดที่แตกต่างกันเหล่านี้โดยสร้าง API เดียวเพื่อนำไปใช้ คุณจึงมุ่งเน้นที่ตรรกะเฉพาะแอปและปล่อยให้แพลตฟอร์มจัดการส่วนที่เหลือได้
นอกจากนี้ แนวทางนี้ยังหมายความว่าเมื่อมีการเพิ่มวิธีใหม่ๆ ในการแทรกเนื้อหาลงในแพลตฟอร์ม คุณไม่จำเป็นต้องทำการเปลี่ยนแปลงโค้ดเพิ่มเติมเพื่อเปิดใช้การรองรับในแอป และหากแอปของคุณจำเป็นต้องใช้การปรับแต่งอย่างเต็มรูปแบบสำหรับ Use Case หนึ่งๆ คุณก็ยังคงใช้ API ที่มีอยู่ได้ ซึ่งจะยังคงทำงานในลักษณะเดิมต่อไป
การใช้งาน
API เป็นอินเทอร์เฟซ Listener ที่มีเมธอดเดียวคือ OnReceiveContentListener
หากต้องการรองรับแพลตฟอร์ม Android เวอร์ชันเก่า เราขอแนะนำให้ใช้อินเทอร์เฟซ OnReceiveContentListener ที่ตรงกันในไลบรารี AndroidX Core
หากต้องการใช้ API ให้ติดตั้งใช้งาน Listener โดยระบุประเภทเนื้อหาที่แอปของคุณจัดการได้ ดังนี้
Kotlin
object MyReceiver : OnReceiveContentListener { val MIME_TYPES = arrayOf("image/*", "video/*") // ... override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? { TODO("Not yet implemented") } }
Java
public class MyReceiver implements OnReceiveContentListener { public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; // ... }
หลังจากระบุประเภท MIME ของเนื้อหาทั้งหมดที่แอปรองรับแล้ว ให้ใช้ Listener ที่เหลือดังนี้
Kotlin
class MyReceiver : OnReceiveContentListener { override fun onReceiveContent(view: View, contentInfo: ContentInfoCompat): ContentInfoCompat { val split = contentInfo.partition { item: ClipData.Item -> item.uri != null } val uriContent = split.first val remaining = split.second if (uriContent != null) { // App-specific logic to handle the URI(s) in uriContent. } // Return anything that your app didn't handle. This preserves the // default platform behavior for text and anything else that you aren't // implementing custom handling for. return remaining } companion object { val MIME_TYPES = arrayOf("image/*", "video/*") } }
Java
public class MyReceiver implements OnReceiveContentListener { public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; @Override public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) { Pair<ContentInfoCompat, ContentInfoCompat> split = contentInfo.partition( item -> item.getUri() != null); ContentInfo uriContent = split.first; ContentInfo remaining = split.second; if (uriContent != null) { // App-specific logic to handle the URI(s) in uriContent. } // Return anything that your app didn't handle. This preserves the // default platform behavior for text and anything else that you aren't // implementing custom handling for. return remaining; } }
หากแอปของคุณรองรับการแชร์ด้วย Intent อยู่แล้ว คุณสามารถนำตรรกะเฉพาะแอปมาใช้ซ้ำเพื่อจัดการ URI เนื้อหาได้ แสดงผลข้อมูลที่เหลือเพื่อมอบสิทธิ์การจัดการข้อมูลนั้นให้กับแพลตฟอร์ม
หลังจากติดตั้งใช้งาน Listener แล้ว ให้ตั้งค่า Listener ในองค์ประกอบ UI ที่เหมาะสมในแอป ดังนี้
Kotlin
class MyActivity : Activity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... val myInput = findViewById(R.id.my_input) ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, MyReceiver()) } }
Java
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { // ... AppCompatEditText myInput = findViewById(R.id.my_input); ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver()); } }
สิทธิ์ URI
แพลตฟอร์มจะให้และยกเลิกสิทธิ์อ่านโดยอัตโนมัติสำหรับ URI เนื้อหาในเพย์โหลดที่ส่งไปยัง OnReceiveContentListener
โดยปกติแล้ว แอปจะประมวลผล URI เนื้อหาในบริการหรือกิจกรรม สำหรับการประมวลผลที่ใช้เวลานาน ให้ใช้ WorkManager เมื่อใช้ฟีเจอร์นี้ ให้ขยายสิทธิ์ไปยังบริการหรือกิจกรรมเป้าหมายโดยส่งเนื้อหาโดยใช้ Intent.setClipData และตั้งค่าแฟล็ก FLAG_GRANT_READ_URI_PERMISSION
หรือจะใช้เทรดเบื้องหลังภายในบริบทปัจจุบันเพื่อประมวลผลเนื้อหาก็ได้ ในกรณีนี้ คุณต้องเก็บข้อมูลอ้างอิงไปยังออบเจ็กต์ payload ที่ Listener ได้รับไว้เพื่อช่วยให้มั่นใจว่าแพลตฟอร์มจะไม่เพิกถอนสิทธิ์ก่อนเวลาอันควร
มุมมองที่กำหนดเอง
หากแอปใช้คลาสย่อย View ที่กำหนดเอง โปรดตรวจสอบว่าไม่มีการข้าม OnReceiveContentListener
หากคลาส View ลบล้างเมธอด onCreateInputConnection ให้ใช้ Jetpack API InputConnectionCompat.createWrapper เพื่อกำหนดค่า InputConnection
หากคลาส View ลบล้างเมธอด onTextContextMenuItem ให้มอบสิทธิ์แก่ super เมื่อรายการเมนูเป็น R.id.paste หรือ R.id.pasteAsPlainText
การเปรียบเทียบกับ API รูปภาพแป้นพิมพ์
คุณอาจคิดว่า OnReceiveContentListener API เป็นเวอร์ชันถัดไปของ keyboard image API ที่มีอยู่ API แบบรวมนี้รองรับฟังก์ชันการทำงานของ API รูปภาพแป้นพิมพ์ รวมถึงฟีเจอร์เพิ่มเติมบางอย่าง ความเข้ากันได้ของอุปกรณ์และฟีเจอร์จะแตกต่างกันไปตามการใช้ไลบรารี Jetpack หรือ API แบบเนทีฟจาก Android SDK
| การดำเนินการหรือฟีเจอร์ | รองรับโดย API รูปภาพแป้นพิมพ์ | รองรับโดย Unified API |
|---|---|---|
| แทรกจากแป้นพิมพ์ | ใช่ (ระดับ API 13 ขึ้นไป) | ใช่ (ระดับ API 13 ขึ้นไป) |
| แทรกโดยใช้การวางจากเมนูแตะค้างไว้ | ไม่ | ใช่ |
| แทรกโดยใช้การลากและวาง | ไม่ | ใช่ (ระดับ API 24 ขึ้นไป) |
| การดำเนินการหรือฟีเจอร์ | รองรับโดย API รูปภาพแป้นพิมพ์ | รองรับโดย Unified API |
|---|---|---|
| แทรกจากแป้นพิมพ์ | ใช่ (ระดับ API 25 ขึ้นไป) | ใช่ (Android 12 ขึ้นไป) |
| แทรกโดยใช้การวางจากเมนูแตะค้างไว้ | ไม่ | |
| แทรกโดยใช้การลากและวาง | ไม่ |