เฟรมเวิร์กที่ใช้คลิปบอร์ดของ Android สำหรับการคัดลอกและวาง รองรับข้อมูลประเภทพื้นฐานและซับซ้อน ได้แก่
- สตริงข้อความ
- โครงสร้างข้อมูลที่ซับซ้อน
- ข้อมูลสตรีมข้อความและไบนารี
- ชิ้นงานแอปพลิเคชัน
ระบบจะจัดเก็บข้อมูลข้อความธรรมดาไว้ในคลิปบอร์ดโดยตรง ส่วนข้อมูลที่ซับซ้อนจะจัดเก็บเป็นข้อมูลอ้างอิงซึ่งแอปพลิเคชันการวางจะแก้ไขกับผู้ให้บริการเนื้อหา
การคัดลอกและวางใช้ได้ทั้งในแอปพลิเคชันและระหว่างแอปพลิเคชันที่ใช้เฟรมเวิร์ก
เนื่องจากเฟรมเวิร์กบางส่วนใช้ผู้ให้บริการเนื้อหา เอกสารนี้จึงถือว่าคุณคุ้นเคยกับ Android Content Provider API อยู่บ้าง
การทำงานกับข้อความ
คอมโพเนนต์บางรายการรองรับการคัดลอกและวางข้อความได้ทันทีตามที่แสดงในตารางต่อไปนี้
ส่วนประกอบ | กำลังคัดลอกข้อความ | กำลังวางข้อความ |
---|---|---|
BasicTextField | ✅ | ✅ |
ฟิลด์ข้อความ | ✅ | ✅ |
คอนเทนเนอร์ตัวเลือก | ✅ |
เช่น คัดลอกข้อความในการ์ดไปยังคลิปบอร์ด
ในข้อมูลโค้ดต่อไปนี้ และวางข้อความที่คัดลอกลงใน TextField
คุณแสดงเมนูเพื่อวางข้อความได้โดยแตะ TextField
ค้างไว้ หรือแตะแถบเคอร์เซอร์
val textFieldState = rememberTextFieldState()
Column {
Card {
SelectionContainer {
Text("You can copy this text")
}
}
BasicTextField(state = textFieldState)
}
คุณสามารถวางข้อความด้วยแป้นพิมพ์ลัด Ctrl+V แป้นพิมพ์ลัดจะพร้อมใช้งานโดยค่าเริ่มต้นเช่นกัน โปรดดูรายละเอียดที่หัวข้อจัดการการดำเนินการของแป้นพิมพ์
คัดลอกด้วย ClipboardManager
คุณคัดลอกข้อความไปยังคลิปบอร์ดได้โดยใช้ ClipboardManager
คัดลอกเมธอด setText()
ออบเจ็กต์สตริงที่ส่งผ่านไปยังคลิปบอร์ด
ข้อมูลโค้ดต่อไปนี้คัดลอก "สวัสดีคลิปบอร์ด"
ไปยังคลิปบอร์ดเมื่อผู้ใช้คลิกปุ่ม
// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current
Button(
onClick = {
// Copy "Hello, clipboard" to the clipboard
clipboardManager.setText("Hello, clipboard")
}
) {
Text("Click to copy a text")
}
ข้อมูลโค้ดต่อไปนี้ทําสิ่งเดียวกัน แต่ให้คุณควบคุมได้ละเอียดยิ่งขึ้น
กรณีการใช้งานทั่วไปคือการคัดลอกเนื้อหาที่ละเอียดอ่อน
เช่น รหัสผ่าน ClipEntry
อธิบายรายการในคลิปบอร์ด
โดยมีออบเจ็กต์ ClipData
ที่อธิบายข้อมูลในคลิปบอร์ด
เมธอด ClipData.newPlainText()
เป็นวิธีการอำนวยความสะดวกในการ
สร้างออบเจ็กต์ ClipData
จากออบเจ็กต์ String
คุณตั้งค่าออบเจ็กต์ ClipEntry
ที่สร้างขึ้นไปยังคลิปบอร์ดได้
โดยการเรียกใช้เมธอด setClip()
เหนือออบเจ็กต์ ClipboardManager
// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current
Button(
onClick = {
val clipData = ClipData.newPlainText("plain text", "Hello, clipboard")
val clipEntry = ClipEntry(clipData)
clipboardManager.setClip(clipEntry)
}
) {
Text("Click to copy a text")
}
วางด้วย ClipboardManager
คุณเข้าถึงข้อความที่คัดลอกไปยังคลิปบอร์ดได้
โดยการเรียกใช้ getText()
Method
ในช่วง ClipboardManager
เมธอด getText()
จะแสดงผลออบเจ็กต์ AnnotatedString
เมื่อคัดลอกข้อความในคลิปบอร์ด
ข้อมูลโค้ดต่อไปนี้จะเพิ่มข้อความในคลิปบอร์ดต่อท้ายข้อความใน TextField
var textFieldState = rememberTextFieldState()
Column {
TextField(state = textFieldState)
Button(
onClick = {
// The getText method returns an AnnotatedString object or null
val annotatedString = clipboardManager.getText()
if(annotatedString != null) {
// The pasted text is placed on the tail of the TextField
textFieldState.edit {
append(text.toString())
}
}
}
) {
Text("Click to paste the text in the clipboard")
}
}
ทำงานกับเนื้อหาอย่างละเอียด
ผู้ใช้ชื่นชอบรูปภาพ วิดีโอ และคอนเทนต์อื่นๆ ที่สื่ออารมณ์ได้
แอปของคุณช่วยให้ผู้ใช้คัดลอกเนื้อหาแบบริชมีเดียได้โดยใช้ ClipboardManager
และ ClipEntry
ตัวแก้ไข contentReceiver
จะช่วยให้คุณใช้การวางข้อมูลอย่างละเอียดได้
คัดลอกข้อมูลอย่างละเอียด
แอปของคุณคัดลอกเนื้อหาแบบริชมีเดียไปยังคลิปบอร์ดโดยตรงไม่ได้
แต่แอปจะส่งออบเจ็กต์ URI
ไปยังคลิปบอร์ดและให้สิทธิ์เข้าถึงเนื้อหาด้วย ContentProvider
ข้อมูลโค้ดต่อไปนี้แสดงวิธีคัดลอกรูปภาพ JPEG ไปยังคลิปบอร์ด
โปรดดูรายละเอียดที่หัวข้อคัดลอกสตรีมข้อมูล
// Get a reference to the context
val context = LocalContext.current
Button(
onClick = {
// URI of the copied JPEG data
val uri = Uri.parse("content://your.app.authority/0.jpg")
// Create a ClipData object from the URI value
// A ContentResolver finds a proper ContentProvider so that ClipData.newUri can set appropriate MIME type to the given URI
val clipData = ClipData.newUri(context.contentResolver, "Copied", uri)
// Create a ClipEntry object from the clipData value
val clipEntry = ClipEntry(clipData)
// Copy the JPEG data to the clipboard
clipboardManager.setClip(clipEntry)
}
) {
Text("Copy a JPEG data")
}
วางเนื้อหาอย่างละเอียด
ตัวแก้ไข contentReceiver
จะช่วยให้คุณจัดการกับการวางข้อมูลอย่างละเอียดได้
เป็น BasicTextField
ในคอมโพเนนต์ที่แก้ไขแล้ว
ข้อมูลโค้ดต่อไปนี้จะเพิ่ม URI ที่วางของข้อมูลรูปภาพ
ไปยังรายการออบเจ็กต์ Uri
// A URI list of images
val imageList by remember{ mutableListOf<Uri>() }
// Remember the ReceiveContentListener object as it is created inside a Composable scope
val receiveContentListener = remember {
ReceiveContentListener { transferableContent ->
// Handle the pasted data if it is image data
when {
// Check if the pasted data is an image or not
transferableContent.hasMediaType(MediaType.Image)) -> {
// Handle for each ClipData.Item object
// The consume() method returns a new TransferableContent object containging ignored ClipData.Item objects
transferableContent.consume { item ->
val uri = item.uri
if (uri != null) {
imageList.add(uri)
}
// Mark the ClipData.Item object consumed when the retrieved URI is not null
uri != null
}
}
// Return the given transferableContent when the pasted data is not an image
else -> transferableContent
}
}
}
val textFieldState = rememberTextFieldState()
BasicTextField(
state = textFieldState,
modifier = Modifier
.contentReceiver(receiveContentListener)
.fillMaxWidth()
.height(48.dp)
)
ตัวแก้ไข contentReceiver
จะรับออบเจ็กต์ ReceiveContentListener
เป็นอาร์กิวเมนต์และเรียกใช้เมธอด onReceive
ของออบเจ็กต์ที่ส่งเมื่อผู้ใช้วางข้อมูลลงใน BasicTextField
ภายในคอมโพเนนต์ที่แก้ไข
ระบบจะส่งออบเจ็กต์ TransferableContent
ไปยังเมธอด onReceive ซึ่งจะอธิบายข้อมูลที่โอนระหว่างแอปได้ในกรณีนี้
คุณสามารถเข้าถึงออบเจ็กต์ ClipEntry
ได้โดยอ้างอิงแอตทริบิวต์ clipEntry
ออบเจ็กต์ ClipEntry
มีออบเจ็กต์ ClipData.Item
ได้หลายรายการ
เมื่อผู้ใช้เลือกรูปภาพหลายรูปและคัดลอกไปยังคลิปบอร์ด
เป็นต้น
คุณควรทำเครื่องหมาย "ใช้แล้ว" หรือ "ละเว้น" สำหรับออบเจ็กต์ ClipData.Item
แต่ละรายการ
และแสดง TransferableContent
ที่มี
ออบเจ็กต์ ClipData.Item
ที่ละเว้น
เพื่อให้ตัวแก้ไขระดับบน contentReceiver
ที่ใกล้เคียงที่สุดสามารถรับได้
โดยเมธอด TransferableContent.hasMediaType()
จะช่วยคุณระบุ
ออบเจ็กต์ TransferableContent
สามารถให้รายการได้หรือไม่
ด้วยประเภทสื่อ
ตัวอย่างเช่น การเรียกเมธอดต่อไปนี้จะแสดง true
หากออบเจ็กต์ TransferableContent
มีรูปภาพได้
transferableContent.hasMediaType(MediaType.Image)
ทำงานกับข้อมูลที่ซับซ้อน
คุณสามารถคัดลอกข้อมูลที่ซับซ้อนไปยังคลิปบอร์ด ในลักษณะเดียวกับที่คุณดำเนินการกับเนื้อหาอย่างละเอียด โปรดดูรายละเอียดที่หัวข้อใช้ผู้ให้บริการเนื้อหาเพื่อคัดลอกข้อมูลที่ซับซ้อน
นอกจากนี้ คุณยังจัดการการวางข้อมูลที่ซับซ้อนในลักษณะเดียวกันกับเนื้อหาริชมีเดียได้ด้วย
คุณสามารถรับ URI ของข้อมูลที่วาง
คุณดึงข้อมูลจริงได้จากContentProvider
โปรดดูข้อมูลเพิ่มเติมที่หัวข้อดึงข้อมูลจากผู้ให้บริการ
ความคิดเห็นเกี่ยวกับการคัดลอกเนื้อหา
ผู้ใช้คาดหวังที่จะได้รับความคิดเห็นเมื่อคัดลอกเนื้อหาไปยังคลิปบอร์ด ดังนั้นนอกเหนือจากเฟรมเวิร์กที่ช่วยในการคัดลอกและวางแล้ว Android จะแสดง UI เริ่มต้นต่อผู้ใช้เมื่อคัดลอกใน Android 13 (API ระดับ 33) ขึ้นไป ฟีเจอร์นี้จึงมีความเสี่ยงที่จะเกิดการแจ้งเตือนซ้ำ ดูข้อมูลเพิ่มเติมเกี่ยวกับกรณีขอบเขตนี้ได้ที่หลีกเลี่ยงการแจ้งเตือนที่ซ้ำกัน
แสดงความคิดเห็นให้กับผู้ใช้ด้วยตนเอง เมื่อคัดลอกใน Android 12L (API ระดับ 32) และต่ำกว่า ดูคำแนะนำ
เนื้อหาที่ละเอียดอ่อน
หากคุณเลือกที่จะให้แอปอนุญาตให้ผู้ใช้คัดลอกเนื้อหาที่ละเอียดอ่อนไปยังคลิปบอร์ด เช่น รหัสผ่าน แอปของคุณต้องบอกให้ระบบทราบ เพื่อให้ระบบหลีกเลี่ยงการแสดงเนื้อหาที่ละเอียดอ่อนที่คัดลอกมา ใน UI (รูปที่ 2)
คุณต้องเพิ่ม Flag ไปยัง ClipDescription
ใน ClipData
ก่อนเรียกใช้เมธอด setClip()
บนออบเจ็กต์ ClipboardManager
// 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)
}
}