Android มีเฟรมเวิร์กที่ใช้คลิปบอร์ดอันทรงพลังสำหรับการคัดลอกและวาง โดยรองรับประเภทข้อมูลทั้งแบบง่ายและซับซ้อน ซึ่งรวมถึงสตริงข้อความ โครงสร้างข้อมูลที่ซับซ้อน ข้อมูลสตรีมข้อความและไบนารี และเนื้อหาแอปพลิเคชัน ข้อมูลข้อความธรรมดาจะจัดเก็บไว้ในคลิปบอร์ดโดยตรง ส่วนข้อมูลที่ซับซ้อนจะจัดเก็บไว้เป็นข้อมูลอ้างอิงที่แอปพลิเคชันที่วางจะแก้ไขด้วย Content Provider การคัดลอกและวางจะทำงานได้ทั้งภายในแอปพลิเคชันและระหว่างแอปพลิเคชันที่ใช้เฟรมเวิร์ก
เนื่องจากส่วนหนึ่งของเฟรมเวิร์กใช้ Content Provider เอกสารนี้จึงถือว่าคุณมีความคุ้นเคยกับ Android Content Provider API ซึ่งอธิบายไว้ในผู้ให้บริการเนื้อหา
ผู้ใช้คาดหวังว่าจะได้รับความคิดเห็นเมื่อคัดลอกเนื้อหาไปยังคลิปบอร์ด ดังนั้นนอกจากเฟรมเวิร์กที่ขับเคลื่อนการคัดลอกและวางแล้ว Android ยังแสดง UI เริ่มต้นแก่ผู้ใช้เมื่อคัดลอกใน Android 13 (ระดับ API 33) ขึ้นไป ฟีเจอร์นี้อาจทำให้เกิดความเสี่ยงที่จะมีการแจ้งเตือนซ้ำ ดูข้อมูลเพิ่มเติมเกี่ยวกับกรณีที่เกิดขึ้นไม่บ่อยนี้ได้ในส่วนหลีกเลี่ยงการแจ้งเตือนซ้ำ
แสดงความคิดเห็นแก่ผู้ใช้ด้วยตนเองเมื่อคัดลอกใน Android 12L (ระดับ API 32) และต่ำกว่า ดูคำแนะนำสำหรับเรื่องนี้ในเอกสารนี้
เฟรมเวิร์กคลิปบอร์ด
เมื่อใช้เฟรมเวิร์กคลิปบอร์ด ให้ใส่ข้อมูลลงในออบเจ็กต์คลิป แล้วใส่ออบเจ็กต์คลิปในคลิปบอร์ดทั้งระบบ ออบเจ็กต์คลิปสามารถใช้รูปแบบใดรูปแบบหนึ่งจาก 3 รูปแบบต่อไปนี้
- ข้อความ
- สตริงข้อความ ใส่สตริงลงในออบเจ็กต์คลิปโดยตรง แล้วใส่ลงในคลิปบอร์ด หากต้องการวางสตริง ให้รับออบเจ็กต์คลิปจากคลิปบอร์ดและคัดลอกสตริงลงในพื้นที่เก็บข้อมูลของแอปพลิเคชัน
- URI
-
ออบเจ็กต์
Uriที่แสดงถึง URI รูปแบบใดก็ได้ ซึ่งส่วนใหญ่ใช้สำหรับการคัดลอกข้อมูลที่ซับซ้อนจาก Content Provider หากต้องการคัดลอกข้อมูล ให้ใส่ออบเจ็กต์Uriลงในออบเจ็กต์คลิป แล้วใส่ออบเจ็กต์คลิปลงในคลิปบอร์ด หากต้องการวางข้อมูล ให้รับออบเจ็กต์คลิป รับออบเจ็กต์Uriแปลงเป็นแหล่งข้อมูล เช่น Content Provider และคัดลอกข้อมูลจากแหล่งที่มาไปยังพื้นที่เก็บข้อมูลของแอปพลิเคชัน - ความตั้งใจ
-
Intentซึ่งรองรับการคัดลอกทางลัดของแอปพลิเคชัน หากต้องการคัดลอกข้อมูล ให้สร้างIntentแล้วใส่ไว้ในออบเจ็กต์คลิป จากนั้นใส่ออบเจ็กต์คลิปไว้ในคลิปบอร์ด หากต้องการวางข้อมูล ให้รับออบเจ็กต์คลิป แล้วคัดลอกออบเจ็กต์Intentไปยังพื้นที่หน่วยความจำของแอปพลิเคชัน
คลิปบอร์ดจะเก็บออบเจ็กต์คลิปได้ครั้งละ 1 รายการเท่านั้น เมื่อแอปพลิเคชันวางออบเจ็กต์คลิปบนคลิปบอร์ด ออบเจ็กต์คลิปก่อนหน้าจะหายไป
หากต้องการอนุญาตให้ผู้ใช้วางข้อมูลลงในแอปพลิเคชัน คุณไม่จำเป็นต้องจัดการข้อมูลทุกประเภท คุณสามารถตรวจสอบข้อมูลในคลิปบอร์ดก่อนที่จะให้ตัวเลือกแก่ผู้ใช้ในการวางข้อมูล นอกจากจะมีรูปแบบข้อมูลบางอย่างแล้ว ออบเจ็กต์คลิปยังมีข้อมูลเมตาที่บอกประเภท MIME ที่พร้อมใช้งานด้วย ข้อมูลเมตานี้ช่วยให้คุณตัดสินใจได้ว่าแอปพลิเคชันของคุณสามารถทำสิ่งที่เป็นประโยชน์กับข้อมูลในคลิปบอร์ดได้หรือไม่ ตัวอย่างเช่น หากคุณมีแอปพลิเคชันที่จัดการข้อความเป็นหลัก คุณอาจต้องการละเว้นออบเจ็กต์คลิปที่มี URI หรือ Intent
นอกจากนี้ คุณอาจต้องการอนุญาตให้ผู้ใช้วางข้อความได้ไม่ว่าข้อมูลในคลิปบอร์ดจะอยู่ในรูปแบบใดก็ตาม โดยบังคับให้ข้อมูลคลิปบอร์ดเป็นการแสดงข้อความ แล้ววางข้อความนี้ ซึ่งอธิบายไว้ในส่วนบังคับให้คลิปบอร์ดเป็นข้อความ
คลาสคลิปบอร์ด
ส่วนนี้จะอธิบายคลาสที่เฟรมเวิร์กคลิปบอร์ดใช้
ClipboardManager
คลิปบอร์ดของระบบ Android จะแสดงด้วยคลาส ClipboardManager ส่วนกลาง
อย่าสร้างอินสแตนซ์คลาสนี้โดยตรง แต่ให้รับการอ้างอิงโดยเรียกใช้ getSystemService(CLIPBOARD_SERVICE) แทน
ClipData, ClipData.Item และ ClipDescription
หากต้องการเพิ่มข้อมูลลงในคลิปบอร์ด ให้สร้างออบเจ็กต์ ClipData ที่มีคำอธิบายข้อมูลและข้อมูลนั้นๆ คลิปบอร์ดจะเก็บ ClipData ไว้ได้ครั้งละ 1 รายการ ClipData ประกอบด้วยออบเจ็กต์ ClipDescription และออบเจ็กต์ ClipData.Item อย่างน้อย 1 รายการ
ออบเจ็กต์ ClipDescription มีข้อมูลเมตาเกี่ยวกับคลิป โดยเฉพาะอย่างยิ่ง จะมีอาร์เรย์ของประเภท MIME ที่ใช้ได้สำหรับข้อมูลของคลิป นอกจากนี้ ใน Android 12 (API ระดับ 31) ขึ้นไป ข้อมูลเมตาจะมีข้อมูลว่าออบเจ็กต์มีข้อความที่มีสไตล์หรือไม่ และข้อมูลเกี่ยวกับประเภทข้อความในออบเจ็กต์
เมื่อคุณวางคลิปไว้ในคลิปบอร์ด ข้อมูลนี้จะพร้อมใช้งานสำหรับแอปพลิเคชันที่วาง ซึ่งจะตรวจสอบได้ว่าสามารถจัดการข้อมูลคลิปได้หรือไม่
ออบเจ็กต์ ClipData.Item มีข้อความ, URI หรือข้อมูล Intent ดังนี้
- ข้อความ
-
CharSequence - URI
-
ก
Uriโดยปกติแล้วจะมี URI ของ Content Provider แม้ว่าจะอนุญาตให้ใช้ URI ใดก็ได้ก็ตาม แอปพลิเคชันที่ให้ข้อมูลจะใส่ URI ไว้ในคลิปบอร์ด แอปพลิเคชันที่ต้องการวางข้อมูลจะรับ URI จากคลิปบอร์ดและใช้เพื่อเข้าถึง Content Provider หรือแหล่งข้อมูลอื่นๆ และดึงข้อมูล - ความตั้งใจ
-
Intentข้อมูลประเภทนี้ช่วยให้คุณคัดลอกทางลัดของแอปพลิเคชันไปยังคลิปบอร์ดได้ จากนั้นผู้ใช้จะวางแป้นพิมพ์ลัดลงในแอปพลิเคชันเพื่อใช้ในภายหลังได้
คุณเพิ่มวัตถุ ClipData.Item ได้มากกว่า 1 รายการในคลิป ซึ่งจะช่วยให้ผู้ใช้คัดลอกและวางรายการที่เลือกหลายรายการเป็นคลิปเดียวได้ ตัวอย่างเช่น หากคุณมีวิดเจ็ตรายการที่ให้ผู้ใช้เลือกรายการได้มากกว่า 1 รายการพร้อมกัน คุณสามารถคัดลอกรายการทั้งหมดไปยังคลิปบอร์ดพร้อมกันได้ โดยสร้าง ClipData.Item แยกต่างหากสำหรับรายการแต่ละรายการ แล้วเพิ่มออบเจ็กต์ ClipData.Item ลงในออบเจ็กต์ ClipData
เมธอดอำนวยความสะดวกของ ClipData
คลาส ClipData มีเมธอดแบบคงที่ที่อำนวยความสะดวกในการสร้างออบเจ็กต์ ClipData ด้วยออบเจ็กต์ ClipData.Item รายการเดียวและออบเจ็กต์ ClipDescription แบบง่าย ดังนี้
-
newPlainText(label, text) - แสดงผลออบเจ็กต์
ClipDataซึ่งออบเจ็กต์ClipData.Itemรายการเดียวมีสตริงข้อความ ป้ายกำกับของออบเจ็กต์ClipDescriptionตั้งค่าเป็นlabelประเภท MIME รายการเดียวในClipDescriptionคือMIMETYPE_TEXT_PLAINใช้
newPlainText()เพื่อสร้างคลิปจากสตริงข้อความ -
newUri(resolver, label, URI) - แสดงผลออบเจ็กต์
ClipDataซึ่งมีออบเจ็กต์ClipData.Itemรายการเดียวที่มี URI ป้ายกำกับของออบเจ็กต์ClipDescriptionตั้งค่าเป็นlabelหาก URI เป็น URI เนื้อหา กล่าวคือ หากUri.getScheme()แสดงผลcontent:เมธอดจะใช้ออบเจ็กต์ContentResolverที่ระบุในresolverเพื่อดึงข้อมูลประเภท MIME ที่พร้อมใช้งานจาก Content Provider จากนั้นจะจัดเก็บไว้ในClipDescriptionสำหรับ URI ที่ไม่ใช่ URIcontent:เมธอดจะตั้งค่าประเภท MIME เป็นMIMETYPE_TEXT_URILISTใช้
newUri()เพื่อสร้างคลิปจาก URI โดยเฉพาะ URI ของcontent: -
newIntent(label, intent) - แสดงผลออบเจ็กต์
ClipDataซึ่งออบเจ็กต์ClipData.ItemรายการเดียวมีIntentป้ายกำกับของออบเจ็กต์ClipDescriptionตั้งค่าเป็นlabelประเภท MIME ตั้งค่าเป็นMIMETYPE_TEXT_INTENTใช้
newIntent()เพื่อสร้างคลิปจากออบเจ็กต์Intent
บังคับข้อมูลคลิปบอร์ดให้เป็นข้อความ
แม้ว่าแอปพลิเคชันของคุณจะจัดการเฉพาะข้อความ แต่คุณสามารถคัดลอกข้อมูลที่ไม่ใช่ข้อความจากคลิปบอร์ดได้โดยแปลงด้วยเมธอด ClipData.Item.coerceToText()
เมธอดนี้จะแปลงข้อมูลใน ClipData.Item เป็นข้อความและแสดงผลเป็น CharSequence ค่าที่ ClipData.Item.coerceToText() แสดงผลจะอิงตามรูปแบบของข้อมูลใน ClipData.Item ดังนี้
- ข้อความ
-
หาก
ClipData.Itemเป็นข้อความ กล่าวคือ หากgetText()ไม่ใช่ค่า Null ฟังก์ชัน coerceToText() จะแสดงผลข้อความ - URI
-
หาก
ClipData.Itemเป็น URI ซึ่งก็คือหากgetUri()ไม่ใช่ค่า NullcoerceToText()จะพยายามใช้เป็น URI เนื้อหา- หาก URI เป็น URI เนื้อหาและผู้ให้บริการสามารถแสดงผลสตรีมข้อความได้
coerceToText()จะแสดงผลสตรีมข้อความ - หาก URI เป็น URI เนื้อหา แต่ผู้ให้บริการไม่มีสตรีมข้อความ
coerceToText()จะแสดงผลการนำเสนอ URI การนำเสนอจะเหมือนกับที่แสดงโดยUri.toString() - หาก URI ไม่ใช่ URI เนื้อหา
coerceToText()จะแสดงผลการนำเสนอของ URI การนำเสนอจะเหมือนกับที่Uri.toString()แสดงผล
- หาก URI เป็น URI เนื้อหาและผู้ให้บริการสามารถแสดงผลสตรีมข้อความได้
- ความตั้งใจ
- หาก
ClipData.Itemเป็นIntentซึ่งก็คือหากgetIntent()ไม่ใช่ค่า NullcoerceToText()จะแปลงเป็น Intent URI และแสดงผล การนำเสนอจะเหมือนกับที่Intent.toUri(URI_INTENT_SCHEME)แสดงผล
เฟรมเวิร์กของคลิปบอร์ดสรุปไว้ในรูปที่ 2 หากต้องการคัดลอกข้อมูล แอปพลิเคชันจะวางออบเจ็กต์ ClipData ไว้ในคลิปบอร์ดส่วนกลาง ClipboardManager ClipData มีออบเจ็กต์ ClipData.Item อย่างน้อย 1 รายการและออบเจ็กต์ ClipDescription 1 รายการ หากต้องการวางข้อมูล แอปพลิเคชันจะรับ ClipData,
รับประเภท MIME จาก ClipDescription และรับข้อมูลจาก
ClipData.Item หรือจาก Content Provider ที่อ้างอิงโดย
ClipData.Item
คัดลอกไปยังคลิปบอร์ด
หากต้องการคัดลอกข้อมูลไปยังคลิปบอร์ด ให้รับแฮนเดิลไปยังออบเจ็กต์ ClipboardManager ส่วนกลาง สร้างออบเจ็กต์ ClipData แล้วเพิ่ม ClipDescription และออบเจ็กต์ ClipData.Item อย่างน้อย 1 รายการลงในออบเจ็กต์ดังกล่าว จากนั้นเพิ่มออบเจ็กต์ ClipData ที่เสร็จแล้วลงในออบเจ็กต์ ClipboardManager ซึ่งจะอธิบายเพิ่มเติมในขั้นตอนต่อไปนี้
- หากคุณคัดลอกข้อมูลโดยใช้ URI เนื้อหา ให้ตั้งค่า Content Provider
- รับคลิปบอร์ดของระบบ:
Kotlin
when(menuItem.itemId) { ... R.id.menu_copy -> { // if the user selects copy // Gets a handle to the clipboard service. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager } }
Java
... // If the user selects copy. case R.id.menu_copy: // Gets a handle to the clipboard service. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
-
คัดลอกข้อมูลไปยังออบเจ็กต์
ClipDataใหม่โดยทำดังนี้-
สำหรับข้อความ
Kotlin
// Creates a new text clip to put on the clipboard. val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
Java
// Creates a new text clip to put on the clipboard. ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
-
สำหรับ URI
ข้อมูลโค้ดนี้สร้าง URI โดยการเข้ารหัสรหัสระเบียนลงใน URI เนื้อหาสำหรับผู้ให้บริการ เทคนิคนี้จะอธิบายไว้อย่างละเอียดในส่วนการเข้ารหัสตัวระบุใน URI
Kotlin
// Creates a Uri using a base Uri and a record ID based on the contact's last // name. Declares the base URI string. const val CONTACTS = "content://com.example.contacts" // Declares a path string for URIs, used to copy data. const val COPY_PATH = "/copy" // Declares the Uri to paste to the clipboard. val copyUri: Uri = Uri.parse("$CONTACTS$COPY_PATH/$lastName") ... // Creates a new URI clip object. The system uses the anonymous // getContentResolver() object to get MIME types from provider. The clip object's // label is "URI", and its data is the Uri previously created. val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
Java
// Creates a Uri using a base Uri and a record ID based on the contact's last // name. Declares the base URI string. private static final String CONTACTS = "content://com.example.contacts"; // Declares a path string for URIs, used to copy data. private static final String COPY_PATH = "/copy"; // Declares the Uri to paste to the clipboard. Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName); ... // Creates a new URI clip object. The system uses the anonymous // getContentResolver() object to get MIME types from provider. The clip object's // label is "URI", and its data is the Uri previously created. ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
-
สำหรับ Intent
ข้อมูลโค้ดนี้จะสร้าง
Intentสำหรับแอปพลิเคชัน แล้วใส่ไว้ในออบเจ็กต์คลิปKotlin
// Creates the Intent. val appIntent = Intent(this, com.example.demo.myapplication::class.java) ... // Creates a clip object with the Intent in it. Its label is "Intent" // and its data is the Intent object created previously. val clip: ClipData = ClipData.newIntent("Intent", appIntent)
Java
// Creates the Intent. Intent appIntent = new Intent(this, com.example.demo.myapplication.class); ... // Creates a clip object with the Intent in it. Its label is "Intent" // and its data is the Intent object created previously. ClipData clip = ClipData.newIntent("Intent", appIntent);
-
สำหรับข้อความ
-
วางออบเจ็กต์คลิปใหม่ในคลิปบอร์ด:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
แสดงความคิดเห็นเมื่อคัดลอกไปยังคลิปบอร์ด
ผู้ใช้คาดหวังว่าจะได้รับการตอบกลับด้วยภาพเมื่อแอปคัดลอกเนื้อหาไปยังคลิปบอร์ด ซึ่งระบบจะดำเนินการโดยอัตโนมัติสำหรับผู้ใช้ใน Android 13 ขึ้นไป แต่ต้องดำเนินการด้วยตนเองในเวอร์ชันก่อนหน้า
ตั้งแต่ Android 13 เป็นต้นไป ระบบจะแสดงการยืนยันภาพมาตรฐานเมื่อมีการเพิ่มเนื้อหาลงในคลิปบอร์ด การยืนยันใหม่จะทำสิ่งต่อไปนี้
- ยืนยันว่าคัดลอกเนื้อหาสำเร็จแล้ว
- แสดงตัวอย่างเนื้อหาที่คัดลอก
ใน Android 12L (API ระดับ 32) และต่ำกว่า ผู้ใช้อาจไม่แน่ใจว่าคัดลอกเนื้อหาสำเร็จหรือไม่ หรือคัดลอกอะไรไป ฟีเจอร์นี้จะกำหนดมาตรฐานการแจ้งเตือนต่างๆ ที่แอปแสดงหลังจากคัดลอก และช่วยให้ผู้ใช้ควบคุมคลิปบอร์ดได้มากขึ้น
หลีกเลี่ยงการแจ้งเตือนซ้ำ
ใน Android 12L (ระดับ API 32) และต่ำกว่า เราขอแนะนำให้แจ้งเตือนผู้ใช้เมื่อคัดลอกสำเร็จโดยแสดงความคิดเห็นในแอปแบบภาพ โดยใช้วิดเจ็ต เช่น Toast หรือ Snackbar หลังจากคัดลอก
เพื่อหลีกเลี่ยงการแสดงข้อมูลซ้ำ เราขอแนะนำอย่างยิ่งให้นำข้อความโทสต์หรือสแน็กบาร์ที่แสดงหลังจากคัดลอกในแอปออกสำหรับ Android 13 ขึ้นไป
ตัวอย่างวิธีใช้มีดังนี้
fun textCopyThenPost(textCopied:String) { val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager // When setting the clipboard text. clipboardManager.setPrimaryClip(ClipData.newPlainText ("", textCopied)) // Only show a toast for Android 12 and lower. if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) Toast.makeText(context, “Copied”, Toast.LENGTH_SHORT).show() }
เพิ่มเนื้อหาที่ละเอียดอ่อนลงในคลิปบอร์ด
หากแอปของคุณอนุญาตให้ผู้ใช้คัดลอกเนื้อหาที่ละเอียดอ่อนไปยังคลิปบอร์ด เช่น รหัสผ่านหรือข้อมูลบัตรเครดิต คุณต้องเพิ่ม Flag ลงใน ClipDescription ใน ClipData ก่อนเรียกใช้ ClipboardManager.setPrimaryClip() การเพิ่ม Flag นี้จะป้องกันไม่ให้เนื้อหาที่ละเอียดอ่อนปรากฏในการยืนยันภาพของเนื้อหาที่คัดลอกใน Android 13 ขึ้นไป
หากต้องการแจ้งว่าเนื้อหามีความละเอียดอ่อน ให้เพิ่มบูลีนเพิ่มเติมใน ClipDescription แอปทั้งหมดต้องดำเนินการนี้ ไม่ว่าจะกำหนดเป้าหมายเป็น API ระดับใดก็ตาม
// If your app is compiled with the API level 33 SDK or higher. clipData.apply { description.extras = PersistableBundle().apply { putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true) } } // If your app is compiled with a lower SDK. clipData.apply { description.extras = PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) } }
วางจากคลิปบอร์ด
ตามที่อธิบายไว้ก่อนหน้านี้ ให้วางข้อมูลจากคลิปบอร์ดโดยรับออบเจ็กต์คลิปบอร์ดส่วนกลาง รับออบเจ็กต์คลิป ดูข้อมูล และหากเป็นไปได้ ให้คัดลอกข้อมูลจากออบเจ็กต์คลิปไปยังพื้นที่เก็บข้อมูลของคุณเอง ส่วนนี้จะอธิบายรายละเอียดเกี่ยวกับวิธีวางข้อมูลคลิปบอร์ดทั้ง 3 รูปแบบ
วางข้อความธรรมดา
หากต้องการวางข้อความธรรมดา ให้รับคลิปบอร์ดส่วนกลางและยืนยันว่าสามารถแสดงผลข้อความธรรมดาได้ จากนั้นรับออบเจ็กต์คลิปและคัดลอกข้อความไปยังพื้นที่เก็บข้อมูลของคุณเองโดยใช้ getText() ตามที่อธิบายไว้ในขั้นตอนต่อไปนี้
- รับออบเจ็กต์
ClipboardManagerส่วนกลางโดยใช้getSystemService(CLIPBOARD_SERVICE)นอกจากนี้ ให้ประกาศตัวแปรร่วมเพื่อเก็บข้อความที่วางไว้ด้วยKotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- พิจารณาว่าคุณต้องเปิดหรือปิดใช้ตัวเลือก "วาง" ในกิจกรรมปัจจุบันหรือไม่ ตรวจสอบว่าคลิปบอร์ดมีคลิปและคุณสามารถจัดการประเภทข้อมูลที่คลิปแสดงได้ ดังนี้
Kotlin
// Gets the ID of the "paste" menu item. val pasteItem: MenuItem = menu.findItem(R.id.menu_paste) // If the clipboard doesn't contain data, disable the paste menu item. // If it does contain data, decide whether you can handle the data. pasteItem.isEnabled = when { !clipboard.hasPrimaryClip() -> { false } !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> { // Disables the paste menu item, since the clipboard has data but it // isn't plain text. false } else -> { // Enables the paste menu item, since the clipboard contains plain text. true } }
Java
// Gets the ID of the "paste" menu item. MenuItem pasteItem = menu.findItem(R.id.menu_paste); // If the clipboard doesn't contain data, disable the paste menu item. // If it does contain data, decide whether you can handle the data. if (!(clipboard.hasPrimaryClip())) { pasteItem.setEnabled(false); } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) { // Disables the paste menu item, since the clipboard has data but // it isn't plain text. pasteItem.setEnabled(false); } else { // Enables the paste menu item, since the clipboard contains plain text. pasteItem.setEnabled(true); }
- คัดลอกข้อมูลจากคลิปบอร์ด จุดนี้ในโค้ดจะเข้าถึงได้ก็ต่อเมื่อเปิดใช้รายการเมนู "วาง" เท่านั้น คุณจึงถือว่าคลิปบอร์ดมีข้อความธรรมดาได้ คุณยังไม่ทราบว่ามีสตริงข้อความหรือ URI ที่ชี้ไปยังข้อความธรรมดาหรือไม่
ข้อมูลโค้ดต่อไปนี้จะทดสอบเรื่องนี้ แต่จะแสดงเฉพาะโค้ดสําหรับจัดการข้อความธรรมดาเท่านั้น
Kotlin
when (menuItem.itemId) { ... R.id.menu_paste -> { // Responds to the user selecting "paste". // Examines the item on the clipboard. If getText() doesn't return null, // the clip item contains the text. Assumes that this application can only // handle one item at a time. val item = clipboard.primaryClip.getItemAt(0) // Gets the clipboard as text. pasteData = item.text return if (pasteData != null) { // If the string contains data, then the paste operation is done. true } else { // The clipboard doesn't contain text. If it contains a URI, // attempts to get data from it. val pasteUri: Uri? = item.uri if (pasteUri != null) { // If the URI contains something, try to get text from it. // Calls a routine to resolve the URI and get data from it. // This routine isn't presented here. pasteData = resolveUri(pasteUri) true } else { // Something is wrong. The MIME type was plain text, but the // clipboard doesn't contain text or a Uri. Report an error. Log.e(TAG,"Clipboard contains an invalid data type") false } } } }
Java
// Responds to the user selecting "paste". case R.id.menu_paste: // Examines the item on the clipboard. If getText() does not return null, // the clip item contains the text. Assumes that this application can only // handle one item at a time. ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); // Gets the clipboard as text. pasteData = item.getText(); // If the string contains data, then the paste operation is done. if (pasteData != null) { return true; // The clipboard doesn't contain text. If it contains a URI, attempts to get // data from it. } else { Uri pasteUri = item.getUri(); // If the URI contains something, try to get text from it. if (pasteUri != null) { // Calls a routine to resolve the URI and get data from it. // This routine isn't presented here. pasteData = resolveUri(Uri); return true; } else { // Something is wrong. The MIME type is plain text, but the // clipboard doesn't contain text or a Uri. Report an error. Log.e(TAG, "Clipboard contains an invalid data type"); return false; } }
วางข้อมูลจาก URI เนื้อหา
หากออบเจ็กต์ ClipData.Item มี URI เนื้อหาและคุณพิจารณาแล้วว่าสามารถจัดการประเภท MIME ประเภทใดประเภทหนึ่งได้ ให้สร้าง ContentResolver และเรียกใช้เมธอด Content Provider ที่เหมาะสมเพื่อดึงข้อมูล
ขั้นตอนต่อไปนี้อธิบายวิธีรับข้อมูลจาก Content Provider โดยอิงตาม URI เนื้อหาในคลิปบอร์ด โดยจะตรวจสอบว่าประเภท MIME ที่แอปพลิเคชันใช้ได้นั้นพร้อมใช้งานจากผู้ให้บริการหรือไม่
-
ประกาศตัวแปรร่วมเพื่อเก็บประเภท MIME ดังนี้
Kotlin
// Declares a MIME type constant to match against the MIME types offered // by the provider. const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
Java
// Declares a MIME type constant to match against the MIME types offered by // the provider. public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
- รับคลิปบอร์ดส่วนกลาง นอกจากนี้ ให้รับ Content Resolver เพื่อให้คุณเข้าถึง Content Provider ได้ด้วย ดังนี้
Kotlin
// Gets a handle to the Clipboard Manager. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // Gets a content resolver instance. val cr = contentResolver
Java
// Gets a handle to the Clipboard Manager. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Gets a content resolver instance. ContentResolver cr = getContentResolver();
- รับคลิปหลักจากคลิปบอร์ดและรับเนื้อหาเป็น URI:
Kotlin
// Gets the clipboard data from the clipboard. val clip: ClipData? = clipboard.primaryClip clip?.run { // Gets the first item from the clipboard data. val item: ClipData.Item = getItemAt(0) // Tries to get the item's contents as a URI. val pasteUri: Uri? = item.uri
Java
// Gets the clipboard data from the clipboard. ClipData clip = clipboard.getPrimaryClip(); if (clip != null) { // Gets the first item from the clipboard data. ClipData.Item item = clip.getItemAt(0); // Tries to get the item's contents as a URI. Uri pasteUri = item.getUri();
- ทดสอบว่า URI เป็น URI เนื้อหาหรือไม่โดยเรียกใช้
getType(Uri)เมธอดนี้จะแสดงผลเป็น Null หากUriไม่ได้ชี้ไปยัง Content Provider ที่ถูกต้องKotlin
// If the clipboard contains a URI reference... pasteUri?.let { // ...is this a content URI? val uriMimeType: String? = cr.getType(it)
Java
// If the clipboard contains a URI reference... if (pasteUri != null) { // ...is this a content URI? String uriMimeType = cr.getType(pasteUri);
- ทดสอบว่า Content Provider รองรับประเภท MIME ที่แอปพลิเคชันเข้าใจหรือไม่ หากมี ให้เรียกใช้
ContentResolver.query()เพื่อรับข้อมูล ค่าที่แสดงผลคือCursorKotlin
// If the return value isn't null, the Uri is a content Uri. uriMimeType?.takeIf { // Does the content provider offer a MIME type that the current // application can use? it == MIME_TYPE_CONTACT }?.apply { // Get the data from the content provider. cr.query(pasteUri, null, null, null, null)?.use { pasteCursor -> // If the Cursor contains data, move to the first record. if (pasteCursor.moveToFirst()) { // Get the data from the Cursor here. // The code varies according to the format of the data model. } // Kotlin `use` automatically closes the Cursor. } } } }
Java
// If the return value isn't null, the Uri is a content Uri. if (uriMimeType != null) { // Does the content provider offer a MIME type that the current // application can use? if (uriMimeType.equals(MIME_TYPE_CONTACT)) { // Get the data from the content provider. Cursor pasteCursor = cr.query(uri, null, null, null, null); // If the Cursor contains data, move to the first record. if (pasteCursor != null) { if (pasteCursor.moveToFirst()) { // Get the data from the Cursor here. // The code varies according to the format of the data model. } } // Close the Cursor. pasteCursor.close(); } } } }
วาง Intent
หากต้องการวาง Intent ให้รับคลิปบอร์ดส่วนกลางก่อน ตรวจสอบออบเจ็กต์ ClipData.Item เพื่อดูว่ามี Intent หรือไม่ จากนั้นเรียกใช้ getIntent() เพื่อคัดลอก Intent ไปยังพื้นที่เก็บข้อมูลของคุณเอง ข้อมูลโค้ดต่อไปนี้แสดงให้เห็นถึงวิธีการนี้
Kotlin
// Gets a handle to the Clipboard Manager. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // Checks whether the clip item contains an Intent by testing whether // getIntent() returns null. val pasteIntent: Intent? = clipboard.primaryClip?.getItemAt(0)?.intent if (pasteIntent != null) { // Handle the Intent. } else { // Ignore the clipboard, or issue an error if // you expect an Intent to be on the clipboard. }
Java
// Gets a handle to the Clipboard Manager. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Checks whether the clip item contains an Intent, by testing whether // getIntent() returns null. Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent(); if (pasteIntent != null) { // Handle the Intent. } else { // Ignore the clipboard, or issue an error if // you expect an Intent to be on the clipboard. }
การแจ้งเตือนของระบบที่แสดงเมื่อแอปเข้าถึงข้อมูลคลิปบอร์ด
ใน Android 12 (ระดับ API 31) ขึ้นไป โดยปกติแล้วระบบจะแสดงข้อความโทสต์เมื่อแอปเรียกใช้ getPrimaryClip()
ข้อความภายในข้อความมีรูปแบบต่อไปนี้
APP pasted from your clipboard
ระบบจะไม่แสดงข้อความโทสต์เมื่อแอปของคุณทำอย่างใดอย่างหนึ่งต่อไปนี้
- เข้าถึง
ClipDataจากแอปของคุณเอง - เข้าถึง
ClipDataซ้ำๆ จากแอปที่เฉพาะเจาะจง ข้อความโทสต์จะปรากฏขึ้นเมื่อแอปของคุณเข้าถึงข้อมูลจากแอปนั้นเป็นครั้งแรกเท่านั้น - ดึงข้อมูลเมตาสำหรับออบเจ็กต์คลิป เช่น โดยการเรียกใช้
getPrimaryClipDescription()แทนgetPrimaryClip()
ใช้ Content Provider เพื่อคัดลอกข้อมูลที่ซับซ้อน
Content Provider รองรับการคัดลอกข้อมูลที่ซับซ้อน เช่น ระเบียนฐานข้อมูลหรือสตรีมไฟล์ หากต้องการคัดลอกข้อมูล ให้ใส่ URI เนื้อหาในคลิปบอร์ด จากนั้นแอปพลิเคชันที่วางจะได้รับ URI นี้จากคลิปบอร์ดและใช้เพื่อดึงข้อมูลฐานข้อมูลหรือข้อบ่งชี้สตรีมไฟล์
เนื่องจากแอปพลิเคชันที่วางมีเพียง URI เนื้อหาสำหรับข้อมูลของคุณ จึงจำเป็นต้องทราบว่าต้องดึงข้อมูลส่วนใด คุณสามารถระบุข้อมูลนี้ได้โดยการเข้ารหัสตัวระบุสำหรับข้อมูลใน URI เอง หรือจะระบุ URI ที่ไม่ซ้ำกันซึ่งแสดงผลข้อมูลที่คุณต้องการคัดลอกก็ได้ เทคนิคที่คุณเลือกจะขึ้นอยู่กับการจัดระเบียบข้อมูล
ส่วนต่อไปนี้จะอธิบายวิธีตั้งค่า URI, ระบุข้อมูลที่ซับซ้อน และระบุสตรีมไฟล์ คำอธิบายจะถือว่าคุณคุ้นเคยกับหลักการทั่วไปของการออกแบบ Content Provider
เข้ารหัสตัวระบุใน URI
เทคนิคที่มีประโยชน์ในการคัดลอกข้อมูลไปยังคลิปบอร์ดด้วย URI คือการเข้ารหัสตัวระบุสำหรับข้อมูลใน URI เอง จากนั้น Content Provider จะรับตัวระบุจาก URI และใช้เพื่อดึงข้อมูลได้ แอปพลิเคชันที่วางไม่จำเป็นต้องทราบว่ามีรหัสระบุอยู่ เพียงแค่ต้องรับ "ข้อมูลอ้างอิง" ซึ่งก็คือ URI บวกกับตัวระบุจากคลิปบอร์ด ส่งให้ Content Provider แล้วรับข้อมูลกลับมา
โดยปกติแล้ว คุณจะเข้ารหัสตัวระบุลงใน URI เนื้อหาโดยเชื่อมโยงตัวระบุไว้ที่ส่วนท้ายของ URI ตัวอย่างเช่น สมมติว่าคุณกำหนด URI ของผู้ให้บริการเป็นสตริงต่อไปนี้
"content://com.example.contacts"
หากต้องการเข้ารหัสชื่อลงใน URI นี้ ให้ใช้ข้อมูลโค้ดต่อไปนี้
Kotlin
val uriString = "content://com.example.contacts/Smith" // uriString now contains content://com.example.contacts/Smith. // Generates a uri object from the string representation. val copyUri = Uri.parse(uriString)
Java
String uriString = "content://com.example.contacts" + "/" + "Smith"; // uriString now contains content://com.example.contacts/Smith. // Generates a uri object from the string representation. Uri copyUri = Uri.parse(uriString);
หากใช้ Content Provider อยู่แล้ว คุณอาจต้องเพิ่มเส้นทาง URI ใหม่ที่ระบุว่า URI มีไว้สำหรับการคัดลอก ตัวอย่างเช่น สมมติว่าคุณมีเส้นทาง URI ต่อไปนี้อยู่แล้ว
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
คุณสามารถเพิ่มเส้นทางอื่นสำหรับการคัดลอก URI ได้ดังนี้
"content://com.example.contacts/copying"
จากนั้นคุณจะตรวจหา URI "คัดลอก" ได้โดยการจับคู่รูปแบบและจัดการด้วยโค้ดที่เฉพาะเจาะจงสำหรับการคัดลอกและวาง
โดยปกติแล้ว คุณจะใช้เทคนิคการเข้ารหัสหากใช้ Content Provider, ฐานข้อมูลภายใน หรือตารางภายในเพื่อจัดระเบียบข้อมูลอยู่แล้ว ในกรณีเหล่านี้ คุณมีข้อมูลหลายรายการที่ต้องการคัดลอก และอาจมีตัวระบุที่ไม่ซ้ำกันสำหรับแต่ละรายการ เมื่อตอบสนองต่อการค้นหาจากแอปพลิเคชันที่วาง คุณสามารถค้นหาข้อมูลตามตัวระบุและแสดงผลข้อมูลนั้นได้
หากไม่มีข้อมูลหลายรายการ คุณก็อาจไม่จำเป็นต้องเข้ารหัสตัวระบุ คุณสามารถใช้ URI ที่ไม่ซ้ำกันสำหรับผู้ให้บริการของคุณ ผู้ให้บริการจะแสดงผลข้อมูลที่มีอยู่ในปัจจุบันเพื่อตอบสนองต่อการค้นหา
คัดลอกโครงสร้างข้อมูล
ตั้งค่า Content Provider สำหรับการคัดลอกและวางข้อมูลที่ซับซ้อนเป็นคลาสย่อยของคอมโพเนนต์ ContentProvider เข้ารหัส URI ที่คุณใส่ไว้ในคลิปบอร์ดเพื่อให้ชี้ไปยังระเบียนที่ต้องการระบุ นอกจากนี้ ให้พิจารณาสถานะปัจจุบันของแอปพลิเคชันด้วย ดังนี้
- หากมี Content Provider อยู่แล้ว คุณสามารถเพิ่มฟังก์ชันการทำงานได้ คุณอาจต้องแก้ไขเฉพาะเมธอด
query()เพื่อจัดการ URI ที่มาจากแอปพลิเคชันที่ต้องการวางข้อมูล คุณอาจต้องการแก้ไขเมธอดเพื่อจัดการรูปแบบ URI "copy" - หากแอปพลิเคชันของคุณดูแลรักษาฐานข้อมูลภายใน คุณอาจต้องย้ายฐานข้อมูลนี้ไปยัง Content Provider เพื่ออำนวยความสะดวกในการคัดลอกข้อมูลจากฐานข้อมูลดังกล่าว
- หากไม่ได้ใช้ฐานข้อมูล คุณสามารถใช้ Content Provider แบบง่ายที่มีวัตถุประสงค์เพียงอย่างเดียวคือการเสนอข้อมูลให้กับแอปพลิเคชันที่วางจากคลิปบอร์ด
ใน Content Provider ให้ลบล้างเมธอดต่อไปนี้เป็นอย่างน้อย
-
query() - แอปพลิเคชันที่วางจะถือว่าสามารถรับข้อมูลของคุณได้โดยใช้วิธีนี้กับ URI ที่คุณใส่ไว้ในคลิปบอร์ด หากต้องการรองรับการคัดลอก ให้เมธอดนี้ตรวจหา URI ที่มีเส้นทาง "copy" พิเศษ จากนั้นแอปพลิเคชันจะสร้าง URI "คัดลอก" เพื่อวางในคลิปบอร์ด ซึ่งมีเส้นทางการคัดลอกและตัวชี้ไปยังระเบียนที่ต้องการคัดลอก
-
getType() - เมธอดนี้ต้องแสดงผลประเภท MIME สำหรับข้อมูลที่คุณต้องการคัดลอก เมธอด
newUri()จะเรียกใช้getType()เพื่อใส่ประเภท MIME ลงในออบเจ็กต์ClipDataใหม่ประเภท MIME สำหรับข้อมูลที่ซับซ้อนจะอธิบายไว้ในผู้ให้บริการเนื้อหา
คุณไม่จำเป็นต้องมีเมธอด Content Provider อื่นๆ เช่น
insert()
หรือ
update()
แอปพลิเคชันที่วางเพียงแค่ต้องรับประเภท MIME ที่รองรับและคัดลอกข้อมูลจากผู้ให้บริการของคุณ
หากคุณมีวิธีการเหล่านี้อยู่แล้ว วิธีการดังกล่าวจะไม่รบกวนการดำเนินการคัดลอก
ข้อมูลโค้ดต่อไปนี้แสดงวิธีตั้งค่าแอปพลิเคชันเพื่อคัดลอกข้อมูลที่ซับซ้อน
-
ในค่าคงที่ส่วนกลางสำหรับแอปพลิเคชัน ให้ประกาศสตริง URI พื้นฐานและเส้นทางที่ระบุสตริง URI ที่คุณใช้เพื่อคัดลอกข้อมูล นอกจากนี้ ให้ประกาศประเภท MIME สำหรับข้อมูลที่คัดลอกด้วย
Kotlin
// Declares the base URI string. private const val CONTACTS = "content://com.example.contacts" // Declares a path string for URIs that you use to copy data. private const val COPY_PATH = "/copy" // Declares a MIME type for the copied data. const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
Java
// Declares the base URI string. private static final String CONTACTS = "content://com.example.contacts"; // Declares a path string for URIs that you use to copy data. private static final String COPY_PATH = "/copy"; // Declares a MIME type for the copied data. public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
- ในกิจกรรมที่ผู้ใช้คัดลอกข้อมูล ให้ตั้งค่าโค้ดเพื่อคัดลอกข้อมูลไปยังคลิปบอร์ด
เมื่อตอบสนองต่อคำขอคัดลอก ให้ใส่ URI ไว้ในคลิปบอร์ด
Kotlin
class MyCopyActivity : Activity() { ... when(item.itemId) { R.id.menu_copy -> { // The user has selected a name and is requesting a copy. // Appends the last name to the base URI. // The name is stored in "lastName". uriString = "$CONTACTS$COPY_PATH/$lastName" // Parses the string into a URI. val copyUri: Uri? = Uri.parse(uriString) // Gets a handle to the clipboard service. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri) // Sets the clipboard's primary clip. clipboard.setPrimaryClip(clip) } }
Java
public class MyCopyActivity extends Activity { ... // The user has selected a name and is requesting a copy. case R.id.menu_copy: // Appends the last name to the base URI. // The name is stored in "lastName". uriString = CONTACTS + COPY_PATH + "/" + lastName; // Parses the string into a URI. Uri copyUri = Uri.parse(uriString); // Gets a handle to the clipboard service. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri); // Sets the clipboard's primary clip. clipboard.setPrimaryClip(clip);
-
ในขอบเขตส่วนกลางของ Content Provider ให้สร้างตัวจับคู่ URI และเพิ่มรูปแบบ URI ที่ตรงกับ URI ที่คุณใส่ไว้ในคลิปบอร์ด
Kotlin
// A Uri Match object that simplifies matching content URIs to patterns. private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { // Adds a matcher for the content URI. It matches. // "content://com.example.contacts/copy/*" addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT) } // An integer to use in switching based on the incoming URI pattern. private const val GET_SINGLE_CONTACT = 0 ... class MyCopyProvider : ContentProvider() { ... }
Java
public class MyCopyProvider extends ContentProvider { ... // A Uri Match object that simplifies matching content URIs to patterns. private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); // An integer to use in switching based on the incoming URI pattern. private static final int GET_SINGLE_CONTACT = 0; ... // Adds a matcher for the content URI. It matches // "content://com.example.contacts/copy/*" sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
-
ตั้งค่าวิธีการ
query()เมธอดนี้สามารถจัดการรูปแบบ URI ที่แตกต่างกันได้ ทั้งนี้ขึ้นอยู่กับวิธีเขียนโค้ด แต่จะแสดงเฉพาะรูปแบบสำหรับการดำเนินการคัดลอกคลิปบอร์ดเท่านั้นKotlin
// Sets up your provider's query() method. override fun query( uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { ... // When based on the incoming content URI: when(sUriMatcher.match(uri)) { GET_SINGLE_CONTACT -> { // Queries and returns the contact for the requested name. Decodes // the incoming URI, queries the data model based on the last name, // and returns the result as a Cursor. } } ... }
Java
// Sets up your provider's query() method. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... // Switch based on the incoming content URI. switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: // Queries and returns the contact for the requested name. Decodes the // incoming URI, queries the data model based on the last name, and // returns the result as a Cursor. ... }
-
ตั้งค่าเมธอด
getType()เพื่อแสดงผลประเภท MIME ที่เหมาะสมสำหรับข้อมูลที่คัดลอก ดังนี้Kotlin
// Sets up your provider's getType() method. override fun getType(uri: Uri): String? { ... return when(sUriMatcher.match(uri)) { GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT ... } }
Java
// Sets up your provider's getType() method. public String getType(Uri uri) { ... switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: return (MIME_TYPE_CONTACT); ... } }
ส่วนวางข้อมูลจาก URI เนื้อหาจะอธิบายวิธีรับ URI เนื้อหาจากคลิปบอร์ดและใช้เพื่อรับและวางข้อมูล
คัดลอกสตรีมข้อมูล
คุณสามารถคัดลอกและวางข้อความและข้อมูลไบนารีจำนวนมากเป็นสตรีมได้ ข้อมูลอาจมีรูปแบบต่างๆ ดังต่อไปนี้
- ไฟล์ที่จัดเก็บไว้ในอุปกรณ์จริง
- สตรีมจากซ็อกเก็ต
- ข้อมูลจำนวนมากที่จัดเก็บไว้ในระบบฐานข้อมูลพื้นฐานของผู้ให้บริการ
Content Provider สำหรับสตรีมข้อมูลจะให้สิทธิ์เข้าถึงข้อมูลด้วยออบเจ็กต์ตัวบอกไฟล์ เช่น AssetFileDescriptor แทนออบเจ็กต์ Cursor แอปพลิเคชันที่วางจะอ่านสตรีมข้อมูลโดยใช้ตัวบอกไฟล์นี้
หากต้องการตั้งค่าแอปพลิเคชันให้คัดลอกสตรีมข้อมูลกับผู้ให้บริการ ให้ทำตามขั้นตอนต่อไปนี้
-
ตั้งค่า URI เนื้อหาสำหรับสตรีมข้อมูลที่คุณกำลังวางในคลิปบอร์ด ตัวเลือกในการดำเนินการนี้มีดังต่อไปนี้
- เข้ารหัสตัวระบุสำหรับสตรีมข้อมูลลงใน URI ตามที่อธิบายไว้ในส่วนเข้ารหัสตัวระบุใน URI จากนั้นเก็บรักษาตารางในผู้ให้บริการที่มีตัวระบุและชื่อสตรีมที่เกี่ยวข้อง
- เข้ารหัสชื่อสตรีมใน URI โดยตรง
- ใช้ URI ที่ไม่ซ้ำกันซึ่งจะแสดงสตรีมปัจจุบันจากผู้ให้บริการเสมอ หากใช้ตัวเลือกนี้ อย่าลืมอัปเดตผู้ให้บริการให้ชี้ไปยังสตรีมอื่นทุกครั้งที่คุณคัดลอกสตรีมไปยังคลิปบอร์ดโดยใช้ URI
- ระบุประเภท MIME สำหรับสตรีมข้อมูลแต่ละประเภทที่คุณวางแผนจะนำเสนอ แอปพลิเคชันที่วางต้องการข้อมูลนี้เพื่อพิจารณาว่าจะวางข้อมูลในคลิปบอร์ดได้หรือไม่
- ใช้เมธอด
ContentProviderวิธีใดวิธีหนึ่งที่แสดงผลตัวบอกไฟล์สำหรับสตรีม หากคุณเข้ารหัสตัวระบุใน URI เนื้อหา ให้ใช้วิธีนี้เพื่อระบุสตรีมที่จะเปิด - หากต้องการคัดลอกสตรีมข้อมูลไปยังคลิปบอร์ด ให้สร้าง URI เนื้อหาและวางไว้ในคลิปบอร์ด
หากต้องการวางสตรีมข้อมูล แอปพลิเคชันจะรับคลิปจากคลิปบอร์ด รับ URI และใช้ URI นั้นในการเรียกใช้เมธอดตัวบอกไฟล์ ContentResolver ที่เปิดสตรีม เมธอด ContentResolver จะเรียกใช้เมธอด ContentProvider ที่เกี่ยวข้อง โดยส่ง URI เนื้อหาไปให้ ผู้ให้บริการจะแสดงผลตัวบอกไฟล์ไปยังเมธอด ContentResolver จากนั้นแอปพลิเคชันที่วางจะมีหน้าที่อ่านข้อมูลจากสตรีม
รายการต่อไปนี้แสดงเมธอดตัวบอกไฟล์ที่สำคัญที่สุดสำหรับ Content Provider แต่ละรายการจะมีเมธอด ContentResolver ที่สอดคล้องกัน โดยมีสตริง "Descriptor" ต่อท้ายชื่อเมธอด ตัวอย่างเช่น ContentResolver
ที่คล้ายกับ
openAssetFile()
คือ
openAssetFileDescriptor()
-
openTypedAssetFile() -
เมธอดนี้จะแสดงผลตัวบอกไฟล์ของชิ้นงาน แต่จะแสดงผลก็ต่อเมื่อผู้ให้บริการรองรับประเภท MIME ที่ระบุเท่านั้น ผู้เรียก (แอปพลิเคชันที่วาง) จะระบุรูปแบบประเภท MIME Content Provider ของแอปพลิเคชันที่คัดลอก URI ไปยังคลิปบอร์ดจะแสดงผลแฮนเดิลไฟล์
AssetFileDescriptorหากระบุประเภท MIME นั้นได้ และจะแสดงข้อยกเว้นหากระบุไม่ได้วิธีนี้จะจัดการส่วนย่อยของไฟล์ คุณสามารถใช้เพื่ออ่านเนื้อหาที่ Content Provider คัดลอกไปยังคลิปบอร์ดได้
-
openAssetFile() -
เมธอดนี้เป็นรูปแบบทั่วไปของ
openTypedAssetFile()โดยจะไม่กรองประเภท MIME ที่อนุญาต แต่จะอ่านส่วนย่อยของไฟล์ได้ -
openFile() -
นี่เป็นรูปแบบทั่วไปของ
openAssetFile()แต่จะอ่านส่วนย่อยของไฟล์ไม่ได้
คุณจะใช้เมธอด openPipeHelper() กับเมธอดตัวบอกไฟล์หรือไม่ก็ได้ ซึ่งจะช่วยให้แอปพลิเคชันที่วางอ่านข้อมูลสตรีมในชุดข้อความเบื้องหลังโดยใช้ไปป์ได้ หากต้องการใช้วิธีนี้ ให้ใช้อินเทอร์เฟซ ContentProvider.PipeDataWriter
ออกแบบฟังก์ชันการคัดลอกและวางที่มีประสิทธิภาพ
โปรดคำนึงถึงประเด็นต่อไปนี้เมื่อออกแบบฟังก์ชันคัดลอกและวางที่มีประสิทธิภาพสำหรับแอปพลิเคชัน
- ในแต่ละครั้งจะมีคลิปเพียงรายการเดียวในคลิปบอร์ด การดำเนินการคัดลอกใหม่โดยแอปพลิเคชันใดก็ตามในระบบจะเขียนทับคลิปก่อนหน้า เนื่องจากผู้ใช้อาจออกจากแอปพลิเคชันของคุณและคัดลอกก่อนที่จะกลับมา คุณจึงไม่สามารถสันนิษฐานได้ว่าคลิปบอร์ดมีคลิปที่ผู้ใช้คัดลอกไว้ก่อนหน้านี้ในแอปพลิเคชันของคุณ
-
วัตถุประสงค์ที่ต้องการของออบเจ็กต์
ClipData.Itemหลายรายการต่อคลิปคือเพื่อรองรับการคัดลอกและวางรายการที่เลือกหลายรายการ แทนที่จะเป็นการอ้างอิงรูปแบบต่างๆ ไปยังรายการที่เลือกรายการเดียว โดยปกติแล้ว คุณต้องการให้ClipData.Itemออบเจ็กต์ทั้งหมดในคลิปมีรูปแบบเดียวกัน กล่าวคือ ต้องเป็นข้อความธรรมดา, URI เนื้อหา หรือIntentทั้งหมด และไม่ผสมกัน -
เมื่อระบุข้อมูล คุณสามารถเสนอการแสดง MIME ที่แตกต่างกันได้ เพิ่มประเภท MIME ที่คุณรองรับลงใน
ClipDescriptionแล้วใช้ประเภท MIME ใน Content Provider -
เมื่อคุณรับข้อมูลจากคลิปบอร์ด แอปพลิเคชันของคุณมีหน้าที่ตรวจสอบประเภท MIME ที่ใช้ได้ แล้วตัดสินใจว่าจะใช้ประเภทใด (หากมี) แม้ว่าจะมีคลิปในคลิปบอร์ดและผู้ใช้ขอให้วาง แต่แอปพลิเคชันของคุณไม่จำเป็นต้องทำการวาง วางหากประเภท MIME เข้ากันได้ คุณอาจบังคับข้อมูลในคลิปบอร์ดให้เป็นข้อความโดยใช้
coerceToText()หากแอปพลิเคชันของคุณรองรับประเภท MIME ที่มีอยู่มากกว่า 1 ประเภท คุณสามารถให้ผู้ใช้เลือกประเภทที่จะใช้ได้