اندروید یک چارچوب قدرتمند مبتنی بر کلیپ بورد برای کپی و چسباندن ارائه می دهد. از انواع داده های ساده و پیچیده، از جمله رشته های متنی، ساختارهای داده پیچیده، متن و داده های جریان باینری و دارایی های برنامه پشتیبانی می کند. دادههای متنی ساده مستقیماً در کلیپ بورد ذخیره میشوند، در حالی که دادههای پیچیده به عنوان مرجع ذخیره میشوند که برنامه چسباندن آن را با ارائهدهنده محتوا حل میکند. کپی و چسباندن هم در یک برنامه کاربردی و هم بین برنامه هایی که فریم ورک را پیاده سازی می کنند کار می کند.
از آنجایی که بخشی از چارچوب از ارائهدهندگان محتوا استفاده میکند، این سند تا حدی آشنایی با API ارائهدهنده محتوای Android را فرض میکند که در ارائهدهندگان محتوا توضیح داده شده است.
کاربران هنگام کپی کردن محتوا در کلیپ بورد انتظار بازخورد دارند، بنابراین علاوه بر چارچوبی که امکان کپی و چسباندن را فراهم می کند، اندروید هنگام کپی کردن در اندروید 13 (سطح API 33) و بالاتر، یک رابط کاربری پیش فرض را به کاربران نشان می دهد. با توجه به این ویژگی، خطر اعلان تکراری وجود دارد. می توانید در بخش Avoid duplicate notifications درباره این edge case اطلاعات بیشتری کسب کنید.
هنگام کپی کردن در Android 12L (سطح API 32) و پایینتر، بازخورد را به صورت دستی به کاربران ارائه دهید. توصیه هایی را برای این موضوع در این سند ببینید.
چارچوب کلیپ بورد
هنگامی که از چارچوب کلیپ بورد استفاده می کنید، داده ها را در یک شی کلیپ قرار دهید و سپس شی کلیپ را در کلیپ بورد کل سیستم قرار دهید. شی clip می تواند یکی از سه شکل زیر را داشته باشد:
- متن
- یک رشته متن رشته را مستقیماً در شی clip قرار دهید، سپس آن را روی کلیپ بورد قرار دهید. برای چسباندن رشته، شی clip را از کلیپ بورد دریافت کنید و رشته را در حافظه برنامه خود کپی کنید.
- URI
- یک شی
Uri
که هر شکلی از URI را نشان می دهد. این در درجه اول برای کپی کردن داده های پیچیده از یک ارائه دهنده محتوا است. برای کپی داده ها، یک شیUri
را در یک شی کلیپ قرار دهید و شی کلیپ را در کلیپ بورد قرار دهید. برای جایگذاری دادهها، شی clip را دریافت کنید، شیUri
را دریافت کنید، آن را به یک منبع داده، مانند ارائهدهنده محتوا، حل کنید، و دادهها را از منبع در حافظه برنامهتان کپی کنید. - قصد
- یک
Intent
این از کپی کردن میانبرهای برنامه پشتیبانی می کند. برای کپی کردن دادهها، یکIntent
ایجاد کنید، آن را در یک شی کلیپ قرار دهید و شی کلیپ را در کلیپ بورد قرار دهید. برای چسباندن داده ها، شی clip را دریافت کنید و سپس شیIntent
را در ناحیه حافظه برنامه خود کپی کنید.
کلیپ بورد هر بار فقط یک شی کلیپ را نگه می دارد. هنگامی که یک برنامه یک شی کلیپ را در کلیپ بورد قرار می دهد، شی کلیپ قبلی ناپدید می شود.
اگر میخواهید به کاربران اجازه دهید دادهها را در برنامهتان جایگذاری کنند، لازم نیست همه انواع دادهها را مدیریت کنید. میتوانید قبل از اینکه به کاربران امکان چسباندن آنها را بدهید، دادهها را در کلیپ بورد بررسی کنید. علاوه بر داشتن یک فرم داده خاص، شی clip همچنین حاوی ابرداده است که به شما می گوید چه نوع MIME در دسترس است. این ابرداده به شما کمک می کند تصمیم بگیرید که آیا برنامه شما می تواند کار مفیدی با داده های کلیپ بورد انجام دهد یا خیر. برای مثال، اگر برنامهای دارید که عمدتاً متن را مدیریت میکند، ممکن است بخواهید اشیایی را که حاوی URI یا intent هستند نادیده بگیرید.
همچنین ممکن است بخواهید به کاربران اجازه دهید بدون توجه به شکل داده ها در کلیپ بورد، متن را جایگذاری کنند. برای انجام این کار، داده های کلیپ بورد را در یک نمایش متنی مجبور کنید و سپس این متن را جایگذاری کنید. این در بخش Coerce the clipboard to text توضیح داده شده است.
کلاس های کلیپ بورد
این بخش کلاس های استفاده شده توسط چارچوب کلیپ بورد را توضیح می دهد.
Clipboard Manager
کلیپ بورد سیستم اندروید توسط کلاس جهانی ClipboardManager
نمایش داده می شود. این کلاس را مستقیماً نمونه سازی نکنید. در عوض، با فراخوانی getSystemService(CLIPBOARD_SERVICE)
به آن ارجاع دهید.
ClipData، ClipData.Item، و ClipDescription
برای افزودن داده به کلیپ بورد، یک شی ClipData
ایجاد کنید که حاوی توضیحاتی از داده ها و خود داده ها باشد. کلیپ بورد هر بار یک ClipData
نگه می دارد. یک ClipData
شامل یک شی ClipDescription
و یک یا چند شی ClipData.Item
است.
یک شی ClipDescription
حاوی ابرداده در مورد کلیپ است. به طور خاص، شامل آرایه ای از انواع MIME موجود برای داده های کلیپ است. بهعلاوه، در Android 12 (سطح API 31) و بالاتر، فراداده شامل اطلاعاتی درباره اینکه آیا شیء دارای متن سبکشده است یا خیر و در مورد نوع نوشتار موجود در شی است . هنگامی که یک کلیپ را در کلیپ بورد قرار می دهید، این اطلاعات برای چسباندن برنامه ها در دسترس است، که می تواند بررسی کند که آیا آنها می توانند داده های کلیپ را مدیریت کنند یا خیر.
یک شی ClipData.Item
حاوی متن، URI یا دادههای هدف است:
- متن
- یک
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 کهcontent:
URI، این روش نوع MIME را رویMIMETYPE_TEXT_URILIST
تنظیم می کند.از
newUri()
برای ایجاد یک کلیپ از یک URI – به ویژه یکcontent:
URI استفاده کنید. -
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()
تهی نباشد-coerceToText() متن را برمی گرداند. - URI
- اگر
ClipData.Item
یک URI باشد—یعنی اگرgetUri()
تهی نباشد—coerceToText()
سعی می کند از آن به عنوان URI محتوا استفاده کند.- اگر URI یک URI محتوا باشد و ارائهدهنده بتواند یک جریان متنی را برگرداند،
coerceToText()
یک جریان متنی را برمیگرداند. - اگر URI یک URI محتوا باشد اما ارائهدهنده جریان متنی را ارائه ندهد،
coerceToText()
نمایشی از URI را برمیگرداند. نمایش همان است که توسطUri.toString()
برگردانده شده است. - اگر URI یک URI محتوا نیست،
coerceToText()
نمایشی از URI برمی گرداند. نمایش همان است که توسطUri.toString()
برگردانده شده است.
- اگر URI یک URI محتوا باشد و ارائهدهنده بتواند یک جریان متنی را برگرداند،
- قصد
- اگر
ClipData.Item
یکIntent
باشد - یعنی اگرgetIntent()
تهی نباشد-coerceToText()
آن را به یک Intent URI تبدیل کرده و برمی گرداند. نمایش همان است که توسط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 محتوا کپی میکنید، یک ارائهدهنده محتوا راهاندازی کنید.
- کلیپ بورد سیستم را دریافت کنید:
کاتلین
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 } }
جاوا
... // 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
جدید کپی کنید:- برای متن
کاتلین
// Creates a new text clip to put on the clipboard. val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
جاوا
// Creates a new text clip to put on the clipboard. ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
- برای یک URI
این قطعه یک URI را با رمزگذاری یک شناسه رکورد بر روی URI محتوا برای ارائهدهنده میسازد. این تکنیک با جزئیات بیشتری در بخش رمزگذاری یک شناسه در بخش URI پوشش داده شده است.
کاتلین
// 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)
جاوا
// 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
برای یک برنامه می سازد و سپس آن را در شی clip قرار می دهد:کاتلین
// 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)
جاوا
// 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);
- برای متن
- شی کلیپ جدید را در کلیپ بورد قرار دهید:
کاتلین
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
جاوا
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
هنگام کپی کردن در کلیپ بورد بازخورد ارائه دهید
وقتی یک برنامه محتوا را در کلیپ بورد کپی می کند، کاربران انتظار بازخورد بصری دارند. این به طور خودکار برای کاربران اندروید 13 و بالاتر انجام می شود، اما باید به صورت دستی در نسخه های قبلی پیاده سازی شود.
از اندروید 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() }
محتوای حساس را به کلیپ بورد اضافه کنید
اگر برنامه شما به کاربران اجازه میدهد محتوای حساس را در کلیپ بورد کپی کنند، مانند گذرواژهها یا اطلاعات کارت اعتباری، باید قبل از فراخوانی ClipboardManager.setPrimaryClip()
یک پرچم به ClipDescription
در ClipData
اضافه کنید. افزودن این پرچم از نمایش محتوای حساس در تأیید تصویری محتوای کپی شده در اندروید 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) } }
چسباندن از کلیپ بورد
همانطور که قبلا توضیح داده شد، با دریافت شی کلیپ بورد کلی، دریافت شی کلیپ، مشاهده داده های آن، و در صورت امکان کپی داده ها از شی کلیپ در فضای ذخیره سازی خود، داده ها را از کلیپ بورد جای گذاری کنید. این بخش به طور مفصل نحوه چسباندن سه شکل داده های کلیپ بورد را توضیح می دهد.
چسباندن متن ساده
برای جایگذاری متن ساده، کلیپبورد جهانی را دریافت کنید و بررسی کنید که میتواند متن ساده را برگرداند. سپس شی clip را دریافت کنید و متن آن را با استفاده از getText()
در فضای ذخیره سازی خود کپی کنید، همانطور که در روش زیر توضیح داده شده است:
- با استفاده از
getSystemService(CLIPBOARD_SERVICE)
شیClipboardManager
کلی را دریافت کنید. همچنین، یک متغیر سراسری برای حاوی متن چسبانده شده اعلام کنید:کاتلین
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
جاوا
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- تعیین کنید که آیا باید گزینه "Paste" را در فعالیت فعلی فعال یا غیرفعال کنید. بررسی کنید که کلیپ بورد حاوی یک کلیپ است و می توانید نوع داده های نمایش داده شده توسط کلیپ را مدیریت کنید:
کاتلین
// 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 } }
جاوا
// 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); }
- داده ها را از کلیپ بورد کپی کنید. این نقطه در کد فقط در صورتی قابل دسترسی است که آیتم منوی "paste" فعال باشد، بنابراین می توانید فرض کنید که کلیپ بورد حاوی متن ساده است. شما هنوز نمی دانید که آیا شامل یک رشته متن است یا یک URI که به متن ساده اشاره می کند. قطعه کد زیر این را آزمایش می کند، اما فقط کد مربوط به مدیریت متن ساده را نشان می دهد:
کاتلین
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 } } } }
جاوا
// 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 اعلام کنید:
کاتلین
// 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"
جاوا
// 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";
- کلیپ بورد جهانی را دریافت کنید. همچنین یک حلکننده محتوا دریافت کنید تا بتوانید به ارائهدهنده محتوا دسترسی داشته باشید:
کاتلین
// Gets a handle to the Clipboard Manager. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // Gets a content resolver instance. val cr = contentResolver
جاوا
// Gets a handle to the Clipboard Manager. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Gets a content resolver instance. ContentResolver cr = getContentResolver();
- کلیپ اصلی را از کلیپ بورد دریافت کنید و محتوای آن را به عنوان URI دریافت کنید:
کاتلین
// 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
جاوا
// 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();
- با فراخوانی
getType(Uri)
تست کنید که آیا URI یک URI محتوا است. اگرUri
به ارائهدهنده محتوای معتبر اشاره نکند، این روش null را برمیگرداند.کاتلین
// If the clipboard contains a URI reference... pasteUri?.let { // ...is this a content URI? val uriMimeType: String? = cr.getType(it)
جاوا
// If the clipboard contains a URI reference... if (pasteUri != null) { // ...is this a content URI? String uriMimeType = cr.getType(pasteUri);
- تست کنید که آیا ارائه دهنده محتوا از یک نوع MIME که برنامه آن را می فهمد پشتیبانی می کند یا خیر. در صورت وجود،
ContentResolver.query()
را برای دریافت داده ها فراخوانی کنید. مقدار بازگشتی یکCursor
است.کاتلین
// 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. } } } }
جاوا
// 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()
فراخوانی کنید تا intent را در فضای ذخیره سازی خود کپی کنید. قطعه زیر این را نشان می دهد:
کاتلین
// 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. }
جاوا
// 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
دسترسی پیدا می کند. نان تست فقط زمانی ظاهر می شود که برنامه شما برای اولین بار به داده های آن برنامه دسترسی پیدا کند. - فراداده را برای شی clip بازیابی می کند، مانند با فراخوانی
getPrimaryClipDescription()
به جایgetPrimaryClip()
.
از ارائه دهندگان محتوا برای کپی کردن داده های پیچیده استفاده کنید
ارائه دهندگان محتوا از کپی کردن داده های پیچیده مانند سوابق پایگاه داده یا جریان فایل پشتیبانی می کنند. برای کپی کردن داده ها، یک URI محتوا را در کلیپ بورد قرار دهید. چسباندن برنامه ها سپس این URI را از کلیپ بورد دریافت کرده و از آن برای بازیابی داده های پایگاه داده یا توصیفگرهای جریان فایل استفاده می کند.
از آنجایی که برنامه چسباندن فقط URI محتوا برای داده های شما دارد، باید بداند کدام قطعه از داده را بازیابی کند. شما می توانید این اطلاعات را با رمزگذاری یک شناسه برای داده ها در خود URI ارائه دهید، یا می توانید یک URI منحصر به فرد ارائه دهید که داده هایی را که می خواهید کپی کنید برمی گرداند. اینکه کدام تکنیک را انتخاب می کنید به سازماندهی داده های شما بستگی دارد.
بخشهای زیر نحوه راهاندازی URI، ارائه دادههای پیچیده و ارائه جریانهای فایل را توضیح میدهند. در توضیحات فرض می شود که شما با اصول کلی طراحی ارائه دهنده محتوا آشنا هستید.
یک شناسه در URI رمزگذاری کنید
یک تکنیک مفید برای کپی کردن داده ها در کلیپ بورد با یک URI، کدگذاری یک شناسه برای داده ها در خود URI است. سپس ارائهدهنده محتوای شما میتواند شناسه را از URI دریافت کرده و از آن برای بازیابی دادهها استفاده کند. برنامه چسباندن نباید بداند که شناسه وجود دارد. فقط باید "مرجع" شما - URI به اضافه شناسه - را از کلیپ بورد دریافت کند، ارائه دهنده محتوای شما را به آن بدهد و داده ها را پس بگیرد.
شما معمولاً با الحاق آن به انتهای URI، یک شناسه را روی یک URI محتوا رمزگذاری می کنید. به عنوان مثال، فرض کنید URI ارائه دهنده خود را به صورت رشته زیر تعریف کنید:
"content://com.example.contacts"
اگر می خواهید نامی را در این URI رمزگذاری کنید، از قطعه کد زیر استفاده کنید:
کاتلین
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)
جاوا
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 برای داده های کپی شده اعلام کنید.
کاتلین
// 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"
جاوا
// 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 را در کلیپ بورد قرار دهید.
کاتلین
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) } }
جاوا
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 هایی که در کلیپ بورد قرار داده اید مطابقت داشته باشد.
کاتلین
// 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() { ... }
جاوا
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 مختلفی را کنترل کند، بسته به اینکه چگونه آن را کدنویسی می کنید، اما فقط الگوی عملیات کپی کردن کلیپ بورد نشان می دهد.کاتلین
// 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. } } ... }
جاوا
// 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 مناسب برای داده های کپی شده تنظیم کنید:کاتلین
// Sets up your provider's getType() method. override fun getType(uri: Uri): String? { ... return when(sUriMatcher.match(uri)) { GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT ... } }
جاوا
// Sets up your provider's getType() method. public String getType(Uri uri) { ... switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: return (MIME_TYPE_CONTACT); ... } }
بخش Paste data from a content URI نحوه دریافت URI محتوا از کلیپ بورد و استفاده از آن برای دریافت و چسباندن داده ها را توضیح می دهد.
جریان های داده را کپی کنید
می توانید مقادیر زیادی از متن و داده های باینری را به عنوان جریان کپی و جایگذاری کنید. داده ها می توانند اشکالی مانند زیر داشته باشند:
- فایل های ذخیره شده در دستگاه واقعی
- جریان از سوکت
- مقادیر زیادی داده در سیستم پایگاه داده زیربنایی یک ارائه دهنده ذخیره می شود
یک ارائهدهنده محتوا برای جریانهای داده، بهجای شی Cursor
، با یک شی توصیفگر فایل، مانند AssetFileDescriptor
، به دادههای خود دسترسی میدهد. برنامه چسباندن جریان داده را با استفاده از این توصیفگر فایل می خواند.
برای تنظیم برنامه خود برای کپی کردن جریان داده با ارائه دهنده، این مراحل را دنبال کنید:
- یک URI محتوا برای جریان داده ای که در کلیپ بورد قرار می دهید تنظیم کنید. گزینه های انجام این کار شامل موارد زیر است:
- همانطور که در بخش Encode an identifier در URI توضیح داده شده است، یک شناسه برای جریان داده در URI رمزگذاری کنید و سپس جدولی را در ارائه دهنده خود نگه دارید که حاوی شناسه ها و نام جریان مربوطه است.
- نام جریان را مستقیماً در URI رمزگذاری کنید.
- از یک URI منحصر به فرد استفاده کنید که همیشه جریان فعلی را از ارائه دهنده برمی گرداند. اگر از این گزینه استفاده میکنید، به یاد داشته باشید که هر زمان که جریان را با استفاده از URI در کلیپبورد کپی میکنید، ارائهدهنده خود را بهروزرسانی کنید تا به جریان دیگری اشاره کند.
- برای هر نوع جریان داده ای که قصد ارائه آن را دارید، یک نوع MIME ارائه دهید. برنامههای کاربردی چسباندن به این اطلاعات نیاز دارند تا تعیین کنند آیا میتوانند دادهها را در کلیپ بورد جایگذاری کنند یا خیر.
- یکی از متدهای
ContentProvider
را اجرا کنید که یک توصیفگر فایل را برای یک جریان برمی گرداند. اگر شناسهها را روی URI محتوا رمزگذاری میکنید، از این روش برای تعیین جریان باز کردن استفاده کنید. - برای کپی کردن جریان داده در کلیپ بورد، URI محتوا را بسازید و آن را در کلیپ بورد قرار دهید.
برای چسباندن یک جریان داده، یک برنامه کاربردی کلیپ را از کلیپ بورد دریافت میکند، URI را دریافت میکند و از آن در فراخوانی روش توصیفگر فایل ContentResolver
استفاده میکند که جریان را باز میکند. متد ContentResolver
متد ContentProvider
مربوطه را فراخوانی می کند و URI محتوا را به آن ارسال می کند. ارائه دهنده شما توصیفگر فایل را به روش ContentResolver
برمی گرداند. سپس برنامه چسباندن مسئولیت خواندن داده ها از جریان را دارد.
لیست زیر مهم ترین روش های توصیف کننده فایل را برای ارائه دهنده محتوا نشان می دهد. هر یک از اینها دارای یک متد ContentResolver
متناظر با رشته "Descriptor" است که به نام متد اضافه شده است. به عنوان مثال، آنالوگ ContentResolver
openAssetFile()
openAssetFileDescriptor()
است.
-
openTypedAssetFile()
این روش یک توصیفگر فایل دارایی را برمی گرداند، اما تنها در صورتی که نوع MIME ارائه شده توسط ارائه دهنده پشتیبانی شود. تماس گیرنده - برنامه ای که چسباندن را انجام می دهد - یک الگوی نوع MIME ارائه می دهد. ارائهدهنده محتوای برنامهای که یک URI را در کلیپبورد کپی میکند، اگر بتواند آن نوع MIME را ارائه کند، یک دسته فایل
AssetFileDescriptor
را برمیگرداند و اگر نتواند، یک استثنا ایجاد میکند.این روش زیر بخش های فایل ها را مدیریت می کند. می توانید از آن برای خواندن دارایی هایی که ارائه دهنده محتوا در کلیپ بورد کپی کرده است استفاده کنید.
-
openAssetFile()
- این روش یک فرم کلی تر از
openTypedAssetFile()
است. انواع مجاز MIME را فیلتر نمی کند، اما می تواند زیربخش های فایل ها را بخواند. -
openFile()
- این یک شکل کلی تر از
openAssetFile()
است. نمی تواند زیربخش های فایل ها را بخواند.
شما می توانید به صورت اختیاری از متد openPipeHelper()
با متد توصیفگر فایل خود استفاده کنید. این به برنامه چسباندن اجازه می دهد تا داده های جریان را در یک رشته پس زمینه با استفاده از یک لوله بخواند. برای استفاده از این روش، رابط ContentProvider.PipeDataWriter
را پیاده سازی کنید.
قابلیت کپی و چسباندن موثر طراحی کنید
برای طراحی عملکرد کپی و چسباندن موثر برای برنامه خود، این نکات را به خاطر بسپارید:
- در هر زمان، تنها یک کلیپ در کلیپ بورد وجود دارد. یک عملیات کپی جدید توسط هر برنامه کاربردی در سیستم، کلیپ قبلی را بازنویسی می کند. از آنجایی که کاربر ممکن است از برنامه شما دور شود و قبل از بازگشت آن را کپی کند، نمی توانید تصور کنید که کلیپ بورد حاوی کلیپی است که کاربر قبلاً در برنامه شما کپی کرده است.
- هدف مورد نظر از چندین شیء
ClipData.Item
در هر کلیپ، پشتیبانی از کپی و چسباندن چندین انتخاب به جای اشکال مختلف ارجاع به یک انتخاب واحد است. شما معمولاً می خواهید تمام اشیاءClipData.Item
در یک کلیپ به یک شکل باشند. یعنی همه آنها باید متن ساده، URI محتوا یاIntent
باشند و ترکیبی نباشند. - هنگامی که داده ارائه می کنید، می توانید نمایش های MIME مختلفی ارائه دهید. انواع MIME را که پشتیبانی می کنید به
ClipDescription
اضافه کنید و سپس انواع MIME را در ارائه دهنده محتوای خود پیاده سازی کنید. - هنگامی که داده ها را از کلیپ بورد دریافت می کنید، برنامه شما مسئول بررسی انواع MIME موجود و سپس تصمیم گیری در مورد استفاده از کدام یک، در صورت وجود، است. حتی اگر یک کلیپ در کلیپ بورد وجود داشته باشد و کاربر درخواست چسباندن کند، برنامه شما نیازی به انجام چسباندن ندارد. اگر نوع MIME سازگار است، خمیر را انجام دهید. ممکن است با استفاده از
coerceToText()
دادههای موجود در کلیپ بورد را به متن تبدیل کنید. اگر برنامه شما بیش از یکی از انواع MIME موجود را پشتیبانی می کند، می توانید به کاربر اجازه دهید کدام یک را برای استفاده انتخاب کند.