복사하여 붙여넣기를 위한 Android 클립보드 기반 프레임워크 는 다음을 포함하여 원시 및 복합 데이터 유형을 지원합니다.
- 텍스트 문자열
- 복잡한 데이터 구조
- 텍스트 및 바이너리 스트림 데이터
- 애플리케이션 애셋
간단한 텍스트 데이터는 클립보드에 직접 저장되며 복잡한 데이터는 참조로 저장되며 콘텐츠 제공자로 확인되는 ID입니다.
복사 및 붙여넣기는 애플리케이션 내에서뿐 아니라 프레임워크를 구현하는 여러 애플리케이션 사이에서 모두 작동합니다.
프레임워크의 일부에서 콘텐츠 제공자를 사용하므로 이 문서에서는 Android Content Provider API에 어느 정도 익숙하다고 가정합니다.
텍스트 작업
일부 구성요소는 다음에서 볼 수 있듯이 텍스트를 바로 복사하여 붙여넣을 수 있습니다. 다음 표를 참조하세요.
구성요소 | 텍스트 복사 | 텍스트 붙여넣기 |
---|---|---|
BasicTextField | ✅ | ✅ |
텍스트입력란 | ✅ | ✅ |
선택 컨테이너 | ✅ |
예를 들어 다음 스니펫에서 카드의 텍스트를 클립보드에 복사하고 복사한 텍스트를 TextField
에 붙여넣을 수 있습니다.
텍스트를 붙여넣으려면 메뉴를
터치 및 TextField
를 길게 누르거나 커서 핸들을 탭하면 됩니다.
val textFieldState = rememberTextFieldState()
Column {
Card {
SelectionContainer {
Text("You can copy this text")
}
}
BasicTextField(state = textFieldState)
}
텍스트를 붙여넣으려면 다음 단축키를 사용하세요. Ctrl+V 단축키는 기본적으로 사용할 수도 있습니다. 자세한 내용은 키보드 작업 처리를 참고하세요.
ClipboardManager
로 복사
ClipboardManager
를 사용하여 텍스트를 클립보드에 복사할 수 있습니다.
setText() 메서드는
클립보드에 전달됩니다.
다음 스니펫은 사용자가 버튼을 클릭하면 '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()
메서드는 문자열 객체에서 ClipData
객체를 만드는 편의 메서드입니다.
ClipboardManager
객체에서 setClip() 메서드를 호출하여 생성된 ClipEntry
객체를 클립보드로 설정할 수 있습니다.
// 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를 사용하여 붙여넣기
클립보드에 복사된 텍스트에 액세스할 수 있습니다.
getText()
메서드 호출
ClipboardManager
동안
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
객체에 대해 소비됨 또는 무시됨을 표시하고 가장 가까운 조상 contentReceiver
수정자가 수신할 수 있도록 무시된 ClipData.Item
객체가 포함된 TransferableContent
를 반환해야 합니다.
TransferableContent.hasMediaType()
메서드를 사용하면 TransferableContent
객체가 미디어 유형이 있는 항목을 제공할 수 있는지 확인할 수 있습니다.
예를 들어 다음 메서드 호출은 true
를 반환합니다.
TransferableContent
객체가 이미지를 제공할 수 있는 경우
transferableContent.hasMediaType(MediaType.Image)
복잡한 데이터 작업
리치 콘텐츠와 동일한 방식으로 복잡한 데이터를 클립보드에 복사할 수 있습니다. 자세한 내용은 콘텐츠 제공업체를 사용하여 복잡한 데이터 복사를 참고하세요.
또한 복잡한 데이터의 붙여넣기를 처리할 수 있습니다.
리치 콘텐츠와 같은 방식으로 사용할 수 있습니다.
붙여넣은 데이터의 URI를 수신할 수 있습니다.
실제 데이터는 ContentProvider
에서 가져올 수 있습니다.
자세한 내용은 제공업체에서 데이터 검색을 참고하세요.
콘텐츠 복사 관련 의견
사용자는 콘텐츠를 클립보드에 복사할 때 피드백을 받기를 기대합니다. 복사하여 붙여넣기를 지원하는 프레임워크 외에도 Android는 사용자가 Android 13 (API 수준 33)에서 복사할 때 사용자에게 기본 UI를 표시합니다. 및 그 이상입니다. 이 기능으로 인해 알림이 중복으로 표시될 수 있습니다. 이 특이 사례에 관한 자세한 내용은 중복 알림 방지를 참고하세요.
사용자에게 수동으로 의견 제공 Android 12L (API 수준 32) 이하에서 복사할 때 권장사항을 참고하세요.
민감한 콘텐츠
앱에서 사용자가 비밀번호와 같은 민감한 콘텐츠를 클립보드에 복사하도록 허용하려는 경우 앱은 시스템이 복사된 민감한 콘텐츠를 UI에 표시하지 않도록 시스템에 알려야 합니다(그림 2).
ClipData
의 ClipDescription
에 플래그를 추가해야 합니다.
ClipboardManager
객체를 통해 setClip()
메서드를 호출하기 전에 다음을 실행합니다.
// 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)
}
}