يدعم إطار عمل Android المستند إلى الحافظة لنسخ البيانات ولصقها أنواع البيانات الأساسية والمعقّدة، بما في ذلك:
- السلاسل النصية
- هياكل البيانات المعقّدة
- بيانات النص ومصدر البيانات الثنائي
- مواد عرض التطبيقات
يتم تخزين بيانات النص البسيطة مباشرةً في الحافظة، بينما يتم تخزين البيانات المعقّدة كمرجع تحلّه التطبيقات التي تلصق البيانات باستخدام موفِّر محتوى.
تعمل ميزتا النسخ واللصق داخل التطبيق وبين التطبيقات التي تستخدم إطار العمل.
بما أنّ جزءًا من إطار العمل يستخدم موفّري المحتوى، تفترض هذه المستندات الإلمام ببعض المفاهيم حول Android Content Provider API.
التعامل مع النص
تتيح بعض المكوّنات نسخ النص ولصقه بدون الحاجة إلى إجراء أي إعدادات، كما هو موضّح في الجدول التالي.
| المكوّن | نسخ النص | لصق النص |
|---|---|---|
| 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 الذي تم تمريره إلى الحافظة.
ينسخ الرمز البرمجي التالي "Hello, clipboard" إلى الحافظة عندما ينقر المستخدم على الزر.
// 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() method is a convenience method to
create a ClipData object from a String object.
يمكنك ضبط كائن 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 واجهة مستخدم تلقائية للمستخدمين عند النسخ في 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) } }