コピーと貼り付けのための Android クリップボード ベースのフレームワーク は、次のようなプリミティブ データ型と複合データ型をサポートしています。
- テキスト文字列
- 複雑なデータ構造
- テキストとバイナリ ストリーム データ
- アプリケーション アセット
シンプルなテキストデータはクリップボードに直接保存され、 複雑なデータは参照として保存される 貼り付けるアプリがコンテンツ プロバイダと解決されることを示します。
コピーと貼り付けは、アプリ内でもアプリ間でも機能します。 フレームワークを実装しています。
フレームワークの一部でコンテンツ プロバイダが使用されるため、 このドキュメントは、Android Content Provider API についてある程度の知識があることを前提としています。
テキストを操作する
以下の図に示すように、一部のコンポーネントは、すぐに使用できるテキストをコピーして貼り付けることもできます。 表します
コンポーネント | テキストをコピーしています | テキストを貼り付けています |
---|---|---|
BasicTextField | ✅ | ✅ |
TextField | ✅ | ✅ |
選択コンテナ | ✅ |
たとえば、カード内のテキストをクリップボードにコピーできます。
コピーしたテキストを TextField
に貼り付けます。
テキストを貼り付けるメニューを
タップ&TextField
を長押しするか、カーソルのハンドルをタップして。
val textFieldState = rememberTextFieldState()
Column {
Card {
SelectionContainer {
Text("You can copy this text")
}
}
BasicTextField(state = textFieldState)
}
次のキーボード ショートカットを使用してテキストを貼り付けることができます。 Ctrl+V デフォルトのキーボード ショートカットも使用できます。 詳しくは、キーボード アクションの処理をご覧ください。
ClipboardManager
でコピー
ClipboardManager
を使用すると、テキストをクリップボードにコピーできます。
setText() メソッドは、
クリップボードに渡された String オブジェクト。
次のスニペットは、ユーザーがボタンをクリックしたときに「Hello, clipboard」をクリップボードにコピーします。
// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current
Button(
onClick = {
// Copy "Hello, clipboard" to the clipboard
clipboardManager.setText("Hello, clipboard")
}
) {
Text("Click to copy a text")
}
次のスニペットは同じことを行いますが、より細かく制御できます。一般的なユースケースは、パスワードなどの機密コンテンツのコピーです。ClipEntry
は、クリップボード上のアイテムを表します。これには、クリップボード上のデータを記述する ClipData
オブジェクトが含まれます。
ClipData.newPlainText()
メソッドは、次のタスクを行うコンビニエンス メソッドです。
String オブジェクトから ClipData
オブジェクトを作成します。
作成した ClipEntry
オブジェクトをクリップボードに設定するには、ClipboardManager
オブジェクトで setClip() メソッドを呼び出します。
// 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 を使用して貼り付ける
クリップボードにコピーされたテキストにアクセスするには、ClipboardManager
で getText()
メソッドを呼び出します。getText()
メソッドは AnnotatedString
オブジェクトを返す
テキストがクリップボードにコピーされたとき。
次のスニペットは、クリップボード内のテキストを TextField
のテキストに追加します。
var 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(text.toString())
}
}
}
) {
Text("Click to paste the text in the clipboard")
}
}
リッチ コンテンツを操作する
ユーザーは画像、動画、その他の表現力豊かなコンテンツを好みます。アプリで、ユーザーが ClipboardManager
と ClipEntry
を使用してリッチ コンテンツをコピーできるようにします。contentReceiver
修飾子は、リッチ コンテンツの貼り付けを実装する際に役立ちます。
リッチ コンテンツをコピーする
アプリは、リッチ コンテンツをクリップボードに直接コピーできません。
代わりに、アプリは URI
オブジェクトをクリップボードに渡し、ContentProvider
を使用してコンテンツへのアクセスを提供します。次のコード スニペットは、JPEG 画像をクリップボードにコピーする方法を示しています。詳しくは、データ ストリームをコピーするをご覧ください。
// Get a reference to the context
val context = LocalContext.current
Button(
onClick = {
// URI of the copied JPEG data
val uri = Uri.parse("content://your.app.authority/0.jpg")
// Create a ClipData object from the URI value
// A ContentResolver finds a proper ContentProvider so that ClipData.newUri can set appropriate MIME type to the given URI
val clipData = ClipData.newUri(context.contentResolver, "Copied", uri)
// Create a ClipEntry object from the clipData value
val clipEntry = ClipEntry(clipData)
// Copy the JPEG data to the clipboard
clipboardManager.setClip(clipEntry)
}
) {
Text("Copy a JPEG data")
}
リッチ コンテンツを貼り付ける
contentReceiver
修飾子を使用すると、リッチ コンテンツの貼り付けを処理できます。
変更されたコンポーネントの BasicTextField
にマッピングします。
次のコード スニペットは、貼り付けた画像データの URI を追加します。
Uri
オブジェクトのリストに追加します。
// A URI list of images
val imageList by remember{ mutableListOf<Uri>() }
// Remember the ReceiveContentListener object as it is created inside a Composable scope
val receiveContentListener = remember {
ReceiveContentListener { transferableContent ->
// Handle the pasted data if it is image data
when {
// Check if the pasted data is an image or not
transferableContent.hasMediaType(MediaType.Image)) -> {
// Handle for each ClipData.Item object
// The consume() method returns a new TransferableContent object containging ignored ClipData.Item objects
transferableContent.consume { item ->
val uri = item.uri
if (uri != null) {
imageList.add(uri)
}
// Mark the ClipData.Item object consumed when the retrieved URI is not null
uri != null
}
}
// Return the given transferableContent when the pasted data is not an image
else -> transferableContent
}
}
}
val textFieldState = rememberTextFieldState()
BasicTextField(
state = textFieldState,
modifier = Modifier
.contentReceiver(receiveContentListener)
.fillMaxWidth()
.height(48.dp)
)
contentReceiver
修飾子は ReceiveContentListener
オブジェクトを引数として受け取り、ユーザーが変更されたコンポーネント内の BasicTextField
にデータを貼り付けると、渡されたオブジェクトの onReceive
メソッドを呼び出します。
TransferableContent
オブジェクトが onReceive メソッドに渡されます。このメソッドは、この場合は貼り付けによってアプリ間で転送できるデータを記述します。ClipEntry
オブジェクトにアクセスするには、clipEntry
属性を参照します。
ユーザーが複数の画像を選択してクリップボードにコピーする場合など、ClipEntry
オブジェクトには複数の ClipData.Item
オブジェクトを含めることができます。各 ClipData.Item
オブジェクトに消費済みまたは無視済みとしてマークを付け、無視された ClipData.Item
オブジェクトを含む TransferableContent
を返して、最も近い祖先の contentReceiver
修飾子が受け取れるようにする必要があります。
TransferableContent.hasMediaType()
メソッドを使用すると、
TransferableContent
オブジェクトがアイテムを提供できるかどうか
指定する必要があります
たとえば、次のメソッド呼び出しは、TransferableContent
オブジェクトが画像を提供できる場合に true
を返します。
transferableContent.hasMediaType(MediaType.Image)
複雑なデータの操作
複雑なデータをクリップボードにコピーできる 方法はリッチコンテンツの場合と同じです 詳しくは、コンテンツ プロバイダを使用して複雑なデータをコピーするをご覧ください。
リッチ コンテンツの場合と同様に、複雑なデータの貼り付けを処理することもできます。貼り付けたデータの URI を取得できます。実際のデータは ContentProvider
から取得できます。
詳しくは、プロバイダからデータを取得するをご覧ください。
コンテンツのコピーに関するフィードバック
クリップボードにコンテンツをコピーする際に、ユーザーはフィードバックを期待します。 そのためコピー&ペーストの基盤となる フレームワークに加え Android 13(API レベル 33)でコピーすると、Android でデフォルトの UI が表示される 以上です。この機能により、通知が重複するリスクがあります。このエッジケースの詳細については、重複する通知を回避するをご覧ください。
Android 12L(API レベル 32)以前でコピーする場合は、手動でフィードバックを提供します。推奨事項をご覧ください。
デリケートなコンテンツ
パスワードなどの機密コンテンツをユーザーがクリップボードにコピーできるようにする場合は、アプリがシステムに通知して、コピーされた機密コンテンツが UI に表示されないようにする必要があります(図 2)。
ClipboardManager
オブジェクトで setClip()
メソッドを呼び出す前に、ClipData
の ClipDescription
にフラグを追加する必要があります。
// 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)
}
}