يتيح إطار العمل المستنِد إلى حافظة Android للنسخ واللصق أنواع البيانات الأساسية والمعقدة، بما في ذلك:
- سلاسل نصية
- هياكل البيانات المعقدة
- بيانات النص والبث الثنائي
- مواد عرض التطبيقات
يتم تخزين بيانات النصوص البسيطة مباشرةً في الحافظة، في حين يتم تخزين البيانات المعقدة كمرجع يحلّه تطبيق اللصق مع مقدّم المحتوى.
يعمل النسخ واللصق داخل التطبيق وبين التطبيقات التي تنفِّذ إطار العمل.
بما أنّ جزءًا من إطار العمل يستخدم موفّري المحتوى،تفترض هذه المقالة بعض الإلمام بواجهة برمجة تطبيقات Android Content Provider API.
استخدام النصوص
تتيح بعض المكونات نسخ النص ولصقه خارج العلبة، كما هو موضح في الجدول التالي.
المكوّن | نسخ النص | لصق نص |
---|---|---|
حقل النص الأساسي | ✅ | ✅ |
TextField | ✅ | ✅ |
SelectionContainer | ✅ |
على سبيل المثال، يمكنك نسخ النص في البطاقة إلى الحافظة
في المقتطف التالي ولصق النص المنسوخ في TextField
.
يمكنك عرض القائمة للصق النص من خلال
النقر مع الاستمرار على TextField
أو النقر على مقبض المؤشر.
val textFieldState = rememberTextFieldState()
Column {
Card {
SelectionContainer {
Text("You can copy this text")
}
}
BasicTextField(state = textFieldState)
}
يمكنك لصق النص باستخدام اختصار لوحة المفاتيح التالي: Ctrl+V . يتوفّر اختصار لوحة المفاتيح أيضًا تلقائيًا. لمعرفة التفاصيل، يُرجى الاطّلاع على مقالة التعامل مع إجراءات لوحة المفاتيح.
النسخ باستخدام "ClipboardManager
"
يمكنك نسخ النصوص إلى الحافظة باستخدام ClipboardManager
.
تنسخ طريقة setText()
عنصر String الذي تم تمريره إلى الحافظة.
ينسخ المقتطف التالي العبارة "مرحبًا، الحافظة"
إلى الحافظة عندما ينقر المستخدم على الزر.
// 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
من كائن سلسلة.
يمكنك ضبط عنصر 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()
على 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
في المكوّن المعدَّل.
يضيف المقتطف التالي من التعليمات البرمجية عنوان URL المُلصق لبيانات الصورة
إلى قائمة بعناصر 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 واجهة مستخدم تلقائية للمستخدمين عند نسخ البيانات في Android 13 (مستوى واجهة برمجة التطبيقات 33). وأعلى. بسبب هذه الميزة، هناك خطر ظهور إشعارات مكرّرة. يمكنك التعرّف على المزيد من المعلومات عن هذه الحالة الهامشية في مقالة تجنُّب الإشعارات المكرّرة.
تقديم ملاحظات للمستخدمين يدويًا عند نسخ المحتوى في Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأقدم اطّلِع على الاقتراح.
المحتوى الحسّاس
إذا اخترت السماح للتطبيق بنسخ المحتوى الحسّاس إلى الحافظة، مثل كلمات المرور، يجب أن يخبر التطبيق النظام حتى يتجنّب النظام عرض المحتوى الحسّاس المنسوخ في واجهة المستخدم (الشكل 2).
يجب إضافة علامة إلى 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)
}
}