يتيح إطار عمل الحافظة في Android لعمليات النسخ واللصق أنواع البيانات البسيطة والمعقدة، بما في ذلك:
- سلاسل نصية
- بُنى البيانات المعقّدة
- بيانات النص ومصدر البيانات الثنائي
- مواد عرض التطبيق
يتم تخزين بيانات النصوص البسيطة مباشرةً في الحافظة، بينما يتم تخزين البيانات المعقّدة كمرجع يحلّه تطبيق اللصق باستخدام أداة توفير المحتوى.
يعمل النسخ واللصق داخل التطبيق وبين التطبيقات التي تنفذ إطار العمل.
بما أنّ جزءًا من إطار العمل يستخدم أدوات توفير المحتوى، يفترض هذا المستند أنّك على دراية ببعض جوانب واجهة برمجة التطبيقات لأداة توفير المحتوى في Android.
التعامل مع النص
تتيح بعض المكوّنات نسخ النص ولصقه تلقائيًا، كما هو موضّح في الجدول التالي.
| المكوّن | نسخ النص | لصق النص |
|---|---|---|
| BasicTextField | ✅ | ✅ |
| TextField | ✅ | ✅ |
| SelectionContainer | ✅ |
على سبيل المثال، يمكنك نسخ النص في البطاقة إلى الحافظة
في المقتطف التالي ولصق النص المنسوخ في TextField.
يمكنك عرض القائمة للصق النص من خلال
النقر مع الاستمرار على TextField أو النقر على مقبض المؤشر.
val textFieldState = rememberTextFieldState() Column { Card { SelectionContainer { Text("You can copy this text") } } BasicTextField(state = textFieldState) }
يمكنك لصق النص باستخدام اختصار لوحة المفاتيح التالي: Ctrl+V . يتوفّر أيضًا اختصار لوحة المفاتيح تلقائيًا. يُرجى الرجوع إلى مقالة التعامل مع إجراءات لوحة المفاتيح للحصول على التفاصيل.
النسخ باستخدام ClipboardManager
يمكنك نسخ النصوص إلى الحافظة باستخدام ClipboardManager.
وتنسخ طريقة setText()
كائن AnnotatedString الذي تم تمريره إلى الحافظة.
ينسخ المقتطف التالي "مرحبًا، الحافظة"
إلى الحافظة عندما ينقر المستخدم على الزر.
// Retrieve a ClipboardManager object val clipboardManager = LocalClipboardManager.current Button( onClick = { // Copy "Hello, clipboard" to the clipboard clipboardManager.setText(AnnotatedString("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.
val 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(annotatedString.text) } } } ) { 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 = remember { mutableStateListOf<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 واجهة مستخدم تلقائية للمستخدمين عند النسخ في الإصدار 13 من نظام التشغيل Android (المستوى 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) } }