Android มีเฟรมเวิร์กที่ใช้คลิปบอร์ดอันทรงประสิทธิภาพสำหรับการคัดลอกและวาง โดยรองรับประเภทข้อมูลทั้งแบบง่ายและซับซ้อน ซึ่งรวมถึงสตริงข้อความ โครงสร้างข้อมูลที่ซับซ้อน ข้อมูลสตรีมข้อความและไบนารี ตลอดจนชิ้นงานแอปพลิเคชัน ข้อมูลข้อความธรรมดาจะเก็บไว้ในคลิปบอร์ดโดยตรง ส่วนข้อมูลที่ซับซ้อนจะถูกเก็บไว้เป็นข้อมูลอ้างอิงที่แอปพลิเคชันการวางจะแก้ไขปัญหาด้วยผู้ให้บริการเนื้อหา การคัดลอกและวางใช้ได้ทั้งในแอปพลิเคชันและระหว่างแอปพลิเคชันที่ใช้เฟรมเวิร์ก
เนื่องจากเฟรมเวิร์กบางส่วนใช้ผู้ให้บริการเนื้อหา เอกสารนี้จึงถือว่าคุณคุ้นเคยกับ Android Content Provider API อยู่บ้าง ซึ่งอธิบายไว้ในผู้ให้บริการเนื้อหา
ผู้ใช้คาดหวังที่จะได้รับความคิดเห็นเมื่อคัดลอกเนื้อหาไปยังคลิปบอร์ด ดังนั้นนอกเหนือจากเฟรมเวิร์กที่ช่วยในการคัดลอกและวางแล้ว Android ยังแสดง UI เริ่มต้นต่อผู้ใช้เมื่อคัดลอกใน Android 13 (API ระดับ 33) ขึ้นไป ฟีเจอร์นี้จึงมีความเสี่ยงที่จะเกิดการแจ้งเตือนซ้ำ ดูข้อมูลเพิ่มเติมเกี่ยวกับกรณีขอบเขตนี้ในส่วนหลีกเลี่ยงการแจ้งเตือนที่ซ้ำกัน
แสดงความคิดเห็นให้ผู้ใช้ทราบด้วยตนเองเมื่อคัดลอกใน Android 12L (API ระดับ 32) และต่ำกว่า ดูคําแนะนําสําหรับกรณีนี้ในเอกสารนี้
เฟรมเวิร์กคลิปบอร์ด
เมื่อใช้เฟรมเวิร์กคลิปบอร์ด ให้นำข้อมูลไปไว้ในออบเจ็กต์คลิป แล้ววางออบเจ็กต์คลิป บนคลิปบอร์ดที่ใช้ทั่วทั้งระบบ ออบเจ็กต์คลิปมีรูปแบบใดรูปแบบหนึ่งต่อไปนี้
- ข้อความ
- สตริงข้อความ ใส่สตริงในออบเจ็กต์คลิปโดยตรง แล้ววางลงในคลิปบอร์ด หากต้องการวางสตริง ให้รับออบเจ็กต์คลิปจากคลิปบอร์ดแล้วคัดลอกสตริงไปยังพื้นที่เก็บข้อมูลของแอปพลิเคชัน
- URI
-
ออบเจ็กต์
Uri
ที่แสดง URI ในรูปแบบใดก็ได้ ซึ่งใช้สำหรับการคัดลอกข้อมูลที่ซับซ้อนจากผู้ให้บริการเนื้อหาเป็นหลัก หากต้องการคัดลอกข้อมูล ให้วางออบเจ็กต์Uri
ลงในออบเจ็กต์คลิป แล้ววางออบเจ็กต์คลิปลงในคลิปบอร์ด หากต้องการวางข้อมูล ให้รับออบเจ็กต์คลิป รับออบเจ็กต์Uri
แปลงเป็นแหล่งข้อมูล เช่น ผู้ให้บริการเนื้อหา แล้วคัดลอกข้อมูลจากแหล่งที่มาไปยังพื้นที่เก็บข้อมูลของแอปพลิเคชัน - ความตั้งใจ
-
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 ของผู้ให้บริการเนื้อหา แต่จะใช้ URI ใดก็ได้ แอปพลิเคชันที่ให้ข้อมูลจะวาง URI บนคลิปบอร์ด แอปพลิเคชันที่ต้องการให้วางข้อมูลจะได้รับ URI จากคลิปบอร์ดและใช้ URI ดังกล่าวเพื่อเข้าถึงผู้ให้บริการเนื้อหาหรือแหล่งข้อมูลอื่นๆ และดึงข้อมูล - Intent
-
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 ที่มีจากผู้ให้บริการเนื้อหา แล้วจัดเก็บไว้ในClipDescription
สำหรับ URI ที่ไม่ใช่content:
URI วิธีการนี้จะตั้งค่าประเภท MIME เป็นMIMETYPE_TEXT_URILIST
ใช้
newUri()
เพื่อสร้างคลิปจาก URI โดยเฉพาะcontent:
URI -
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()
ไม่ใช่ค่าว่างcoerceToText()
จะพยายามใช้ URI ดังกล่าวเป็น 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
หรือจากผู้ให้บริการเนื้อหาที่ ClipData.Item
อ้างอิง
คัดลอกไปยังคลิปบอร์ด
หากต้องการคัดลอกข้อมูลไปยังคลิปบอร์ด ให้รับแฮนเดิลของออบเจ็กต์ ClipboardManager
ระดับโลก สร้างออบเจ็กต์ ClipData
และเพิ่มออบเจ็กต์ ClipDescription
และClipData.Item
อย่างน้อย 1 รายการ จากนั้นเพิ่มออบเจ็กต์ ClipData
ที่เสร็จแล้วลงในออบเจ็กต์ ClipboardManager
โปรดดูรายละเอียดเพิ่มเติมในขั้นตอนต่อไปนี้
- หากคุณคัดลอกข้อมูลโดยใช้ URI เนื้อหา ให้ตั้งค่าผู้ให้บริการเนื้อหา
- รับคลิปบอร์ดของระบบ
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()
การเพิ่มแฟล็กนี้จะป้องกันไม่ให้เนื้อหาที่ละเอียดอ่อนปรากฏในการยืนยันด้วยภาพของเนื้อหาที่คัดลอกมาใน 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); }
- คัดลอกข้อมูลจากคลิปบอร์ด โค้ดจะไปถึงจุดนี้ต่อเมื่อเปิดใช้รายการเมนู "วาง" เท่านั้น คุณจึงถือว่าคลิปบอร์ดมีข้อความธรรมดา คุณยังไม่ทราบว่า URL ดังกล่าวมีสตริงข้อความหรือ 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
และเรียกใช้เมธอดของผู้ให้บริการเนื้อหาที่เหมาะสมเพื่อดึงข้อมูล
ขั้นตอนต่อไปนี้อธิบายวิธีรับข้อมูลจากผู้ให้บริการเนื้อหาตาม 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";
- ดาวน์โหลดคลิปบอร์ดส่วนกลาง นอกจากนี้ คุณยังต้องมีโปรแกรมแก้ไขเนื้อหาเพื่อให้เข้าถึงผู้ให้บริการเนื้อหาได้
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
ไม่ได้ชี้ไปยังผู้ให้บริการเนื้อหาที่ถูกต้อง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);
- ทดสอบว่าผู้ให้บริการเนื้อหารองรับประเภท MIME ที่แอปพลิเคชันเข้าใจหรือไม่ หากมี ให้โทรหา
ContentResolver.query()
เพื่อรับข้อมูล ผลลัพธ์คือCursor
Kotlin
// 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
หากต้องการวางความตั้งใจ ให้เริ่มจากคลิปบอร์ดส่วนกลาง ตรวจสอบออบเจ็กต์ 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()
ใช้ผู้ให้บริการเนื้อหาเพื่อคัดลอกข้อมูลที่ซับซ้อน
ผู้ให้บริการเนื้อหารองรับการคัดลอกข้อมูลที่ซับซ้อน เช่น ระเบียนฐานข้อมูลหรือสตรีมไฟล์ หากต้องการคัดลอกข้อมูล ให้วาง URI ของเนื้อหาลงในคลิปบอร์ด จากนั้นแอปพลิเคชันวางจะรับ URI นี้จากคลิปบอร์ด และใช้เพื่อดึงข้อมูลฐานข้อมูลหรือตัวบ่งชี้สตรีมไฟล์
เนื่องจากแอปพลิเคชันการวางมีเพียง URI เนื้อหาสําหรับข้อมูลของคุณ แอปพลิเคชันจึงต้องทราบว่าจะดึงข้อมูลส่วนใด คุณสามารถระบุข้อมูลนี้โดยการเข้ารหัสตัวระบุสําหรับข้อมูลใน URI เอง หรือระบุ URI ที่ไม่ซ้ำกันซึ่งแสดงข้อมูลที่คุณต้องการคัดลอก เทคนิคที่คุณเลือกจะขึ้นอยู่กับการจัดระเบียบข้อมูล
ส่วนต่อไปนี้จะอธิบายวิธีตั้งค่า URI, ระบุข้อมูลที่ซับซ้อน และระบุไฟล์สตรีม คำอธิบายนี้ถือว่าคุณคุ้นเคยกับหลักการทั่วไปของการออกแบบสำหรับผู้ให้บริการเนื้อหา
เข้ารหัสตัวระบุใน URI
เทคนิคที่มีประโยชน์ในการคัดลอกข้อมูลไปยังคลิปบอร์ดด้วย URI คือการเข้ารหัสตัวระบุสำหรับข้อมูลใน URI นั้นๆ จากนั้นผู้ให้บริการเนื้อหาจะรับตัวระบุจาก URI และใช้ตัวระบุดังกล่าวเพื่อเรียกข้อมูล แอปพลิเคชันที่วางไม่ต้องทราบว่ามีตัวระบุอยู่ คุณเพียงแค่ต้องรับ "ข้อมูลอ้างอิง" หรือ URI และตัวระบุจากคลิปบอร์ด แล้วระบุผู้ให้บริการเนื้อหา จากนั้นรับข้อมูลกลับมา
โดยปกติแล้ว คุณจะเข้ารหัสตัวระบุลงใน 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);
หากคุณใช้ผู้ให้บริการเนื้อหาอยู่แล้ว คุณอาจต้องเพิ่มเส้นทาง 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 "copy" โดยการจับคู่รูปแบบและจัดการด้วยโค้ดที่เจาะจงสำหรับการคัดลอกและวาง
ปกติแล้ว คุณจะใช้เทคนิคการเข้ารหัสหากใช้ผู้ให้บริการเนื้อหา ฐานข้อมูลภายใน หรือตารางภายในเพื่อจัดระเบียบข้อมูลอยู่แล้ว ในกรณีเหล่านี้ คุณมีข้อมูลหลายรายการที่ต้องการคัดลอก และคาดว่าจะมีตัวระบุที่ไม่ซ้ำกันสำหรับแต่ละรายการ ในการตอบกลับการค้นหาจากแอปพลิเคชันการคัดลอก คุณสามารถค้นหาข้อมูลตามตัวระบุและแสดงผลได้
หากมีข้อมูลเพียงไม่กี่รายการ คุณอาจไม่จำเป็นต้องเข้ารหัสตัวระบุ คุณสามารถใช้ URI ที่ไม่ซ้ำกันสำหรับผู้ให้บริการของคุณ ผู้ให้บริการจะแสดงข้อมูลที่เก็บไว้ในปัจจุบันเพื่อตอบกลับการค้นหา
คัดลอกโครงสร้างข้อมูล
ตั้งค่าผู้ให้บริการเนื้อหาสำหรับการคัดลอกและวางข้อมูลที่ซับซ้อนเป็นคลาสย่อยของคอมโพเนนต์ ContentProvider
เข้ารหัส URI ที่คุณวางไว้ในคลิปบอร์ดเพื่อให้ชี้ไปยังระเบียนที่ต้องการระบุ นอกจากนี้ ให้พิจารณาสถานะปัจจุบันของใบสมัครดังนี้
- หากมีผู้ให้บริการเนื้อหาอยู่แล้ว คุณสามารถเพิ่มฟังก์ชันการทำงานได้ คุณอาจต้องแก้ไขเมธอด
query()
เพื่อจัดการ URI ที่มาจากแอปพลิเคชันที่ต้องการวางข้อมูลเท่านั้น คุณอาจต้องแก้ไขวิธีจัดการรูปแบบ URI "คัดลอก" - หากแอปพลิเคชันของคุณดูแลรักษาฐานข้อมูลภายใน คุณอาจต้องย้ายฐานข้อมูลนี้ไปยังผู้ให้บริการเนื้อหาเพื่อให้คัดลอกได้ง่ายขึ้น
- หากไม่ได้ใช้ฐานข้อมูล คุณสามารถใช้ผู้ให้บริการเนื้อหาแบบง่ายที่มีวัตถุประสงค์เพียงอย่างเดียวคือนำเสนอข้อมูลแก่แอปพลิเคชันที่วางจากคลิปบอร์ด
ในผู้ให้บริการเนื้อหา ให้ลบล้างวิธีการต่อไปนี้เป็นอย่างน้อย
-
query()
- การวางแอปพลิเคชันจะถือว่าแอปพลิเคชันสามารถรับข้อมูลของคุณได้โดยใช้เมธอดนี้กับ URI ที่คุณวางไว้ในคลิปบอร์ด หากต้องการรองรับการคัดลอก ให้เมธอดนี้ตรวจหา URI ที่มีเส้นทาง "copy" พิเศษ จากนั้นแอปพลิเคชันจะสร้าง URI "copy" เพื่อวางลงในคลิปบอร์ด ซึ่งมีเส้นทางการคัดลอกและเคอร์เซอร์ไปยังระเบียนที่ต้องการคัดลอก
-
getType()
- เมธอดนี้ต้องแสดงผลประเภท MIME สำหรับข้อมูลที่คุณต้องการคัดลอก เมธอด
newUri()
จะเรียกใช้getType()
เพื่อใส่ประเภท MIME ลงในออบเจ็กต์ClipData
ใหม่ประเภท MIME สำหรับข้อมูลที่ซับซ้อนจะอธิบายไว้ในผู้ให้บริการเนื้อหา
คุณไม่จำเป็นต้องมีวิธีการอื่นๆ ของผู้ให้บริการเนื้อหา เช่น
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);
-
ในขอบเขตรวมของผู้ให้บริการเนื้อหา ให้สร้างเครื่องมือจับคู่ 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 เนื้อหาจากคลิปบอร์ด รวมถึงใช้เพื่อรับและวางข้อมูล
คัดลอกสตรีมข้อมูล
คุณสามารถคัดลอกและวางข้อความและข้อมูลไบนารีจํานวนมากเป็นสตรีมได้ ข้อมูลอาจมีแบบฟอร์ม ดังตัวอย่างต่อไปนี้
- ไฟล์ที่จัดเก็บไว้ในอุปกรณ์จริง
- สตรีมจากซ็อกเก็ต
- ข้อมูลจํานวนมากที่จัดเก็บไว้ในระบบฐานข้อมูลที่สําคัญของผู้ให้บริการ
ผู้ให้บริการเนื้อหาสำหรับสตรีมข้อมูลให้สิทธิ์เข้าถึงข้อมูลด้วยออบเจ็กต์ข้อบ่งชี้ไฟล์ เช่น AssetFileDescriptor
แทนที่จะเป็นออบเจ็กต์ Cursor
แอปพลิเคชันวางจะอ่านสตรีมข้อมูลโดยใช้ตัวระบุไฟล์นี้
หากต้องการตั้งค่าแอปพลิเคชันให้คัดลอกสตรีมข้อมูลกับผู้ให้บริการ ให้ทำตามขั้นตอนต่อไปนี้
-
ตั้งค่า URI ของเนื้อหาสำหรับสตรีมข้อมูลที่คุณวางไว้ในคลิปบอร์ด ตัวเลือกในการดำเนินการมีดังนี้
- เข้ารหัสตัวระบุสําหรับสตรีมข้อมูลใน URI ตามที่อธิบายไว้ในส่วนเข้ารหัสตัวระบุใน URI จากนั้นดูแลรักษาตารางในผู้ให้บริการซึ่งมีตัวระบุและชื่อสตรีมที่เกี่ยวข้อง
- เข้ารหัสชื่อสตรีมใน URI โดยตรง
- ใช้ URI ที่ไม่ซ้ำกันซึ่งจะแสดงผลสตรีมปัจจุบันจากผู้ให้บริการเสมอ หากใช้ตัวเลือกนี้ โปรดอย่าลืมอัปเดตผู้ให้บริการให้ชี้ไปยังสตรีมอื่นทุกครั้งที่คุณคัดลอกสตรีมไปยังคลิปบอร์ดโดยใช้ URI
- ระบุประเภท MIME สําหรับสตรีมข้อมูลแต่ละประเภทที่คุณวางแผนจะเสนอ แอปพลิเคชันการวางต้องใช้ข้อมูลนี้เพื่อระบุว่าสามารถวางข้อมูลในคลิปบอร์ดได้หรือไม่
- ใช้เมธอด
ContentProvider
รายการใดรายการหนึ่งซึ่งแสดงผลตัวระบุไฟล์สําหรับสตรีม หากคุณเข้ารหัสตัวระบุใน URI เนื้อหา ให้ใช้วิธีการนี้เพื่อกำหนดสตรีมที่จะเปิด - หากต้องการคัดลอกสตรีมข้อมูลไปยังคลิปบอร์ด ให้สร้าง URI ของเนื้อหาและวางลงในคลิปบอร์ด
หากต้องการวางสตรีมข้อมูล แอปพลิเคชันจะรับคลิปจากคลิปบอร์ด รับ URI และใช้คลิปในการเรียกใช้เมธอดตัวระบุไฟล์ ContentResolver
ที่เปิดสตรีม เมธอด ContentResolver
จะเรียกเมธอด ContentProvider
ที่เกี่ยวข้อง โดยส่ง URI ของเนื้อหาไปให้ ผู้ให้บริการจะแสดงผลตัวระบุไฟล์ไปยังเมธอด ContentResolver
จากนั้นแอปพลิเคชันวางจะมีหน้าที่รับผิดชอบในการอ่านข้อมูลจากสตรีม
รายการต่อไปนี้แสดงวิธีการระบุไฟล์ที่สำคัญที่สุดสำหรับผู้ให้บริการเนื้อหา แต่ละรายการมีเมธอด ContentResolver
ที่สอดคล้องกันโดยมีสตริง "Descriptor" ต่อท้ายชื่อเมธอด เช่น ContentResolver
อะนาล็อกของ
openAssetFile()
คือ
openAssetFileDescriptor()
-
openTypedAssetFile()
-
วิธีนี้จะแสดงข้อบ่งชี้ไฟล์เนื้อหา แต่เฉพาะในกรณีที่ผู้ให้บริการรองรับประเภท MIME ที่ระบุเท่านั้น ผู้โทร (แอปพลิเคชันที่กำลังวาง) จะระบุรูปแบบประเภท MIME ผู้ให้บริการเนื้อหาของแอปพลิเคชันที่คัดลอก URI ไปยังคลิปบอร์ดจะแสดงผลแฮนเดิลไฟล์
AssetFileDescriptor
หากสามารถระบุประเภท MIME นั้นได้ และแสดงข้อยกเว้นหากไม่สามารถระบุได้วิธีการนี้จัดการกับส่วนย่อยของไฟล์ คุณสามารถใช้เพื่ออ่านชิ้นงานที่ผู้ให้บริการเนื้อหาคัดลอกไปยังคลิปบอร์ด
-
openAssetFile()
-
วิธีนี้เป็นรูปแบบทั่วไปของ
openTypedAssetFile()
โดยจะไม่กรองไฟล์ประเภท MIME ที่อนุญาต แต่สามารถอ่านส่วนย่อยของไฟล์ได้ -
openFile()
-
รูปแบบนี้เป็นรูปแบบทั่วไปของ
openAssetFile()
และไม่สามารถอ่านส่วนย่อยของไฟล์ได้
หรือคุณจะเลือกใช้เมธอด openPipeHelper()
กับเมธอดข้อบ่งชี้ไฟล์ก็ได้ ซึ่งจะช่วยให้แอปพลิเคชันวางอ่านข้อมูลสตรีมในเธรดเบื้องหลังได้โดยใช้ไปป์ หากต้องการใช้วิธีการนี้ ให้ใช้อินเตอร์เฟซ ContentProvider.PipeDataWriter
ออกแบบฟังก์ชันการคัดลอกและวางที่มีประสิทธิภาพ
โปรดคำนึงถึงประเด็นต่อไปนี้เพื่อออกแบบฟังก์ชันการคัดลอกและวางที่มีประสิทธิภาพสำหรับแอปพลิเคชัน
- คลิปในคลิปบอร์ดจะมีเพียงรายการเดียวเท่านั้น การดำเนินการคัดลอกใหม่โดยแอปพลิเคชันใดก็ตามในระบบจะเขียนทับคลิปก่อนหน้า เนื่องจากผู้ใช้อาจไปยังส่วนอื่นของแอปพลิเคชันแล้วคัดลอกข้อมูลก่อนที่จะกลับมา คุณจึงไม่สามารถคาดเดาได้ว่าคลิปบอร์ดจะมีคลิปที่ผู้ใช้คัดลอกไว้ก่อนหน้านี้ในแอปพลิเคชันของคุณ
-
วัตถุประสงค์ของการใช้วัตถุ
ClipData.Item
หลายรายการต่อคลิปคือเพื่อรองรับการคัดลอกและวางการเลือกหลายรายการแทนการอ้างอิงรูปแบบต่างๆ ไปยังการเลือกรายการเดียว โดยปกติแล้ว คุณต้องการให้ClipData.Item
วัตถุทั้งหมดในคลิปมีรูปแบบเดียวกัน กล่าวคือ ทั้งหมดต้องเป็นข้อความธรรมดา, Content URI หรือIntent
และต้องไม่ผสมกัน -
เมื่อให้ข้อมูล คุณสามารถนำเสนอ MIME ที่แตกต่างกันได้ เพิ่มประเภท MIME ที่รองรับลงใน
ClipDescription
แล้วใช้ประเภท MIME นั้นในผู้ให้บริการเนื้อหา -
เมื่อคุณได้รับข้อมูลจากคลิปบอร์ด แอปพลิเคชันของคุณมีหน้าที่ตรวจสอบประเภท MIME ที่มี จากนั้นจึงตัดสินใจว่าจะใช้ประเภทใด (หากมี) แม้ว่าจะมีคลิปในคลิปบอร์ดและผู้ใช้ขอให้วาง แต่แอปพลิเคชันของคุณก็ไม่จําเป็นต้องวาง วางข้อความหากประเภท MIME เข้ากันได้ คุณอาจบังคับให้ข้อมูลในคลิปบอร์ดเป็นข้อความโดยใช้
coerceToText()
หากแอปพลิเคชันรองรับประเภท MIME ที่ใช้ได้มากกว่า 1 ประเภท คุณสามารถอนุญาตให้ผู้ใช้เลือกประเภทที่จะใช้