Kopyala ve yapıştır

Kopyalama ve yapıştırma için Android pano tabanlı çerçeve, aşağıdakiler dahil olmak üzere basit ve karmaşık veri türlerini destekler:

  • Metin dizeleri
  • Karmaşık veri yapıları
  • Metin ve ikili akış verileri
  • Uygulama öğeleri

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 bölümünde içerik sağlayıcılar kullanıldığından bu belgede, Android Content Provider API hakkında bilgi sahibi olduğunuz varsayılır.

Metinle çalışma

Bazı bileşenler, aşağıdaki tabloda gösterildiği gibi metin kopyalama ve yapıştırma özelliğini destekler.

Bileşen Metin kopyalanıyor Metin yapıştırma
BasicTextField ✅ ✅
TextField ✅ ✅
SelectionContainer ✅

Örneğin, aşağıdaki snippet'te karttaki metni panoya kopyalayıp TextField uygulamasına yapıştırabilirsiniz. TextField simgesine dokunup basılı tutarak veya imleç tutma yerine dokunarak metni yapıştırma menüsünü görüntüleyebilirsiniz.

val textFieldState = rememberTextFieldState()

Column {
    Card {
        SelectionContainer {
            Text("You can copy this text")
        }
    }
    BasicTextField(state = textFieldState)
}

Metni aşağıdaki klavye kısayoluyla yapıştırabilirsiniz: Ctrl+V . Klavye kısayolu da varsayılan olarak kullanılabilir. Ayrıntılar için Klavye işlemlerini işleme başlıklı makaleyi inceleyin.

ClipboardManager ile kopyala

ClipboardManager ile metinleri panoya kopyalayabilirsiniz. setText() yöntemi, iletilen AnnotatedString nesnesini panoya kopyalar. Aşağıdaki snippet, kullanıcı düğmeyi tıkladığında "Merhaba, pano" ifadesini panoya kopyalar.

// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current

Button(
    onClick = {
        // Copy "Hello, clipboard" to the clipboard
        clipboardManager.setText(AnnotatedString("Hello, clipboard"))
    }
) {
    Text("Click to copy a text")
}

Aşağıdaki snippet de aynı işlemi yapar ancak size daha ayrıntılı kontrol olanağı sunar. Yaygın kullanım alanlarından biri, şifre gibi hassas içerikleri kopyalamaktır. ClipEntry, panodaki bir öğeyi açıklar. Panodaki verileri açıklayan bir ClipData nesnesi içerir. ClipData.newPlainText() yöntemi, bir String nesnesinden ClipData nesnesi oluşturmak için kullanılan kolaylık yöntemidir. Oluşturulan ClipEntry nesnesini, ClipboardManager nesnesi üzerinden setClip() yöntemini çağırarak panoya ayarlayabilirsiniz.

// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current

Button(
    onClick = {
        val clipData = ClipData.newPlainText("plain text", "Hello, clipboard")
        val clipEntry = ClipEntry(clipData)
        clipboardManager.setClip(clipEntry)
    }
) {
    Text("Click to copy a text")
}

ClipboardManager ile yapıştırma

Panoya kopyalanan metne ClipboardManager üzerinden getText() yöntemini çağırarak erişebilirsiniz. getText() yöntemi, panoya bir metin kopyalandığında AnnotatedString nesnesi döndürür. Aşağıdaki snippet, panodaki metni TextField içindeki metne ekler.

val textFieldState = rememberTextFieldState()

Column {
    TextField(state = textFieldState)

    Button(
        onClick = {
            // The getText method returns an AnnotatedString object or null
            val annotatedString = clipboardManager.getText()
            if (annotatedString != null) {
                // The pasted text is placed on the tail of the TextField
                textFieldState.edit {
                    append(annotatedString.text)
                }
            }
        }
    ) {
        Text("Click to paste the text in the clipboard")
    }
}

Zengin içeriklerle çalışma

Kullanıcılar resimlere, videolara ve diğer etkileyici içeriklere bayılır. Uygulamanız, kullanıcının ClipboardManager ve ClipEntry ile zengin içerik kopyalamasına olanak tanıyor. contentReceiver değiştiricisi, zengin içerik yapıştırma özelliğini uygulamanıza yardımcı olur.

Zengin içeriği kopyalama

Uygulamanız, zengin içeriği doğrudan panoya kopyalayamaz. Bunun yerine, uygulamanız panoya bir URI nesnesi iletiyor ve ContentProvider ile içeriğe erişim sağlıyor. Aşağıdaki kod snippet'inde, JPEG görüntüsünün panoya nasıl kopyalanacağı gösterilmektedir. Ayrıntılar için Veri akışlarını kopyalama bölümüne bakın.

// Get a reference to the context
val context = LocalContext.current

Button(
    onClick = {
        // URI of the copied JPEG data
        val uri = Uri.parse("content://your.app.authority/0.jpg")
        // Create a ClipData object from the URI value
        // A ContentResolver finds a proper ContentProvider so that ClipData.newUri can set appropriate MIME type to the given URI
        val clipData = ClipData.newUri(context.contentResolver, "Copied", uri)
        // Create a ClipEntry object from the clipData value
        val clipEntry = ClipEntry(clipData)
        // Copy the JPEG data to the clipboard
        clipboardManager.setClip(clipEntry)
    }
) {
    Text("Copy a JPEG data")
}

