מערכת Android מספקת מסגרת חזקה המבוססת על לוח העריכה להעתקה ולהדבקה. הוא תומך בהתאמה פשוטה וסוגי נתונים מורכבים, כולל מחרוזות טקסט, מבני נתונים מורכבים, טקסט וזרם בינארי ונכסי אפליקציות. נתוני טקסט פשוטים מאוחסנים ישירות בלוח העריכה, ואילו הנתונים מאוחסנים כחומר עזר שאפליקציית ההדבקה מוצאת פתרון אצל ספק התוכן. מתבצעת העתקה וההדבקה פועלת גם בתוך אפליקציה וגם בין אפליקציות שמטמיעות את .
מאחר שחלק מה-framework משתמש בספקי תוכן, המסמך הזה מניח שיש לכם היכרות מסוימת עם ממשק ה-API של Android Content Provider, שמתואר ספקי תוכן.
המשתמשים מצפים לקבל משוב כשהם מעתיקים תוכן ללוח, כך שבנוסף ל-framework שמפעילה העתקה והדבקה, מערכת Android מציגה למשתמשים ממשק משתמש שמוגדר כברירת מחדל כשמעתיקים את Android 13 (רמת API 33) גבוהה יותר. בגלל התכונה הזו, יש סיכון לקבל התראה כפולה. אפשר לקבל מידע נוסף על את רצף הקצה הזה איך נמנעים מכפילויות בתזכורות .
שליחת משוב למשתמשים באופן ידני בזמן העתקה ב-Android מגרסה 12L (API ברמה 32) ומטה. צפייה המלצות בנושא במסמך זה.
מסגרת הלוח
כשמשתמשים במסגרת של לוח העריכה, צריך להוסיף את הנתונים לאובייקט של הקליפ ואז להוסיף את אובייקט הקליפ בלוח שמיועד למערכת כולה. אובייקט הקליפ יכול להופיע באחת משלוש צורות:
- טקסט
- מחרוזת טקסט. מחברים את המחרוזת ישירות לאובייקט הקליפ, שאותו מציבים לוח העריכה. כדי להדביק את המחרוזת, יש לקבל את אובייקט הקליפ מהלוח ולהעתיק את מחרוזת לאחסון של האפליקציה.
- URI
-
אובייקט
Uri
שמייצג כל URI. הוא מתאים בעיקר להעתקה של נתונים מורכבים מספק תוכן. להעתקה נתונים, יש להכניס אובייקטUri
לאובייקט של קליפ ולהציב את אובייקט הקליפ לוח העריכה. כדי להדביק את הנתונים, יש לקבל את אובייקט הקליפ, מקבלים את האובייקטUri
, מפנה אותו למקור נתונים, כגון ספק תוכן, ומעתיקים את הנתונים את המקור באחסון של האפליקציה. - כוונה
-
Intent
. הזה תומכת בהעתקת קיצורי דרך של אפליקציות. כדי להעתיק נתונים, צריך ליצורIntent
, לשים אותו באובייקט של קליפ, ומציבים את אובייקט הקליפ בלוח. כדי להדביק את הנתונים, מזינים את אובייקט הקליפ ולאחר מכן מעתיקים את האובייקטIntent
באזור הזיכרון שלנו.
הלוח יכול להחזיק רק אובייקט קליפ אחד בכל פעם. כשאפליקציה מציבה אובייקט קליפ אובייקט הקליפ הקודם ייעלם.
אם תרצו לאפשר למשתמשים להדביק נתונים באפליקציה, אין צורך לטפל בכל סוגי . אפשר לבחון את הנתונים בלוח לפני שמאפשרים למשתמשים להדביק אותם. אובייקט הקליפ מכיל גם מטא-נתונים שאומרים איזה סוג של MIME זמינים. המטא-נתונים האלה עוזרים לכם להחליט אם האפליקציה יכולה לעשות משהו מועיל לנתונים שבלוח העריכה. לדוגמה, אם יש לך אפליקציה שמטפלת בעיקר בטקסט, עשויה לרצות להתעלם מאובייקטי קליפ שמכילים URI או Intent.
אפשר גם לאפשר למשתמשים להדביק טקסט, ללא קשר לסוג הנתונים בלוח העריכה. שפת תרגום לעשות זאת, לאלץ את נתוני הלוח לייצוג טקסט, ולאחר מכן להדביק את הטקסט. הדבר שמתואר בקטע לאלץ את הלוח לטקסט.
שיעורים של לוח אינטראקטיבי
בקטע הזה מתוארות הכיתות שבהן נעשה שימוש במסגרת של הלוח.
מנהל הלוח
הלוח של מערכת Android מיוצג על ידי הפרמטר הגלובלי
כיתה אחת (ClipboardManager
).
אין ליצור את המחלקה הזו ישירות. במקום זאת, אפשר לקבל הפניה אליו על ידי הפעלה
getSystemService(CLIPBOARD_SERVICE)
ClipData, ClipData.Item ו-ClipDescription
כדי להוסיף נתונים ללוח, צריך ליצור
אובייקט ClipData
שמכיל
תיאור של הנתונים והנתונים עצמם. בלוח העריכה מוצג ClipData
אחד בכל פעם
בזמן האימון. ClipData
מכיל
אובייקט ClipDescription
ושלב אחד או יותר
ClipData.Item
אובייקטים.
אובייקט ClipDescription
מכיל מטא-נתונים לגבי הקליפ. באופן ספציפי,
מכיל מערך של סוגי MIME זמינים לנתוני הקליפ. בנוסף, ב-
Android 12 (רמת API 31) ואילך, המטא-נתונים כוללים מידע לגבי סטטוס האובייקט
מכיל/ה
טקסט מעוצב ועל
סוג הטקסט באובייקט.
כשמציבים קליפ על הלוח, המידע הזה זמין להדבקת אפליקציות,
יכול לבדוק אם הוא יכול לטפל בנתוני הקליפ.
אובייקט ClipData.Item
מכיל את נתוני הטקסט, ה-URI או ה-Intent:
- טקסט
-
CharSequence
. - URI
-
Uri
. הכתובת הזו מכילה בדרך כלל URI של ספק תוכן, אם כי כל URI מותר. האפליקציה שמספקת את הנתונים ממקמת את ה-URI בלוח. הגשת מועמדות שרוצים להדביק את הנתונים, מקבלים את ה-URI מהלוח ומשתמשים בו כדי לגשת לתוכן ספק או מקור נתונים אחר, ולאחזר את הנתונים. - כוונה
-
Intent
. סוג הנתונים הזה מאפשר להעתיק קיצור דרך של אפליקציה לוח העריכה. המשתמשים יכולים להדביק את קיצור הדרך באפליקציות שלהם לשימוש במועד מאוחר יותר.
אפשר להוסיף יותר מאובייקט ClipData.Item
אחד לקליפ. כך המשתמשים יכולים להעתיק וגם
להדביק כמה אפשרויות כקליפ אחד. לדוגמה, אם יש לך ווידג'ט של רשימה שמאפשר
משתמש בוחר יותר מפריט אחד בכל פעם, ניתן להעתיק את כל הפריטים ללוח בבת אחת. לבצע
הזה, ליצור 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 שאינו URI שלcontent:
, ה-method מגדיר את סוג ה-MIMEMIMETYPE_TEXT_URILIST
.אפשר להשתמש ב-
newUri()
כדי ליצור קליפ מ-URI, במיוחד URI שלcontent:
. -
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()
אינו null —coerceToText()
מנסה להשתמש בו כ-URI של תוכן.- אם ה-URI הוא URI של תוכן והספק יכול להחזיר זרם טקסט,
הפונקציה
coerceToText()
מחזירה זרם טקסט. - אם ה-URI הוא URI של תוכן אבל הספק לא מציע זרם טקסט,
coerceToText()
מחזירה ייצוג של ה-URI. הייצוג הוא זהה לערך שהוחזר על ידיUri.toString()
- אם ה-URI אינו URI של תוכן,
coerceToText()
מחזיר ייצוג של ב-URI. הייצוג זהה לייצוג שהוחזר על ידיUri.toString()
- אם ה-URI הוא URI של תוכן והספק יכול להחזיר זרם טקסט,
הפונקציה
- כוונה
- אם
ClipData.Item
הואIntent
– כלומר, אםgetIntent()
שאינו null — מערכתcoerceToText()
ממירה אותו ל-URI של Intent ומחזירה אותו. הייצוג זהה לייצוג שהוחזר על ידיIntent.toUri(URI_INTENT_SCHEME)
מסגרת הלוח מסוכמת בתרשים 2. כדי להעתיק נתונים, אפליקציה מציבה
אובייקט ClipData
בלוח הגלובלי ClipboardManager
.
ClipData
מכיל אובייקט ClipData.Item
אחד או יותר ואובייקט אחד
אובייקט ClipDescription
. כדי להדביק נתונים, אפליקציה מקבלת את ClipData
,
מקבל את סוג ה-MIME שלו מה-ClipDescription
, ומאחזר את הנתונים
ClipData.Item
או מספק התוכן שהפנו אליו
ClipData.Item
.
העתקה ללוח
כדי להעתיק נתונים ללוח, צריך לקבל כינוי לאובייקט ClipboardManager
הגלובלי,
יוצרים אובייקט ClipData
ומוסיפים אובייקט ClipDescription
ועוד אחד
ClipData.Item
אובייקטים. לאחר מכן מוסיפים את האובייקט 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
לאפליקציה ולאחר מכן מוסיף באובייקט הקליפ: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() }
הוספת תוכן רגיש ללוח
אם האפליקציה מאפשרת למשתמשים להעתיק תוכן רגיש ללוח, כמו סיסמאות או קרדיט
פרטי הכרטיס, עליך להוסיף דגל ל-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) } }
הדבקה מהלוח
כפי שתואר קודם, מדביקים נתונים מהלוח על ידי קבלת האובייקט הגלובלי של הלוח, קבלת אובייקט הקליפ, עיון בנתונים שלו ואם אפשר, העתקה של הנתונים מאובייקט הקליפ לאחסון שלכם. בקטע הזה מוסבר בפירוט איך להדביק את שלוש צורות הלוח .
הדבקת טקסט פשוט
כדי להדביק טקסט פשוט, צריך להוריד את הלוח הגלובלי ולוודא שהוא יכול להחזיר טקסט פשוט. ואז מקבלים
של אובייקט הקליפ ולהעתיק אותו לאחסון שלכם באמצעות 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); }
- מעתיקים את הנתונים מהלוח. ניתן להגיע לנקודה הזו בקוד רק אם
'הדבקה' האפשרות בתפריט מופעלת, כך שניתן להניח שהלוח מכיל
טקסט. עדיין לא ידוע לך אם העמודה מכילה מחרוזת טקסט או 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
כדי להדביק Intent, קודם צריך להשיג את הלוח הגלובלי. בדיקת האובייקט ClipData.Item
כדי לבדוק אם הוא מכיל Intent
. לאחר מכן, צריך להתקשר אל getIntent()
כדי להעתיק את
בכוונה טובה, לאחסון שלכם. קטע הקוד הבא מדגים את זה:
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 לפי התאמה לדפוסים ולטפל בו עם קוד ספציפיים להעתקה ולהדבקה.
בדרך כלל משתמשים בשיטת הקידוד אם אתם כבר משתמשים בספק תוכן, מסד נתונים או טבלה פנימית כדי לארגן את הנתונים. במקרים כאלה, יש לכם כמה קטעי נתונים שרוצים להעתיק, וככל הנראה מזהה ייחודי לכל חלק. בתגובה לשאילתה מאת תוכלו לחפש את הנתונים לפי המזהה שלהם ולהחזיר אותם.
אם אין לכם מספר קטעי נתונים, כנראה שאין צורך לקודד מזהה. אפשר להשתמש ב-URI ייחודי לספק שלכם. בתגובה לשאילתה, הספק מחזיר את הנתונים שהיא מכילה כרגע.
העתקת מבני נתונים
להגדיר ספק תוכן להעתקה ולהדבקה של נתונים מורכבים כמחלקה משנה של
ContentProvider
לרכיב הזה. מקודדים את ה-URI שהוספתם ללוח כך שיצביע על הרשומה המדויקת שאתם רוצים.
לספק. בנוסף, חשוב להביא בחשבון את המצב הקיים של האפליקציה:
- אם כבר יש לכם ספק תוכן, תוכלו להוסיף לפונקציונליות שלו. אפשר רק
יהיה צורך לשנות את השיטה
query()
כדי לטפל במזהי URI שמגיעים מאפליקציות שרוצים להדביק נתונים. כדאי לשנות את השיטה כדי לטפל ב"עותק" URI דפוס. - אם לאפליקציה שלך יש מסד נתונים פנימי, כדאי להעביר את מסד הנתונים הזה לספק תוכן כדי להקל על ההעתקה ממנו.
- אם אתם לא משתמשים במסד נתונים, תוכלו להטמיע ספק תוכן פשוט המטרה היחידה היא להציע נתונים לאפליקציות שמדביקות מהלוח.
בספק התוכן, מבטלים לפחות את השיטות הבאות:
-
query()
- הדבקת אפליקציות מניחה שהן יכולות לקבל את הנתונים שלך באמצעות השיטה הזו עם ה-URI שאתה לשים על הלוח. כדי לתמוך בהעתקה, שיטה זו תזהה מזהי URI שמכילים "העתקה" נתיב. לאחר מכן האפליקציה שלך תוכל ליצור "עותק" URI להוספה הלוח, שמכיל את נתיב ההעתקה ומצביע אל הרשומה המדויקת שברצונך להעתיק.
-
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
שפותחת את השידור.
ה-method ContentResolver
מפעילה את ה-method המתאים של ContentProvider
,
ומעבירים את ה-URI של התוכן. הספק מחזיר את מתאר הקובץ אל
אמצעי תשלום אחד (ContentResolver
). לאחר מכן, לאפליקציה ההדבקה יש אחריות לקרוא את
מהזרם.
ברשימה הבאה מפורטות השיטות החשובות ביותר לתיאור קבצים של ספק התוכן. כל אחד
מהם יש שיטת ContentResolver
תואמת עם המחרוזת
'Descriptor' נוסף לשם ה-method. לדוגמה, ContentResolver
אנלוגי של
openAssetFile()
תואם לערך
openAssetFileDescriptor()
.
-
openTypedAssetFile()
-
השיטה הזו מחזירה מתאר של קובץ נכס, אבל רק אם סוג ה-MIME שסופק הוא שנתמך על ידי הספק. מבצע ההדבקה – האפליקציה שמבצעת את ההדבקה – מספקת תבנית מסוג MIME. ספק התוכן של האפליקציה שמעתיקה URI הלוח יחזיר כינוי לקובץ
AssetFileDescriptor
אם הוא יכול לספק זאת סוג MIME וגורם חריג, אם הוא לא מצליח.השיטה הזו מטפלת בקטעי משנה של קבצים. אפשר להשתמש בו כדי לקרוא נכסים ספק התוכן העתיק ללוח העריכה.
-
openAssetFile()
-
השיטה הזו היא צורה כללית יותר של
openTypedAssetFile()
. ללא סינון לסוגי MIME מותרים, אך הוא יכול לקרוא קטעי משנה של קבצים. -
openFile()
-
זהו צורה כללית יותר של
openAssetFile()
. הוא לא יכול לקרוא תתי-סעיפים של .
אפשר גם להשתמש
openPipeHelper()
באמצעות רכיב descriptor של הקובץ. כך האפליקציה ההדבקה יכולה לקרוא את נתוני הזרם
שרשור ברקע באמצעות קו אנכי. כדי להשתמש בשיטה הזו, צריך להטמיע את
ContentProvider.PipeDataWriter
גרפי.
עיצוב פונקציונליות יעילה של העתקה והדבקה
כדי לתכנן פונקציונליות יעילה של העתקה והדבקה עבור האפליקציה, כדאי לזכור את הנקודות הבאות:
- תמיד יש רק קליפ אחד בלוח. פעולת העתקה חדשה על ידי באפליקציה במערכת מחליפה את הקליפ הקודם. מאחר שהמשתמש עשוי לנווט מחוץ לאפליקציה ולהעתיק לפני החזרה, לא תוכלו להניח את הלוח מכיל את הקליפ שהמשתמש העתיק בעבר באפליקציה שלך.
-
המטרה של מספר אובייקטים מסוג
ClipData.Item
בכל קליפ היא לתמוך בהעתקה ובהדבקה של כמה אפשרויות בחירה במקום בצורות שונות הפניה לבחירה אחת. בדרך כלל אתה רוצה את כל הClipData.Item
לאובייקטים בקליפ להיות באותו צורה. כלומר, הם חייבים להיות טקסט פשוט, תוכן URI, אוIntent
, ולא מעורבים. -
כשאתם מספקים נתונים, אתם יכולים להציע ייצוגי MIME שונים. הוספה של סוגי MIME
תומכים ב-
ClipDescription
, ואז להטמיע את סוגי ה-MIME ספק התוכן שלך. -
כשמקבלים נתונים מהלוח, האפליקציה אחראית לבדוק את
סוגי MIME הזמינים ולאחר מכן להחליט באיזה מהם להשתמש, אם בכלל. גם אם יש
מהלוח העריכה והמשתמש מבקש לבצע הדבקה, האפליקציה לא נדרשת
כדי לבצע את ההדבקה. מבצעים את ההדבקה אם סוג ה-MIME תואם. אפשר לאלץ את הנתונים
בלוח כדי לשלוח טקסט באמצעות
coerceToText()
. אם האפליקציה תומכת יש יותר מאחד מסוגי MIME הזמינים, ואפשר לאפשר למשתמש לבחור באיזה מהם להשתמש.