ผู้ใช้ชื่นชอบรูปภาพ วิดีโอ และเนื้อหาที่แสดงออกอื่นๆ แต่การแทรกและย้ายเนื้อหาเหล่านี้ในแอปไม่ใช่เรื่องง่ายเสมอไป เพื่อให้แอปรับเนื้อหาที่สมบูรณ์ได้ง่ายขึ้น Android 12 (ระดับ API 31) จึงขอแนะนำ API แบบรวมศูนย์ที่ช่วยให้แอปยอมรับเนื้อหาจากแหล่งที่มาใดก็ได้ ไม่ว่าจะเป็นคลิปบอร์ด แป้นพิมพ์ หรือการลาก
คุณสามารถแนบอินเทอร์เฟซ เช่น
OnReceiveContentListener,
กับคอมโพเนนต์ UI และรับการเรียกกลับเมื่อมีการแทรกเนื้อหาผ่านกลไกใดก็ตาม
การเรียกกลับจะกลายเป็นที่เดียวที่โค้ดของคุณใช้จัดการการรับเนื้อหาทั้งหมด ตั้งแต่ข้อความธรรมดาและข้อความที่มีสไตล์ ไปจนถึงมาร์กอัป รูปภาพ วิดีโอ ไฟล์เสียง และอื่นๆ
API นี้ยังพร้อมใช้งานใน AndroidX ตั้งแต่ Core 1.7 และ Appcompat 1.4 ซึ่งเราขอแนะนำให้คุณใช้เมื่อใช้งานฟังก์ชันนี้ เพื่อให้มีความเข้ากันได้แบบย้อนหลังกับ Android เวอร์ชันก่อนหน้า
ภาพรวม
API อื่นๆ ที่มีอยู่แต่ละกลไก UI เช่น เมนูแตะค้างไว้หรือการลาก จะมี API ที่เกี่ยวข้องของตัวเอง ซึ่งหมายความว่าคุณต้องผสานรวมกับ API แต่ละรายการแยกกัน โดยเพิ่มโค้ดที่คล้ายกันสำหรับกลไกแต่ละรายการที่แทรกเนื้อหา ดังนี้
API OnReceiveContentListener จะรวมเส้นทางโค้ดที่แตกต่างกันเหล่านี้เข้าด้วยกันโดยสร้าง API เดียวเพื่อใช้งาน คุณจึงมุ่งเน้นไปที่ตรรกะเฉพาะของแอปได้ และปล่อยให้แพลตฟอร์มจัดการส่วนที่เหลือ
แนวทางนี้ยังหมายความว่าเมื่อมีการเพิ่มวิธีใหม่ๆ ในการแทรกเนื้อหาลงในแพลตฟอร์ม คุณไม่จำเป็นต้องทำการเปลี่ยนแปลงโค้ดเพิ่มเติมเพื่อเปิดใช้การรองรับในแอป และหากแอปต้องใช้การปรับแต่งแบบเต็มสำหรับกรณีการใช้งานที่เฉพาะเจาะจง คุณยังคงใช้ API ที่มีอยู่ได้ ซึ่งจะยังคงทำงานในลักษณะเดิม
การใช้งาน
API เป็นอินเทอร์เฟซ Listener ที่มีเมธอดเดียวคือ
OnReceiveContentListener
เราขอแนะนำให้ใช้
matching
OnReceiveContentListener
อินเทอร์เฟซที่ตรงกันในไลบรารี AndroidX Core เพื่อรองรับแพลตฟอร์ม Android เวอร์ชันเก่า
หากต้องการใช้ 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
การเปรียบเทียบกับ Keyboard Image API
คุณสามารถคิดว่า OnReceiveContentListener API เป็น Keyboard Image API เวอร์ชันถัดไปของ
ที่มีอยู่ API แบบรวมศูนย์นี้รองรับฟังก์ชันการทำงานของ Keyboard Image API รวมถึงฟีเจอร์เพิ่มเติมบางอย่าง ความเข้ากันได้ของอุปกรณ์และฟีเจอร์จะแตกต่างกันไปตามว่าคุณใช้ไลบรารี Jetpack หรือ API ดั้งเดิมจาก Android SDK
| การกระทำหรือฟีเจอร์ | รองรับโดย Keyboard Image API | รองรับโดย API แบบรวมศูนย์ |
|---|---|---|
| แทรกจากแป้นพิมพ์ | ใช่ (ระดับ API 13 ขึ้นไป) | ใช่ (ระดับ API 13 ขึ้นไป) |
| แทรกโดยใช้การวางจากเมนูแตะค้างไว้ | ไม่ | ใช่ |
| แทรกโดยใช้การลากและวาง | ไม่ | ใช่ (ระดับ API 24 ขึ้นไป) |
| การกระทำหรือฟีเจอร์ | รองรับโดย Keyboard Image API | รองรับโดย API แบบรวมศูนย์ |
|---|---|---|
| แทรกจากแป้นพิมพ์ | ใช่ (ระดับ API 25 ขึ้นไป) | ใช่ (Android 12 ขึ้นไป) |
| แทรกโดยใช้การวางจากเมนูแตะค้างไว้ | ไม่ | |
| แทรกโดยใช้การลากและวาง | ไม่ |