Zengin içerik yapıştırma

contentReceiver değiştiricisiyle, değiştirilen bileşende zengin içeriğin BasicTextField öğesine yapıştırılmasını yönetebilirsiniz. Aşağıdaki kod snippet'i, bir resim verisinin yapıştırılan URI'sini Uri nesneleri listesine ekler.

// A URI list of images
val imageList = remember { mutableStateListOf<Uri>() }

// Remember the ReceiveContentListener object as it is created inside a Composable scope
val receiveContentListener = remember {
    ReceiveContentListener { transferableContent ->
        // Handle the pasted data if it is image data
        when {
            // Check if the pasted data is an image or not
            transferableContent.hasMediaType(MediaType.Image) -> {
                // Handle for each ClipData.Item object
                // The consume() method returns a new TransferableContent object containging ignored ClipData.Item objects
                transferableContent.consume { item ->
                    val uri = item.uri
                    if (uri != null) {
                        imageList.add(uri)
                    }
                    // Mark the ClipData.Item object consumed when the retrieved URI is not null
                    uri != null
                }
            }
            // Return the given transferableContent when the pasted data is not an image
            else -> transferableContent
        }
    }
}

val textFieldState = rememberTextFieldState()

BasicTextField(
    state = textFieldState,
    modifier = Modifier
        .contentReceiver(receiveContentListener)
        .fillMaxWidth()
        .height(48.dp)
)

contentReceiver değiştiricisi, bağımsız değişken olarak bir ReceiveContentListener nesnesi alır ve kullanıcı, değiştirilen bileşenin içindeki BasicTextField öğesine veri yapıştırdığında iletilen nesnenin onReceive yöntemini çağırır.

onReceive yöntemine, bu durumda yapıştırılarak uygulamalar arasında aktarılabilecek verileri açıklayan bir TransferableContent nesnesi iletilir. clipEntry özelliğine başvurarak ClipEntry nesnesine erişebilirsiniz.

Örneğin, kullanıcı birden fazla resim seçip bunları panoya kopyaladığında bir ClipEntry nesnesinde birden fazla ClipData.Item nesnesi olabilir. Her ClipData.Item nesnesi için tüketildi veya yoksayıldı olarak işaretlemeniz ve yoksayılan ClipData.Item nesnelerini içeren bir TransferableContent döndürmeniz gerekir. Böylece en yakın üst öğe contentReceiver değiştiricisi bunu alabilir.

TransferableContent.hasMediaType() yöntemi, TransferableContent nesnesinin medya türüne sahip bir öğe sağlayıp sağlayamayacağını belirlemenize yardımcı olabilir. Örneğin, aşağıdaki yöntem çağrısı, TransferableContent nesnesi resim sağlayabiliyorsa true değerini döndürür.

transferableContent.hasMediaType(MediaType.Image)

Karmaşık verilerle çalışma

Karmaşık verileri, zengin içeriklerde olduğu gibi panoya kopyalayabilirsiniz. Ayrıntılar için Karmaşık verileri kopyalamak için içerik sağlayıcıları kullanma başlıklı makaleyi inceleyin.

Ayrıca, zengin içerik için karmaşık verilerin yapıştırılmasını da aynı şekilde işleyebilirsiniz. Yapıştırılan verilerin URI'sini alabilirsiniz. Gerçek veriler ContentProvider kaynağından alınabilir. Daha fazla bilgi için Sağlayıcıdan veri alma bölümüne bakın.

İçerik kopyalamayla ilgili geri bildirim

Kullanıcılar, içeriği panoya kopyaladıklarında geri bildirim bekler. Bu nedenle, kopyalama ve yapıştırma işlemlerini destekleyen çerçeveye ek olarak Android, Android 13 (API düzeyi 33) ve sonraki sürümlerde kopyalama işlemi yapan kullanıcılara varsayılan bir kullanıcı arayüzü gösterir. Bu özellik nedeniyle yinelenen bildirim riski vardır. Bu uç durum hakkında daha fazla bilgiyi Yinelenen bildirimleri önleme bölümünde bulabilirsiniz.

Android 13'teki pano bildirimini gösteren animasyon
Şekil 1. Android 13 ve sonraki sürümlerde içerik panoya girdiğinde gösterilen kullanıcı arayüzü.

Android 12L (API düzeyi 32) ve daha eski sürümlerde kopyalama işlemi yapıldığında kullanıcılara manuel olarak geri bildirim verme. Öneriyi görün.

Hassas içerik

Uygulamanızın, kullanıcının şifreler gibi hassas içerikleri panoya kopyalamasına izin vermesini tercih ederseniz uygulamanızın bunu sisteme bildirmesi gerekir. Böylece sistem, kopyalanan hassas içeriği kullanıcı arayüzünde göstermez (Şekil 2).

Hassas içeriği işaretleyen kopyalanmış metin önizlemesi.
Şekil 2. Hassas içerik işaretli kopyalanan metin önizlemesi.

ClipboardManager nesnesi üzerinden setClip() yöntemini çağırmadan önce ClipData içinde ClipDescription öğesine bir işaret eklemeniz gerekir:

// 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)
    }
}