Android, kopyalama ve yapıştırma için güçlü bir panosu tabanlı çerçeve sunar. Metin dizeleri, karmaşık veri yapıları, metin ve ikili akış verileri ve uygulama öğeleri dahil olmak üzere basit ve karmaşık veri türlerini destekler. Basit metin verileri doğrudan panoda depolanır. Karmaşık veriler ise yapıştırma uygulamasının bir içerik sağlayıcıyla çözdüğü bir referans olarak depolanır. Kopyalama ve yapıştırma, hem bir uygulama içinde hem de çerçeveyi uygulayan uygulamalar arasında çalışır.
Çerçevenin bir kısmı içerik sağlayıcıları kullandığından bu dokümanda, İçerik sağlayıcılar bölümünde açıklanan Android Content Provider API hakkında bilgi sahibi olduğunuz varsayılmaktadır.
Kullanıcılar, içeriği panosuna kopyalarken geri bildirim bekler. Bu nedenle Android, kopyalama ve yapıştırma işlemini destekleyen çerçeveye ek olarak Android 13 (API düzeyi 33) ve sonraki sürümlerde kopyalama işlemi sırasında kullanıcılara varsayılan bir kullanıcı arayüzü gösterir. Bu özellik nedeniyle, bildirimlerin tekrar gönderilme riski vardır. Bu uç durum hakkında daha fazla bilgiyi Yinelenen bildirimler almamaya dikkat edin bölümünde bulabilirsiniz.
Android 12L (API düzeyi 32) ve önceki sürümlerde kopyalama işlemi sırasında kullanıcılara manuel olarak geri bildirim sağlayın. Bu dokümanda bu konudaki önerilere göz atın.
Pano çerçevesi
Pano çerçevesini kullandığınızda verileri bir klip nesnesine, ardından klip nesnesini sistem genelindeki panosuna yerleştirin. Klip nesnesi üç biçimden birinde olabilir:
- Metin
- Metin dizesi. Dize doğrudan klip nesnesine yerleştirilir ve daha sonra panosuna kopyalanır. Dizesi yapıştırmak için panodan klip nesnesini alın ve dizeyi uygulamanızın depolama alanına kopyalayın.
- URI
-
URI'nin herhangi bir biçimini temsil eden bir
Uri
nesnesi. Bu, esas olarak bir içerik sağlayıcıdan karmaşık verileri kopyalamak için kullanılır. Verileri kopyalamak için birUri
nesnesini bir klip nesnesine, klip nesnesini de panoya yerleştirin. Verileri yapıştırmak için klip nesnesini,Uri
nesnesini alın, bunu bir veri kaynağına (ör. içerik sağlayıcı) çözümleyin ve kaynaktaki verileri uygulamanızın depolama alanına kopyalayın. - Amaç
-
Intent
. Bu, uygulama kısayollarının kopyalanmasını destekler. Verileri kopyalamak için birIntent
oluşturun, bu nesneyi bir klip nesnesine koyun ve klip nesnesini panoya yerleştirin. Verileri yapıştırmak için klip nesnesini alın ve ardındanIntent
nesnesini uygulamanızın bellek alanına kopyalayın.
Panoda aynı anda yalnızca bir klip nesnesi bulunabilir. Bir uygulama, klip nesnesini panosuna yerleştirdiğinde önceki klip nesnesi kaybolur.
Kullanıcıların uygulamanıza veri yapıştırmasına izin vermek istiyorsanız tüm veri türlerini işlemeniz gerekmez. Kullanıcılara yapıştırma seçeneği sunmadan önce, panosundaki verileri inceleyebilirsiniz. Klip nesnesi, belirli bir veri formuna sahip olmanın yanı sıra hangi MIME türlerinin kullanılabileceğini belirten meta veriler de içerir. Bu meta veriler, uygulamanızın panosundaki verilerle yararlı bir şey yapıp yapamayacağını belirlemenize yardımcı olur. Örneğin, temel olarak metinle çalışan bir uygulamanız varsa URI veya intent içeren klip nesnelerini yoksayabilirsiniz.
Ayrıca, kullanıcıların, verileri panoya kopyaladıktan sonra biçimi ne olursa olsun metin yapıştırmasına izin verebilirsiniz. Bunu yapmak için, pano verilerini metin olarak göstermeye zorlayıp bu metni yapıştırın. Bu konu Panoyu metne zorlama bölümünde açıklanmıştır.
Pano sınıfları
Bu bölümde, panos çerçevesi tarafından kullanılan sınıflar açıklanmaktadır.
ClipboardManager
Android sistem panosu, genel ClipboardManager
sınıfı tarafından temsil edilir.
Bu sınıfı doğrudan örneklendiremezsiniz. Bunun yerine, getSystemService(CLIPBOARD_SERVICE)
çağrısını yaparak öğeye referans alın.
ClipData, ClipData.Item ve ClipDescription
Verileri panosuna eklemek için verilerin açıklamasını ve verilerin kendisini içeren bir ClipData
nesnesi oluşturun. Panoda aynı anda bir ClipData
bulunabilir. ClipData
, bir ClipDescription
nesnesi ve bir veya daha fazla ClipData.Item
nesnesi içerir.
ClipDescription
nesnesi, kliple ilgili meta verileri içerir. Özellikle, klibin verileri için kullanılabilen MIME türlerinin bir dizisini içerir. Ayrıca, Android 12 (API düzeyi 31) ve sonraki sürümlerde meta veriler, nesnenin stilize metin içerip içermediği ve nesnedeki metin türü hakkında bilgi içerir.
Panoya bir klip eklediğinizde bu bilgiler, yapıştırma uygulamaları tarafından kullanılabilir. Bu uygulamalar, klip verilerini işleyip işleyemeyeceklerini inceleyebilir.
ClipData.Item
nesnesi metni, URI'yi veya intent verilerini içerir:
- Metin
-
A
CharSequence
. - URI
-
A
Uri
. Bu genellikle bir içerik sağlayıcı URI'si içerir ancak herhangi bir URI'ye izin verilir. Verileri sağlayan uygulama, URI'yi panoya yerleştirir. Verileri yapıştırmak isteyen uygulamalar, URI'yi panosundan alır ve içerik sağlayıcıya veya başka bir veri kaynağına erişip verileri almak için kullanır. - Amaç
-
Intent
. Bu veri türü, bir uygulama kısayolunu panoya kopyalamanıza olanak tanır. Kullanıcılar daha sonra kullanmak üzere kısayolu uygulamalarına yapıştırabilir.
Bir klibe birden fazla ClipData.Item
nesnesi ekleyebilirsiniz. Bu sayede kullanıcılar birden fazla seçimi tek bir klip olarak kopyalayıp yapıştırabilir. Örneğin, kullanıcının aynı anda birden fazla öğe seçmesine olanak tanıyan bir liste widget'ınız varsa tüm öğeleri bir kerede panoya kopyalayabilirsiniz. Bunu yapmak için her liste öğesi için ayrı bir ClipData.Item
oluşturun ve ardından ClipData.Item
nesnelerini ClipData
nesnesine ekleyin.
ClipData kolaylık yöntemleri
ClipData
sınıfı, tek bir ClipData.Item
nesnesi ve basit bir ClipDescription
nesnesi içeren bir ClipData
nesnesi oluşturmak için statik kolaylık yöntemleri sağlar:
-
newPlainText(label, text)
- Tek
ClipData.Item
nesnesi metin dizesi içeren birClipData
nesnesi döndürür.ClipDescription
nesnesinin etiketilabel
olarak ayarlanmıştır.ClipDescription
dosyasında tek MIME türüMIMETYPE_TEXT_PLAIN
'dur.Metin dizesinden klip oluşturmak için
newPlainText()
simgesini kullanın. -
newUri(resolver, label, URI)
- Tek
ClipData.Item
nesnesi URI içeren birClipData
nesnesi döndürür.ClipDescription
nesnesinin etiketilabel
olarak ayarlanmıştır. URI bir içerik URI'siyse (yaniUri.getScheme()
content:
döndürüyorsa) yöntem, mevcut MIME türlerini içerik sağlayıcıdan almak içinresolver
içinde sağlananContentResolver
nesnesini kullanır. Ardından bunlarıClipDescription
içinde depolar.content:
URI olmayan bir URI için yöntem, MIME türünüMIMETYPE_TEXT_URILIST
olarak ayarlar.URI'den (özellikle
content:
URI'den) klip oluşturmak içinnewUri()
'ü kullanın. -
newIntent(label, intent)
- Tek
ClipData.Item
nesnesiIntent
içeren birClipData
nesnesi döndürür.ClipDescription
nesnesinin etiketilabel
olarak ayarlanmıştır. MIME türüMIMETYPE_TEXT_INTENT
olarak ayarlanmıştır.Intent
nesnesinden klip oluşturmak içinnewIntent()
'ü kullanın.
Panoya kopyalanan verileri metne dönüştürme
Uygulamanız yalnızca metinle çalışıyor olsa bile ClipData.Item.coerceToText()
yöntemiyle dönüştürerek metin olmayan verileri panodan kopyalayabilirsiniz.
Bu yöntem, ClipData.Item
içindeki verileri metne dönüştürür ve bir CharSequence
döndürür. ClipData.Item.coerceToText()
işlevinin döndürdüğü değer, ClipData.Item
'teki verilerin biçimine bağlıdır:
- Metin
-
ClipData.Item
metinse (yanigetText()
null değilse) coerceToText() metni döndürür. - URI
-
ClipData.Item
bir URI ise (yanigetUri()
boş değilse)coerceToText()
bunu içerik URI'si olarak kullanmaya çalışır.- URI bir içerik URI'siyse ve sağlayıcı bir metin akışı döndürebiliyorsa
coerceToText()
bir metin akışı döndürür. - URI bir içerik URI'siyse ancak sağlayıcı metin akışı sunmuyorsa
coerceToText()
, URI'nin bir temsilini döndürür. Temsil,Uri.toString()
tarafından döndürülen temsille aynıdır. - URI bir içerik URI'si değilse
coerceToText()
, URI'nin bir temsilini döndürür. Temsil,Uri.toString()
tarafından döndürülen değerle aynıdır.
- URI bir içerik URI'siyse ve sağlayıcı bir metin akışı döndürebiliyorsa
- Amaç
ClipData.Item
birIntent
ise (yanigetIntent()
boş değilse)coerceToText()
bunu bir Intent URI'sine dönüştürür ve döndürür. Temsil,Intent.toUri(URI_INTENT_SCHEME)
tarafından döndürülen temsille aynıdır.
Pano çerçevesi Şekil 2'de özetlenmiştir. Uygulama, verileri kopyalamak için ClipboardManager
global panosuna bir ClipData
nesnesi yerleştirir. ClipData
, bir veya daha fazla ClipData.Item
nesnesi ve bir ClipDescription
nesnesi içerir. Uygulamalar, verileri yapıştırmak için ClipData
öğesini alır, ClipDescription
öğesinden MIME türünü alır ve verileri ClipData.Item
öğesinden veya ClipData.Item
tarafından belirtilen içerik sağlayıcıdan alır.
Panoya kopyala
Verileri panoya kopyalamak için global ClipboardManager
nesnesinin bir tutamacını alın, bir ClipData
nesnesi oluşturun ve bu nesneye bir ClipDescription
ve bir veya daha fazla ClipData.Item
nesnesi ekleyin. Ardından, tamamlanmış ClipData
nesnesini ClipboardManager
nesnesine ekleyin. Bu konu aşağıdaki prosedürde daha ayrıntılı olarak açıklanmıştır:
- Verileri içerik URI'si kullanarak kopyaliyorsanız bir içerik sağlayıcı oluşturun.
- Sistem panosuna erişme:
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);
-
Verileri yeni bir
ClipData
nesnesine kopyalayın:-
Metinler için
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 için
Bu snippet, sağlayıcının içerik URI'sine bir kayıt kimliği kodlayarak bir URI oluşturur. Bu teknik, URI'de tanımlayıcı kodlama bölümünde daha ayrıntılı olarak ele alınmıştır.
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 için
Bu snippet, bir uygulama için bir
Intent
oluşturur ve ardından bunu klip nesnesine yerleştirir: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);
-
Metinler için
-
Yeni klip nesnesini panoya yerleştirin:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Panoya kopyalarken geri bildirim sağlama
Kullanıcılar, bir uygulamanın içeriği panosuna kopyaladığında görsel geri bildirim bekler. Bu işlem, Android 13 ve sonraki sürümlerin yüklü olduğu cihazlardaki kullanıcılar için otomatik olarak yapılır ancak önceki sürümlerde manuel olarak uygulanmalıdır.
Android 13'ten itibaren sistem, panoya içerik eklendiğinde standart bir görsel onay gösterir. Yeni onay şu işlemleri yapar:
- İçeriğin başarıyla kopyalandığını onaylar.
- Kopyalanan içeriğin önizlemesini sağlar.
Android 12L (API düzeyi 32) ve önceki sürümlerde kullanıcılar, içeriği başarıyla kopyalayıp kopyalamadığınızdan veya neyi kopyaladığından emin olmayabilir. Bu özellik, kopyalama işleminden sonra uygulamalar tarafından gösterilen çeşitli bildirimleri standart hale getirir ve kullanıcılara panos üzerinde daha fazla kontrol sunar.
Yinelenen bildirimleri önleme
Android 12L (API düzeyi 32) ve önceki sürümlerde, kullanıcılar başarılı bir şekilde kopyalama işlemi gerçekleştirdiğinde Toast
veya Snackbar
gibi bir widget kullanarak görsel uygulama içi geri bildirim göndererek kullanıcıları uyarmanızı öneririz.
Bilgilerin yinelenen şekilde gösterilmesini önlemek için Android 13 ve sonraki sürümlerde uygulama içi kopyadan sonra gösterilen pop-up'ları veya bilgi çubuğu mesajlarını kaldırmanızı önemle tavsiye ederiz.
Aşağıda bunun nasıl uygulanacağına dair bir örnek verilmiştir:
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() }
Hassas içeriği panoya ekleme
Uygulamanız, kullanıcıların şifre veya kredi kartı bilgileri gibi hassas içerikleri panosuna kopyalamasına izin veriyorsa ClipboardManager.setPrimaryClip()
işlevini çağırmadan önce ClipData
içinde ClipDescription
öğesine bir işaret eklemeniz gerekir. Bu işaretin eklenmesi, Android 13 ve sonraki sürümlerde kopyalanan içeriğin görsel onayında hassas içeriğin görünmesini engeller.
Hassas içerikleri işaretlemek için ClipDescription
değerine bir boole ek ekleyin. Hedeflenen API düzeyinden bağımsız olarak tüm uygulamalar bunu yapmalıdır.
// 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) } }
Panodan yapıştırma
Daha önce açıklandığı gibi, global pano nesnesini, klip nesnesini, verilerine bakarak ve mümkünse klip nesnesinden kendi depolama alanınıza kopyalayarak panodaki verileri yapıştırın. Bu bölümde, üç tür pano verisinin nasıl yapıştırılacağı ayrıntılı olarak açıklanmaktadır.
Düz metin yapıştırma
Düz metin yapıştırmak için genel panoyu alın ve düz metin döndürüp döndürmediğini doğrulayın. Ardından, klip nesnesini alın ve metnini aşağıdaki prosedürde açıklandığı gibi getText()
kullanarak kendi depolama alanınıza kopyalayın:
getSystemService(CLIPBOARD_SERVICE)
kullanarak globalClipboardManager
nesnesini alın. Ayrıca, yapıştırılan metni içerecek bir genel değişken de tanımlayın:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- Mevcut etkinlikte "yapıştır" seçeneğini etkinleştirmeniz veya devre dışı bırakmanız gerekip gerekmediğini belirleyin. Apanızın bir klip içerdiğini ve klibin temsil ettiği veri türünü işleyebildiğinizi doğrulayın:
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); }
- Verileri panoya kopyalayın. Koddaki bu noktaya yalnızca "yapıştır" menü öğesi etkinse erişilebilir. Bu nedenle, panosunun düz metin içerdiğini varsayabilirsiniz. Henüz bir metin dizesi mi yoksa düz metne yönlendiren bir URI mi içerdiğini bilmiyorsunuzdur.
Aşağıdaki kod snippet'inde bu test yapılmaktadır ancak yalnızca düz metni işleme kodu gösterilmektedir:
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; } }
İçerik URI'sinden veri yapıştırma
ClipData.Item
nesnesi bir içerik URI'si içeriyorsa ve MIME türlerinden birini işleyebileceğinizi belirlerseniz verileri almak için bir ContentResolver
oluşturun ve uygun içerik sağlayıcı yöntemini çağırın.
Aşağıdaki prosedürde, panodaki bir içerik URI'sine göre içerik sağlayıcıdan nasıl veri alınacağı açıklanmaktadır. Uygulamanın kullanabileceği bir MIME türünün sağlayıcıda mevcut olup olmadığını kontrol eder.
-
MIME türünü içerecek bir genel değişken tanımlayın:
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";
- Global panoyu alın. İçerik sağlayıcıya erişebilmek için bir içerik çözücü de edinin:
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();
- Panodan birincil klibi ve içeriğini URI olarak alın:
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();
getType(Uri)
işlevini çağırarak URI'nin içerik URI'si olup olmadığını test edin.Uri
geçerli bir içerik sağlayıcıyı işaret etmiyorsa bu yöntem null değerini döndürür.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);
- İçerik sağlayıcının, uygulamanın anladığı bir MIME türünü destekleyip desteklemediğini test edin. Bu durumda, verileri almak için
ContentResolver.query()
numaralı telefonu arayın. Döndürülen değer birCursor
olur.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(); } } } }
İntent yapıştırma
Bir intent'i yapıştırmak için önce genel panoyu alın. ClipData.Item
nesnesinin Intent
içerip içermediğini kontrol edin. Ardından, intent'i kendi depolama alanınıza kopyalamak için getIntent()
işlevini çağırın. Aşağıdaki snippet'te bu gösterilmektedir:
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. }
Uygulamanız panosundaki verilere eriştiğinde gösterilen sistem bildirimi
Android 12 (API düzeyi 31) ve sonraki sürümlerde, uygulamanız getPrimaryClip()
çağrısı yaptığında sistem genellikle bir pop-up mesajı gösterir.
İletideki metin aşağıdaki biçimi içerir:
APP pasted from your clipboard
Uygulamanız aşağıdakilerden birini yaptığında sistem bir pop-up mesajı göstermiyor:
- Kendi uygulamanızdan
ClipData
erişimi - Belirli bir uygulamadan
ClipData
'ye tekrar tekrar erişir. Bildirim yalnızca uygulamanız söz konusu uygulamadaki verilere ilk kez eriştiğinde gösterilir. - Klip nesnesinin meta verilerini alır (ör.
getPrimaryClip()
yerinegetPrimaryClipDescription()
çağrısı yaparak).
Karmaşık verileri kopyalamak için içerik sağlayıcıları kullanma
İçerik sağlayıcılar, veritabanı kayıtları veya dosya akışları gibi karmaşık verilerin kopyalanmasını destekler. Verileri kopyalamak için panoya bir içerik URI'si koyun. Ardından, yapıştırma uygulamaları bu URI'yi panosundan alır ve veritabanı verilerini veya dosya akışı tanımlayıcılarını almak için kullanır.
Yapıştırma uygulamasında yalnızca verilerinizin içerik URI'si bulunduğundan, hangi veri parçasının alınacağını bilmesi gerekir. Bu bilgileri, URI'nin kendisinde verilerin tanımlayıcısını kodlayarak sağlayabilir veya kopyalamak istediğiniz verileri döndüren benzersiz bir URI sağlayabilirsiniz. Hangi tekniği seçeceğiniz, verilerinizin düzenlenmesine bağlıdır.
Aşağıdaki bölümlerde URI'lerin nasıl oluşturulacağı, karmaşık verilerin ve dosya akışlarının nasıl sağlanacağı açıklanmaktadır. Açıklamalarda, içerik sağlayıcı tasarımının genel ilkelerine aşina olduğunuz varsayılmaktadır.
URI'de tanımlayıcıyı kodlama
Verileri URI ile panoya kopyalamak için kullanabileceğiniz kullanışlı bir teknik, URI'nin kendisinde veriler için bir tanımlayıcı kodlamaktır. Ardından içerik sağlayıcınız, URI'den tanımlayıcıyı alıp verileri almak için kullanabilir. Yapıştırma uygulamasının, tanımlayıcının var olduğunu bilmesi gerekmez. İçerik sağlayıcının, "referansınızı" (URI ve tanımlayıcı) panodan alması, bunu içerik sağlayıcınıza vermesi ve verileri geri alması yeterlidir.
Genellikle bir tanımlayıcının URI'ye kodlanmasını, URI'nin sonuna eklemek suretiyle sağlarsınız. Örneğin, sağlayıcı URI'nizi aşağıdaki dize olarak tanımladığınızı varsayalım:
"content://com.example.contacts"
Bu URI'ye bir ad kodlamak istiyorsanız aşağıdaki kod snippet'ini kullanın:
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);
Halihazırda bir içerik sağlayıcı kullanıyorsanız URI'nin kopyalama için olduğunu belirten yeni bir URI yolu ekleyebilirsiniz. Örneğin, aşağıdaki URI yollarına sahip olduğunuzu varsayalım:
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
URI'leri kopyalamak için başka bir yol ekleyebilirsiniz:
"content://com.example.contacts/copying"
Ardından, kalıp eşleştirme yaparak "kopya" URI'sini algılayabilir ve kopyalama ve yapıştırmaya özel kodla işleyebilirsiniz.
Verilerinizi düzenlemek için zaten bir içerik sağlayıcı, dahili veritabanı veya dahili tablo kullanıyorsanız normalde kodlama tekniğini kullanırsınız. Bu tür durumlarda, kopyalamak istediğiniz birden fazla veri parçası ve muhtemelen her parça için benzersiz bir tanımlayıcınız vardır. Yapıştırma uygulamasından gelen bir sorguya yanıt olarak, verileri tanımlayıcısına göre arayabilir ve döndürebilirsiniz.
Birden fazla veriniz yoksa büyük olasılıkla bir tanımlayıcı kodlamanız gerekmez. Sağlayıcınıza özgü bir URI kullanabilirsiniz. Sağlayıcınız, bir sorguya yanıt olarak şu anda içerdiği verileri döndürür.
Veri yapılarını kopyalama
Karmaşık verileri kopyalayıp yapıştırmak için bir içerik sağlayıcıyı bileşenin alt sınıfı olarak ayarlayın.ContentProvider
Panoya koyduğunuz URI'yi, sağlamak istediğiniz tam kaydı işaret edecek şekilde kodlayın. Ayrıca, başvurunuzun mevcut durumunu da göz önünde bulundurun:
- Halihazırda bir içerik sağlayıcınız varsa işlevini genişletebilirsiniz. Yalnızca veri yapıştırmak isteyen uygulamalardan gelen URI'leri işlemek için
query()
yöntemini değiştirmeniz gerekebilir. "Kopya" URI kalıbını işleyen yöntemi değiştirmeniz muhtemeldir. - Uygulamanızda dahili bir veritabanı varsa bu veritabanından kopyalama işlemini kolaylaştırmak için veritabanını bir içerik sağlayıcıya taşımak isteyebilirsiniz.
- Veritabanı kullanmıyorsanız tek amacı, panosundan yapıştırma yapan uygulamalara veri sunmak olan basit bir içerik sağlayıcı uygulayabilirsiniz.
İçerik sağlayıcıda en az aşağıdaki yöntemleri geçersiz kılın:
-
query()
- Yapıştırma uygulamaları, bu yöntemi kullanarak verileri, panoya eklediğiniz URI ile alabileceğini varsayar. Kopyalamayı desteklemek için bu yöntemin özel bir "copy" yolu içeren URI'leri algılamasını sağlayın. Ardından uygulamanız, kopya yolunu ve kopyalamak istediğiniz kaydın tam işaretçisini içeren, panosuna yerleştirmek için bir "kopya" URI'si oluşturabilir.
-
getType()
- Bu yöntem, kopyalamak istediğiniz verilerin MIME türlerini döndürmelidir.
newUri()
yöntemi, MIME türlerini yeniClipData
nesnesine yerleştirmek içingetType()
'u çağırır.Karmaşık veriler için MIME türleri İçerik sağlayıcılar bölümünde açıklanmıştır.
insert()
veya update()
gibi diğer içerik sağlayıcı yöntemlerinden herhangi birine sahip olmanız gerekmez.
Yapıştırma uygulamasının tek yapması gereken, desteklenen MIME türlerinizi almak ve sağlayıcınızdan veri kopyalamaktır.
Bu yöntemlere zaten sahipseniz kopyalama işlemlerini etkilemezler.
Aşağıdaki snippet'lerde, uygulamanızın karmaşık verileri kopyalayacak şekilde nasıl ayarlanacağı gösterilmektedir:
-
Uygulamanızın genel sabitlerinde, verileri kopyalamak için kullandığınız URI dizelerini tanımlayan bir temel URI dizesi ve bir yol beyan edin. Kopyalanan veriler için bir MIME türü de belirtin.
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";
- Kullanıcıların veri kopyaladığı etkinlikte, verileri panoya kopyalayacak şekilde kodu ayarlayın.
Kopyalama isteğine yanıt olarak URI'yi panoya koyun.
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);
-
İçerik sağlayıcınızın genel kapsamında bir URI eşleştirici oluşturun ve panoya koyduğunuz URI'lerle eşleşen bir URI kalıbı ekleyin.
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()
yöntemini ayarlayın. Bu yöntem, nasıl kodladığınıza bağlı olarak farklı URI kalıplarını işleyebilir ancak yalnızca panoya kopyalama işleminin kalıbı gösterilir.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. ... }
-
Kopyalanan veriler için uygun bir MIME türü döndürmek üzere
getType()
yöntemini ayarlayın: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); ... } }
Bir içerik URI'sinden veri yapıştırma bölümünde, içerik URI'sinin nasıl panoya alınacağı ve verileri almak ve yapıştırmak için nasıl kullanılacağı açıklanmaktadır.
Veri akışlarını kopyalama
Büyük miktarda metin ve ikili veriyi akış olarak kopyalayıp yapıştırabilirsiniz. Veriler aşağıdaki gibi biçimlere sahip olabilir:
- Gerçek cihazda depolanan dosyalar
- Yuvalardan gelen akışlar
- Sağlayıcının temel veritabanı sisteminde depolanan büyük miktarda veri
Veri akışları için içerik sağlayıcı, Cursor
nesnesi yerine AssetFileDescriptor
gibi bir dosya tanımlayıcısı nesnesi ile verilerine erişim sağlar. Yapıştırma uygulaması, bu dosya tanımlayıcısını kullanarak veri akışını okur.
Uygulamanızı, bir sağlayıcıyla veri akışı kopyalayacak şekilde ayarlamak için aşağıdaki adımları uygulayın:
-
Panoya yerleştirdiğiniz veri akışı için bir içerik URI'si oluşturun. Bunu yapmanın seçenekleri şunlardır:
- URI'de tanımlayıcı kodlama bölümünde açıklandığı gibi, veri akışı için URI'ye bir tanımlayıcı kodlayın ve ardından sağlayıcınızda tanımlayıcıları ve ilgili akış adını içeren bir tablo bulundurun.
- Akış adını doğrudan URI'de kodlayın.
- Sağlayıcıdan her zaman mevcut yayını döndüren benzersiz bir URI kullanın. Bu seçeneği kullanıyorsanız yayını URI'yi kullanarak panoya kopyaladığınızda sağlayıcınızı farklı bir yayına yönlendirecek şekilde güncellemeyi unutmayın.
- Sunmayı planladığınız her veri akışı türü için bir MIME türü belirtin. Verileri yapıştıran uygulamalar, verileri panoya yapıştırıp yapıştıramayacaklarını belirlemek için bu bilgilere ihtiyaç duyar.
- Bir akış için dosya tanımlayıcı döndüren
ContentProvider
yöntemlerinden birini uygulayın. İçerik URI'sinde tanımlayıcıları kodlarsanız hangi akışın açılacağını belirlemek için bu yöntemi kullanın. - Veri akışını panoya kopyalamak için içerik URI'sini oluşturup panoya yerleştirin.
Bir uygulama, veri akışını yapıştırmak için klibi panosundan alır, URI'yi alır ve akışı açan bir ContentResolver
dosya tanımlayıcı yöntemi çağrısında kullanır. ContentResolver
yöntemi, içerik URI'sini ileterek ilgili ContentProvider
yöntemini çağırır. Sağlayıcınız, dosya tanımlayıcısını ContentResolver
yöntemine döndürür. Ardından, yapıştırma uygulamasının akıştaki verileri okuma sorumluluğu vardır.
Aşağıdaki listede, içerik sağlayıcı için en önemli dosya tanımlayıcısı yöntemleri gösterilmektedir. Bunların her biri, yöntem adına "Tanımlayıcı" dizesi eklenmiş bir ContentResolver
yöntemine sahiptir. Örneğin, openAssetFile()
değerinin ContentResolver
analogu openAssetFileDescriptor()
'dir.
-
openTypedAssetFile()
-
Bu yöntem, yalnızca sağlanan MIME türü sağlayıcı tarafından destekleniyorsa bir öğe dosyası tanımlayıcısı döndürür. Arayan (yapıştırma işlemini yapan uygulama) bir MIME türü kalıbı sağlar. Bir URI'yi panosuna kopyalayan uygulamanın içerik sağlayıcısı, söz konusu MIME türünü sağlayabiliyorsa bir
AssetFileDescriptor
dosya tutamacını döndürür, sağlayamıyorsa istisna atar.Bu yöntem, dosyaların alt bölümlerini işler. İçerik sağlayıcının panoya kopyaladığı öğeleri okumak için bu özelliği kullanabilirsiniz.
-
openAssetFile()
-
Bu yöntem,
openTypedAssetFile()
işlevinin daha genel bir biçimidir. İzin verilen MIME türlerine göre filtreleme yapmaz ancak dosyaların alt bölümlerini okuyabilir. -
openFile()
-
Bu,
openAssetFile()
'un daha genel bir biçimidir. Dosyaların alt bölümlerini okuyamaz.
Dilerseniz openPipeHelper()
yöntemini dosya tanımlayıcı yönteminizle birlikte kullanabilirsiniz. Bu sayede yapıştırma uygulaması, bir boru kullanarak arka plandaki iş parçacığında akış verilerini okuyabilir. Bu yöntemi kullanmak için ContentProvider.PipeDataWriter
arayüzünü uygulayın.
Etkili bir kopyalama ve yapıştırma işlevi tasarlama
Uygulamanız için etkili bir kopyala ve yapıştır işlevi tasarlamak istiyorsanız aşağıdaki noktaları göz önünde bulundurun:
- Herhangi bir zamanda, panosuzda yalnızca bir klip bulunur. Sistemdeki herhangi bir uygulama tarafından yapılan yeni bir kopyalama işlemi, önceki klibin üzerine yazılır. Kullanıcı, uygulamanızdan uzaklaşıp geri dönmeden önce kopyalama işlemi yapabileceğinden, panosunun daha önce uygulamanızda kopyaladığı klibi içerdiğini varsayamazsınız.
-
Klip başına birden fazla
ClipData.Item
nesnesi eklemenin amacı, tek bir seçime referans verme Genellikle bir klipteki tümClipData.Item
öğelerin aynı şekle sahip olmasını istersiniz. Yani bunların tümü basit metin, içerik URI'si veyaIntent
olmalıdır ve bunların bir karışımı olmamalıdır. -
Veri sağlarken farklı MIME temsilleri sunabilirsiniz. Desteklediğiniz MIME türlerini
ClipDescription
dosyasına ekleyin ve ardından MIME türlerini içerik sağlayıcınıza uygulayın. -
Verileri panodan aldığınızda, mevcut MIME türlerini kontrol etmek ve varsa bunlardan hangisinin kullanılacağına karar vermek uygulamanızın sorumluluğundadır. Klipboard'da bir klip varsa ve kullanıcı yapıştırma isteğinde bulunsa bile uygulamanızın yapıştırma işlemini yapması gerekmez. MIME türü uyumluysa yapıştırma işlemini yapın.
coerceToText()
kullanarak panoya kopyalanan verileri metne dönüştürebilirsiniz. Uygulamanız mevcut MIME türlerinden birden fazlasını destekliyorsa kullanıcının hangisini kullanacağını seçmesine izin verebilirsiniz.