복사하여 붙여넣기

복사하여 붙여넣기를 위한 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")
    }
}

리치 콘텐츠 사용

사용자는 이미지, 동영상, 기타 생생한 콘텐츠를 좋아합니다. 앱에서 사용자가 ClipboardManagerClipEntry를 사용하여 리치 콘텐츠를 복사할 수 있도록 할 수 있습니다. 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 13 클립보드 알림을 보여주는 애니메이션
그림 1. Android에서 콘텐츠가 클립보드에 들어갈 때 표시되는 UI 13 이상

사용자에게 수동으로 의견 제공 Android 12L (API 수준 32) 이하에서 복사할 때 권장사항을 참고하세요.

민감한 콘텐츠

앱에서 사용자가 비밀번호와 같은 민감한 콘텐츠를 클립보드에 복사하도록 허용하려는 경우 앱은 시스템이 복사된 민감한 콘텐츠를 UI에 표시하지 않도록 시스템에 알려야 합니다(그림 2).

민감한 콘텐츠를 신고하는 복사된 텍스트 미리보기
그림 2. 민감한 콘텐츠 플래그가 포함된 복사된 텍스트 미리보기

ClipDataClipDescription에 플래그를 추가해야 합니다. 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)
    }
}