Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

복사하여 붙여넣기

Android는 복사하여 붙여넣기를 지원하는 강력한 클립보드 기반 프레임워크를 제공합니다. 텍스트 문자열, 복잡한 데이터 구조, 텍스트 및 바이너리 스트림 데이터, 애플리케이션 애셋을 포함하여 단순한 데이터 유형과 복잡한 데이터 유형을 모두 지원합니다. 단순한 텍스트 데이터는 클립보드에 직접 저장되며 복잡한 데이터는 붙여넣기 애플리케이션이 콘텐츠 제공업체를 통해 확인하는 참조로 저장됩니다. 하나의 애플리케이션 내에서뿐 아니라 프레임워크를 구현하는 여러 애플리케이션 사이에서 모두 복사하여 붙여넣기를 할 수 있습니다.

프레임워크의 일부에서 콘텐츠 제공자를 사용하므로 이 주제에서는 콘텐츠 제공자 주제에서 설명된 Android Content Provider API를 어느 정도 알고 있다고 가정합니다.

클립보드 프레임워크

클립보드 프레임워크를 사용할 때 데이터를 클립 객체에 넣은 다음 클립 객체를 시스템 전체 클립보드에 배치합니다. 클립 객체는 다음 세 가지 형식 중 하나일 수 있습니다.

텍스트
텍스트 문자열입니다. 문자열을 클립 객체에 직접 넣은 다음 클립보드에 배치합니다. 문자열을 붙여넣으려면 클립보드에서 클립 객체를 가져와서 문자열을 애플리케이션의 저장소에 복사합니다.
URI
모든 URI 형식을 표시하는 Uri 객체입니다. 주로 콘텐츠 제공업체의 복잡한 데이터를 복사하기 위한 것입니다. 데이터를 복사하려면 Uri 객체를 클립 객체에 넣고 클립 객체를 클립보드에 배치합니다. 데이터를 붙여넣으려면 클립 객체와 Uri 객체를 가져와서 콘텐츠 제공업체와 같은 데이터 소스로 확인하고 소스에서 애플리케이션의 저장소로 데이터를 복사합니다.
인텐트
Intent입니다. 애플리케이션 바로가기 복사를 지원합니다. 데이터를 복사하려면 인텐트를 만들고 클립 객체에 넣은 다음 클립 객체를 클립보드에 배치합니다. 데이터를 붙여넣으려면 클립 객체를 가져온 다음 인텐트 객체를 애플리케이션의 메모리 영역으로 복사합니다.

클립보드는 한 번에 클립 객체 하나만 보유합니다. 애플리케이션에서 클립 객체를 클립보드에 배치하면 이전 클립 객체는 사라집니다.

사용자가 애플리케이션에 데이터를 붙여넣도록 허용하려면 모든 데이터 유형을 처리할 필요가 없습니다. 클립보드의 데이터를 검사한 후 사용자에게 붙여넣기 옵션을 제공할 수 있습니다. 클립 객체에는 특정 데이터 형식 외에도 사용 가능한 MIME 유형을 알려주는 메타데이터도 포함됩니다. 메타데이터를 통해 애플리케이션이 클립보드 데이터로 유용한 작업을 실행할 수 있는지 판단할 수 있습니다. 예를 들어 텍스트를 주로 처리하는 애플리케이션이 있다면 URI 또는 인텐트가 포함된 클립 객체를 무시하는 것이 좋습니다.

사용자가 클립보드의 데이터 형식에 상관없이 텍스트를 붙여넣도록 하는 것도 좋습니다. 이렇게 하려면 클립보드 데이터를 텍스트 표현으로 강제 변환한 다음 이 텍스트를 붙여넣으면 됩니다. 이 방법은 클립보드를 텍스트로 강제 변환 섹션에 설명되어 있습니다.

클립보드 클래스

이 섹션에서는 클립보드 프레임워크에서 사용하는 클래스를 설명합니다.

ClipboardManager

Android 시스템에서 시스템 클립보드는 전역 ClipboardManager 클래스로 표시됩니다. 이 클래스를 직접 인스턴스화하지 마세요. 대신 getSystemService(CLIPBOARD_SERVICE)를 호출하여 클래스 참조를 가져옵니다.

ClipData, ClipData.Item, ClipDescription

데이터를 클립보드에 추가하려면 데이터 설명과 데이터 자체를 모두 포함하는 ClipData 객체를 만드세요. 클립보드는 한 번에 ClipData 하나만 보유합니다. ClipData에는 ClipDescription 객체와 하나 이상의 ClipData.Item 객체가 포함됩니다.

ClipDescription 객체에는 클립에 관한 메타데이터가 포함됩니다. 특히 클립 데이터에 사용할 수 있는 MIME 유형의 배열이 포함됩니다. 클립보드에 클립을 배치하면 이 배열을 붙여넣기 애플리케이션에서 사용할 수 있습니다. 붙여넣기 애플리케이션은 이 배열을 검사하여 사용 가능한 MIME 유형을 처리할 수 있는지 확인할 수 있습니다.

ClipData.Item 객체에는 텍스트, URI 또는 인텐트 데이터가 포함됩니다.

텍스트
CharSequence입니다.
URI
Uri입니다. 모든 URI가 허용되지만 일반적으로 콘텐츠 제공업체 URI가 포함됩니다. 데이터를 제공하는 애플리케이션은 URI를 클립보드에 배치합니다. 데이터를 붙여넣으려는 애플리케이션은 클립보드에서 URI를 가져와서 콘텐츠 제공업체(또는 다른 데이터 소스)에 액세스하여 데이터를 검색하는 데 사용합니다.
인텐트
Intent입니다. 이 데이터 유형을 사용하면 애플리케이션 바로가기를 클립보드에 복사할 수 있습니다. 그러면 사용자가 바로가기를 애플리케이션에 붙여넣어 나중에 사용할 수 있습니다.

클립에 ClipData.Item 객체를 두 개 이상 추가할 수 있습니다. 이렇게 하면 사용자가 여러 선택사항을 단일 클립으로 복사하여 붙여넣기 할 수 있습니다. 예를 들어 사용자가 항목을 한 번에 두 개 이상 선택할 수 있는 목록 위젯이 있다면 클립보드에 모든 항목을 한 번에 복사할 수 있습니다. 이 작업을 실행하려면 각 목록 항목에 별도의 ClipData.Item을 만든 다음 ClipData.Item 객체를 ClipData 객체에 추가합니다.

ClipData 편의 메서드

ClipData 클래스는 단일 ClipData.Item 객체 및 단순한 ClipDescription 객체를 사용하여 ClipData 객체를 만들기 위한 정적 편의 메서드를 제공합니다.

newPlainText(label, text)
단일 ClipData.Item 객체에 텍스트 문자열이 포함되는 ClipData 객체를 반환합니다. ClipDescription 객체의 라벨이 label로 설정됩니다. ClipDescription의 단일 MIME 유형은 MIMETYPE_TEXT_PLAIN입니다.

newPlainText()를 사용하여 텍스트 문자열에서 클립을 만드세요.

newUri(resolver, label, URI)
단일 ClipData.Item 객체에 URI가 포함되는 ClipData 객체를 반환합니다. ClipDescription 객체의 라벨이 label로 설정됩니다. URI가 콘텐츠 URI(Uri.getScheme()content: 반환)면 메서드는 resolver에서 제공한 ContentResolver 객체를 사용하여 콘텐츠 제공업체에서 사용 가능한 MIME 유형을 검색하고 ClipDescription에 저장합니다. content: URI가 아닌 URI의 경우 메서드는 MIME 유형을 MIMETYPE_TEXT_URILIST로 설정합니다.

newUri()를 사용하여 URI, 특히 content: URI에서 클립을 만드세요.

newIntent(label, intent)
단일 ClipData.Item 객체에 Intent가 포함되는 ClipData 객체를 반환합니다. ClipDescription 객체의 라벨이 label로 설정됩니다. MIME 유형이 MIMETYPE_TEXT_INTENT로 설정됩니다.

newIntent()를 사용하여 인텐트 객체에서 클립을 만드세요.

클립보드 데이터를 텍스트로 강제 변환

애플리케이션에서 텍스트만 처리한다고 해도 텍스트가 아닌 데이터를 메서드 ClipData.Item.coerceToText()로 변환하여 클립보드에서 복사할 수 있습니다.

이 메서드는 ClipData.Item의 데이터를 텍스트로 변환하여 CharSequence를 반환합니다. ClipData.Item.coerceToText()가 반환하는 값은 ClipData.Item의 데이터 형식에 기반합니다.

텍스트
ClipData.Item이 텍스트(getText()는 null이 아님)인 경우 coerceToText()는 텍스트를 반환합니다.
URI
ClipData.Item이 URI(getUri()는 null이 아님)인 경우 coerceToText()가 콘텐츠 URI로 사용하려고 합니다.
  • URI가 콘텐츠 URI이고 제공업체가 텍스트 스트림을 반환할 수 있다면 coerceToText()는 텍스트 스트림을 반환합니다.
  • URI가 콘텐츠 URI이지만 제공업체가 텍스트 스트림을 제공하지 않는다면 coerceToText()는 URI 표시를 반환합니다. 표시는 Uri.toString()에서 반환한 것과 동일합니다.
  • URI가 콘텐츠 URI가 아니라면 coerceToText()는 URI 표시를 반환합니다. 표시는 Uri.toString()에서 반환한 것과 동일합니다.
인텐트
ClipData.Item이 인텐트(getIntent()는 null이 아님)인 경우 coerceToText()는 인텐트 URI로 변환하여 반환합니다. 표시는 Intent.toUri(URI_INTENT_SCHEME)에서 반환한 것과 동일합니다.

클립보드 프레임워크가 그림 1에 요약되어 있습니다. 데이터를 복사하기 위해 애플리케이션은 ClipData 객체를 ClipboardManager 전역 클립보드에 배치합니다. ClipData에는 ClipData.Item 객체 하나 이상과 ClipDescription 객체 하나가 포함됩니다. 데이터를 붙여넣기 위해 애플리케이션은 ClipData를 가져오고 ClipDescription에서 MIME 유형을 가져오며 ClipData.Item 또는 ClipData.Item에서 참조하는 콘텐츠 제공업체에서 데이터를 가져옵니다.

복사하여 붙여넣기 프레임워크의 블록 다이어그램

그림 1. Android 클립보드 프레임워크

클립보드에 복사

앞서 설명한 것처럼 클립보드에 데이터를 복사하려면 전역 ClipboardManager 객체의 핸들을 가져와서 ClipData 객체를 만들고 ClipDescription 및 하나 이상의 ClipData.Item 객체를 추가한 다음 ClipboardManager 객체에 완성된 ClipData 객체를 추가하세요. 이 내용은 다음 절차에 자세히 설명되어 있습니다.

  1. 콘텐츠 URI를 사용하여 데이터를 복사하려면 콘텐츠 제공자를 설정하세요.

    노트 패드 샘플 애플리케이션은 복사하여 붙여넣기에 콘텐츠 제공자를 사용하는 예입니다. NotePadProvider 클래스는 콘텐츠 제공업체를 구현합니다. NotePad 클래스는 지원되는 MIME 유형을 포함하여 제공업체와 기타 애플리케이션 사이의 계약을 정의합니다.

  2. 시스템 클립보드 가져오기

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

    자바

    ...
    
    // if the user selects copy
    case R.id.menu_copy:
    
    // Gets a handle to the clipboard service.
    ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
  3. 새로운 ClipData 객체로 데이터 복사하기

    • 텍스트의 경우

      Kotlin

      // Creates a new text clip to put on the clipboard
      val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
      

      자바

      // Creates a new text clip to put on the clipboard
      ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
      
    • URI의 경우

      이 스니펫은 제공자의 콘텐츠 URI에 레코드 ID를 인코딩하여 URI를 구성합니다. 이 기법은 URI에 식별자 인코딩 섹션에서 더 자세히 다뤄집니다.

      Kotlin

      // Creates a Uri based on 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 that you use 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)
      

      자바

      // Creates a Uri based on 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 that you use 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);
      
    • 인텐트의 경우

      이 스니펫은 애플리케이션의 인텐트를 구성한 다음 클립 객체에 넣습니다.

      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)
      

      자바

      // 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);
      
  4. 새로운 클립 객체를 클립보드에 배치합니다.

    Kotlin

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip)
    

    자바

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip);
    

클립보드에서 붙여넣기

앞서 설명한 것처럼 전역 클립보드 객체와 클립 객체를 가져와서 데이터를 살펴본 다음 가능하다면 클립 객체의 데이터를 자체 저장소에 복사하여 클립보드에서 데이터를 붙여넣습니다. 이 섹션에서는 세 가지 클립보드 데이터 형식에 이 작업을 실행하는 방법을 자세히 설명합니다.

일반 텍스트 붙여넣기

일반 텍스트를 붙여넣으려면 먼저 전역 클립보드를 가져와서 일반 텍스트를 반환할 수 있는지 확인합니다. 그리고 다음 절차에서 설명하는 것처럼 클립 객체를 가져와서 getText()를 사용하여 텍스트를 자체 저장소에 복사합니다.

  1. getSystemService(CLIPBOARD_SERVICE)를 사용하여 전역 ClipboardManager 객체를 가져옵니다. 전역 변수를 선언하여 붙여넣은 텍스트도 포함합니다.

    Kotlin

    var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var pasteData: String = ""
    

    자바

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
    
  2. 다음으로 현재 활동에서 '붙여넣기' 옵션을 사용 설정 또는 사용 중지해야 하는지 확인합니다. 클립보드에 클립이 포함되어 있는지 클립이 표시하는 데이터 유형을 처리할 수 있는지 확인해야 합니다.

    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 if you can handle the data.
    pasteItem.isEnabled = when {
        !clipboard.hasPrimaryClip() -> {
            false
        }
        !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> {
            // This disables the paste menu item, since the clipboard has data but it is not plain text
            false
        }
        else -> {
            // This enables the paste menu item, since the clipboard contains plain text.
            true
        }
    }
    

    자바

    // 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 if you can handle the data.
    if (!(clipboard.hasPrimaryClip())) {
    
        pasteItem.setEnabled(false);
    
    } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {
    
        // This disables the paste menu item, since the clipboard has data but it is not plain text
        pasteItem.setEnabled(false);
    } else {
    
        // This enables the paste menu item, since the clipboard contains plain text.
        pasteItem.setEnabled(true);
    }
    
  3. 클립보드에서 데이터를 복사합니다. 프로그램의 이 지점은 '붙여넣기' 메뉴 항목이 사용 설정된 경우에만 도달할 수 있으므로 클립보드에 일반 텍스트가 포함되어 있다고 가정할 수 있습니다. 텍스트 문자열이나 일반 텍스트를 가리키는 URI가 포함되어 있는지는 아직 모릅니다. 다음 스니펫은 이를 테스트하지만 일반 텍스트를 처리하는 코드만 표시합니다.

    Kotlin

    when (menuItem.itemId) {
        ...
        R.id.menu_paste -> {    // Responds to the user selecting "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.
            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 does not 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 is not
                    // presented here.
                    pasteData = resolveUri(pasteUri)
                    true
                } else {
    
                    // Something is wrong. The MIME type was plain text, but the clipboard does not
                    // contain either text or a Uri. Report an error.
                    Log.e(TAG,"Clipboard contains an invalid data type")
                    false
                }
            }
        }
    }
    

    자바

    // 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 does not 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 is not
            // presented here.
            pasteData = resolveUri(Uri);
            return true;
        } else {
    
            // Something is wrong. The MIME type was plain text, but the clipboard does not contain either
            // text or a Uri. Report an error.
            Log.e(TAG, "Clipboard contains an invalid data type");
            return false;
        }
    }
    

콘텐츠 URI에서 데이터 붙여넣기

ClipData.Item 객체에 콘텐츠 URI가 포함되어 있고 MIME 유형 중 하나를 처리할 수 있다고 판단되면 ContentResolver를 만들고 적절한 콘텐츠 제공업체 메서드를 호출하여 데이터를 검색합니다.

다음 절차에서는 클립보드의 콘텐츠 URI에 기반한 콘텐츠 제공업체에서 데이터를 가져오는 방법을 설명합니다. 애플리케이션이 사용할 수 있는 MIME 유형이 제공업체에서 제공되는지 확인합니다.

  1. 전역 변수를 선언하여 MIME 유형을 포함합니다.

    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"
    

    자바

    // 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";
    
  2. 전역 클립보드 가져오기 또한 콘텐츠 리졸버를 가져와서 콘텐츠 제공업체에 액세스할 수 있습니다.

    Kotlin

    // Gets a handle to the Clipboard Manager
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
    // Gets a content resolver instance
    val cr = contentResolver
    

    자바

    // Gets a handle to the Clipboard Manager
    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    
    // Gets a content resolver instance
    ContentResolver cr = getContentResolver();
    
  3. 클립보드에서 기본 클립을 가져오고 그 콘텐츠를 URI로 가져옵니다.

    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
    

    자바

    // 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();
    
  4. getType(Uri)을 호출하여 URI가 콘텐츠 URI인지 테스트합니다. 이 메서드는 Uri가 유효한 콘텐츠 제공업체를 가리키지 않으면 null을 반환합니다.

    Kotlin

        // If the clipboard contains a URI reference
        pasteUri?.let {
    
            // Is this a content URI?
            val uriMimeType: String? = cr.getType(it)
    

    자바

        // If the clipboard contains a URI reference
        if (pasteUri != null) {
    
            // Is this a content URI?
            String uriMimeType = cr.getType(pasteUri);
    
  5. 콘텐츠 제공업체가 현재 애플리케이션이 이해하는 MIME 유형을 지원하는지 테스트합니다. 지원한다면 ContentResolver.query()를 호출하여 데이터를 가져옵니다. 반환값은 Cursor입니다.

    Kotlin

            // If the return value is not 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 will vary according to the
                        // format of the data model.
                    }
    
                    // Kotlin `use` will automatically close the Cursor
                }
            }
        }
    }
    

    자바

            // If the return value is not 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 will vary according to the
                        // format of the data model.
                        }
                    }
    
                    // close the Cursor
                    pasteCursor.close();
                 }
             }
         }
    }
    

인텐트 붙여넣기

인텐트를 붙여넣으려면 먼저 전역 클립보드를 가져옵니다. ClipData.Item 객체를 검사하여 인텐트가 포함되어 있는지 확인합니다. 그런 다음 getIntent()를 호출하여 인텐트를 자체 저장소에 복사합니다. 다음 스니펫은 이 작업을 보여줍니다.

Kotlin

// Gets a handle to the Clipboard Manager
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

// Checks to see if the clip item contains an Intent, by testing to see if 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 your application was expecting an Intent to be
    // on the clipboard
}

자바

// Gets a handle to the Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

// Checks to see if the clip item contains an Intent, by testing to see if 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 your application was expecting an Intent to be
    // on the clipboard
}

콘텐츠 제공업체를 사용하여 복잡한 데이터 복사

콘텐츠 제공업체는 데이터베이스 레코드나 파일 스트림과 같은 복잡한 데이터의 복사를 지원합니다. 데이터를 복사하려면 콘텐츠 URI를 클립보드에 배치합니다. 그러면 붙여넣기 애플리케이션이 클립보드에서 URI를 가져와서 데이터베이스 데이터나 파일 스트림 설명어를 검색하는 데 사용합니다.

붙여넣기 애플리케이션에는 데이터의 콘텐츠 URI만 있으므로 어떤 데이터 조각을 검색할지 알아야 합니다. URI 자체에 데이터 식별자를 인코딩하여 이 정보를 제공하거나 복사하려는 데이터를 반환하는 고유한 URI를 제공할 수 있습니다. 데이터 구성에 따라 선택하는 기법이 달라집니다.

다음 섹션에서는 URI를 설정하는 방법과 복잡한 데이터 및 파일 스트림을 제공하는 방법을 설명합니다. 설명에서는 개발자가 콘텐츠 제공업체 디자인의 일반 원칙을 잘 알고 있다고 가정합니다.

URI에 식별자 인코딩

URI를 사용하여 클립보드에 데이터를 복사하는 유용한 기법은 URI 자체에 데이터 식별자를 인코딩하는 것입니다. 그러면 콘텐츠 제공업체가 URI에서 식별자를 가져와서 데이터 검색에 사용할 수 있습니다. 붙여넣기 애플리케이션은 식별자가 있는지 알 필요가 없습니다. 해야 할 작업은 클립보드에서 '참조'(URI와 식별자)를 가져와서 콘텐츠 제공업체에 제공하고 데이터를 다시 가져오는 것뿐입니다.

일반적으로 식별자를 URI 끝에 연결하여 콘텐츠 URI에 식별자를 인코딩합니다. 예를 들어 제공업체 URI를 다음 문자열로 정의한다고 가정해보겠습니다.

"content://com.example.contacts"

이 URI에 이름을 인코딩하려면 다음 스니펫을 사용할 수 있습니다.

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)

자바

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);

콘텐츠 제공업체를 이미 사용하고 있다면 URI가 복사용인 것을 표시하는 새로운 URI 경로를 추가하는 것이 좋습니다. 예를 들어 다음 URI 경로가 이미 있다고 가정해보겠습니다.

"content://com.example.contacts"/people
"content://com.example.contacts"/people/detail
"content://com.example.contacts"/people/images

URI 복사에 특수한 또 다른 경로를 추가할 수 있습니다.

"content://com.example.contacts/copying"

그런 다음 패턴 일치를 통해 '복사' URI를 감지하고 복사하여 붙여넣기용 특수 코드를 사용하여 처리할 수 있습니다.

이미 콘텐츠 제공업체나 내부 데이터베이스, 내부 테이블을 사용하여 데이터를 체계화하는 경우 일반적으로 인코딩 기법을 사용합니다. 이러한 경우 복사하려는 여러 데이터 조각이 있고 각 조각에 고유한 식별자가 있을 수 있습니다. 붙여넣기 애플리케이션의 쿼리에 대한 응답으로 데이터를 식별자로 찾아서 반환할 수 있습니다.

여러 데이터 조각이 없으면 식별자를 인코딩하지 않아도 됩니다. 제공업체에 고유한 URI를 간단히 사용할 수 있습니다. 쿼리에 대한 응답으로 제공업체는 현재 포함된 데이터를 반환할 수 있습니다.

ID로 단일 레코드 가져오기는 노트 패드 샘플 애플리케이션이 메모 목록에서 메모를 여는 데 사용합니다. 이 샘플에서는 SQL 데이터베이스의 _id 필드를 사용하지만 원하는 숫자 또는 문자 식별자를 모두 가질 수 있습니다.

데이터 구조 복사

복잡한 데이터를 복사하여 붙여넣기 위해 콘텐츠 제공업체를 ContentProvider 구성요소의 서브클래스로 설정합니다. 클립보드에 배치한 URI도 인코딩하여 제공하려는 정확한 레코드를 가리키도록 해야 합니다. 또한 애플리케이션의 기존 상태를 고려해야 합니다.

  • 콘텐츠 제공업체가 이미 있는 경우 기능에 추가할 수 있습니다. 데이터를 붙여넣으려는 애플리케이션에서 나오는 URI를 처리하기 위해 query() 메서드만 수정해야 할 수 있습니다. 메서드를 수정하여 '복사' URI 패턴을 처리하는 것이 좋습니다.
  • 애플리케이션에서 내부 데이터베이스를 유지 관리하는 경우 이 데이터베이스를 콘텐츠 제공업체로 이동하여 복사를 용이하게 하는 것이 좋습니다.
  • 현재 데이터베이스를 사용하지 않는 경우 클립보드에서 붙여넣기 하는 애플리케이션에 데이터를 제공하는 것이 유일한 목적인 단순한 콘텐츠 제공업체를 구현할 수 있습니다.

콘텐츠 제공업체에서 적어도 다음 메서드를 재정의하는 것이 좋습니다.

query()
붙여넣기 애플리케이션은 클립보드에 배치한 URI와 함께 이 메서드를 사용하여 데이터를 가져올 수 있다고 가정합니다. 복사를 지원하려면 이 메서드에서 특수한 '복사' 경로가 포함된 URI를 감지해야 합니다. 그런 다음 애플리케이션에서 '복사' URI를 만들어 복사 경로와 복사하려는 정확한 레코드의 포인터를 포함하는 클립보드에 배치할 수 있습니다.
getType()
이 메서드는 복사하려는 데이터의 MIME 유형을 반환해야 합니다. MIME 유형을 새로운 ClipData 객체에 넣기 위해 메서드 newUri()getType()을 호출합니다.

복잡한 데이터의 MIME 유형은 콘텐츠 제공자 주제에 설명되어 있습니다.

insert() 또는 update()와 같은 다른 콘텐츠 제공업체 메서드를 보유할 필요는 없습니다. 붙여넣기 애플리케이션은 지원되는 MIME 유형만 가져와서 제공업체의 데이터를 복사하면 됩니다. 이러한 메서드가 이미 있다면 복사 작업을 방해하지 않습니다.

다음 스니펫은 애플리케이션을 설정하여 복잡한 데이터를 복사하는 방법을 보여줍니다.

  1. 애플리케이션의 전역 상수에서 데이터를 복사하는 데 사용하는 URI 문자열을 식별하는 경로와 기본 URI 문자열을 선언합니다. 복사된 데이터의 MIME 유형도 선언합니다.

    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"
    

    자바

    // 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";
    
  2. 사용자가 데이터를 복사하는 활동에서 코드를 설정하여 데이터를 클립보드에 복사합니다. 복사 요청에 대한 응답으로 클립보드에 URI를 배치합니다.

    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)
    
            // Set the clipboard's primary clip.
            clipboard.setPrimaryClip(clip)
        }
    }
    

    자바

    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);
    
        // Set the clipboard's primary clip.
        clipboard.setPrimaryClip(clip);
    
  3. 콘텐츠 제공업체의 전역 범위에서 URI 매처를 만들고 클립보드에 배치한 URI와 일치할 URI 패턴을 추가하세요.

    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() {
        ...
    }
    

    자바

    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);
    
  4. query() 메서드를 설정합니다. 이 메서드는 코딩 방법에 따라 다양한 URI 패턴을 처리할 수 있지만 클립보드 복사 작업의 패턴만 표시됩니다.

    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 -> {
    
                // query and return the contact for the requested name. Here you would decode
                // the incoming URI, query the data model based on the last name, and return the result
                // as a Cursor.
            }
        }
    
        ...
    
    }
    

    자바

    // 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:
    
            // query and return the contact for the requested name. Here you would decode
            // the incoming URI, query the data model based on the last name, and return the result
            // as a Cursor.
    
        ...
    
    }
    
  5. getType() 메서드를 설정하여 복사된 데이터에 적절한 MIME 유형을 반환하세요.

    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
            ...
        }
    }
    

    자바

    // Sets up your provider's getType() method.
    public String getType(Uri uri) {
        ...
    
        switch (sUriMatcher.match(uri)) {
        case GET_SINGLE_CONTACT:
            return (MIME_TYPE_CONTACT);
        ...
        }
    }
    

콘텐츠 URI에서 데이터 붙여넣기 섹션에서는 클립보드에서 콘텐츠 URI를 가져와서 데이터를 가져오고 붙여넣는 데 사용하는 방법을 설명합니다.

데이터 스트림 복사

대량의 텍스트 및 바이너리 데이터를 스트림으로 복사하여 붙여넣기 할 수 있습니다. 데이터는 다음과 같은 형식일 수 있습니다.

  • 실제 기기에 저장된 파일
  • 소켓 스트림
  • 제공업체의 기본 데이터베이스 시스템에 저장된 대량의 데이터

데이터 스트림용 콘텐츠 제공업체는 Cursor 객체가 아닌 AssetFileDescriptor와 같은 파일 설명자 객체를 사용하여 데이터에 액세스할 수 있도록 합니다. 붙여넣기 애플리케이션은 파일 설명자를 사용하여 데이터 스트림을 읽습니다.

애플리케이션을 설정하여 제공업체와 함께 데이터 스트림을 복사하려면 다음 단계를 따르세요.

  1. 클립보드에 배치하는 데이터 스트림의 콘텐츠 URI를 설정합니다. 이 작업 실행의 옵션에는 다음이 포함됩니다.
    • URI에 식별자 인코딩 섹션에서 설명한 것처럼 URI에 데이터 스트림의 식별자를 인코딩하고 식별자와 그에 상응하는 스트림 이름이 포함된 테이블을 제공업체에 유지합니다.
    • 스트림 이름을 URI에 직접 인코딩합니다.
    • 항상 제공업체의 현재 스트림을 반환하는 고유한 URI를 사용합니다. 이 옵션을 사용하는 경우 URI를 통해 클립보드에 스트림을 복사할 때마다 제공업체를 업데이트하여 다른 스트림을 가리키도록 해야 합니다.
  2. 제공하려는 각 데이터 스트림 유형에 MIME 유형을 제공합니다. 붙여넣기 애플리케이션에는 데이터를 클립보드에 붙여넣을 수 있는지 확인하기 위해 이 정보가 필요합니다.
  3. 스트림의 파일 설명자를 반환하는 ContentProvider 메서드 중 하나를 구현합니다. 콘텐츠 URI에 식별자를 인코딩한다면 이 메서드를 사용하여 어떤 스트림을 열 것인지 확인합니다.
  4. 데이터 스트림을 클립보드에 복사하려면 콘텐츠 URI를 구성하여 클립보드에 배치합니다.

데이터 스트림을 붙여넣으려면 애플리케이션은 클립보드에서 클립을 가져오고 URI를 가져와서 스트림을 여는 ContentResolver 파일 설명자 메서드를 호출하는 데 사용합니다. ContentResolver 메서드는 상응하는 ContentProvider 메서드를 호출하여 콘텐츠 URI를 전달합니다. 제공업체는 파일 설명자를 ContentResolver 메서드에 반환합니다. 그러면 붙여넣기 애플리케이션은 스트림에서 데이터를 읽어야 합니다.

다음 목록은 콘텐츠 제공업체의 가장 중요한 파일 설명자 메서드를 보여줍니다. 각각에는 메서드 이름에 추가된 문자열 '설명어'와 함께 상응하는 ContentResolver 메서드가 있습니다. 예를 들어 openAssetFile()ContentResolver 아날로그는 openAssetFileDescriptor()입니다.

openTypedAssetFile()
이 메서드는 제공된 MIME 유형을 제공업체에서 지원하는 경우에만 애셋 파일 설명자를 반환해야 합니다. 호출자(붙여넣기를 실행하는 애플리케이션)는 MIME 유형 패턴을 제공합니다. URI를 클립보드에 복사한 애플리케이션의 콘텐츠 제공업체는 MIME 유형을 제공할 수 있는 경우 AssetFileDescriptor 파일 핸들을 반환하고 제공할 수 없는 경우 예외를 발생시킵니다.

이 메서드는 파일의 하위 섹션을 처리합니다. 이 메서드를 사용하여 콘텐츠 제공업체가 클립보드에 복사한 애셋을 읽을 수 있습니다.

openAssetFile()
이 메서드는 더 일반적인 형식의 openTypedAssetFile()입니다. 허용된 MIME 유형은 필터링하지 않지만 파일의 하위 섹션은 읽을 수 있습니다.
openFile()
더 일반적인 형식의 openAssetFile()입니다. 파일의 하위 섹션을 읽을 수 없습니다.

파일 설명자 메서드와 함께 선택적으로 openPipeHelper() 메서드를 사용할 수 있습니다. 이렇게 하면 붙여넣기 애플리케이션이 파이프를 사용하여 백그라운드 스레드에서 스트림 데이터를 읽을 수 있습니다. 이 메서드를 사용하려면 ContentProvider.PipeDataWriter 인터페이스를 구현해야 합니다. 이 작업의 실행 예는 노트 패드 샘플 애플리케이션에서 NotePadProvider.javaopenTypedAssetFile() 메서드로 제공됩니다.

효과적인 복사/붙여넣기 기능 디자인

애플리케이션에 효과적인 복사하여 붙여넣기 기능을 디자인하려면 다음 사항에 유의하세요.

  • 언제나 클립보드에는 클립이 하나만 있습니다. 시스템의 모든 애플리케이션에서 실행하는 새로운 복사 작업은 이전 클립을 덮어씁니다. 사용자가 애플리케이션에서 이동하여 돌아오기 전에 복사할 수 있으므로 클립보드에 사용자가 개발자의 애플리케이션에서 이전에 복사한 클립이 포함되어 있다고 가정할 수 없습니다.
  • 클립당 여러 ClipData.Item 객체의 용도는 다양한 형식의 단일 선택 참조가 아니라 여러 선택의 복사하여 붙여넣기를 지원하는 것입니다. 개발자는 일반적으로 클립의 ClipData.Item 객체가 모두 동일한 형식이기를 바랍니다. 즉, 모두 단순하게 텍스트나 콘텐츠 URI, Intent여야 하고 혼합되어서는 안 됩니다.
  • 데이터를 제공할 때 다양한 MIME 표시를 제공할 수 있습니다. 지원하는 MIME 유형을 ClipDescription에 추가하고 콘텐츠 제공업체에서 MIME 유형을 구현하세요.
  • 클립보드에서 데이터를 가져올 때 애플리케이션은 사용 가능한 MIME 유형을 확인하고 사용할 MIME 유형을 판단해야 합니다. 클립보드에 클립이 있고 사용자가 붙여넣기를 요청하더라도 애플리케이션은 붙여넣기를 실행할 필요가 없습니다. MIME 유형이 호환되는 경우 개발자는 붙여넣기를 실행해야 합니다. 원한다면 coerceToText()를 사용하여 클립보드의 데이터를 텍스트로 강제 변환할 수도 있습니다. 애플리케이션이 사용 가능한 MIME 유형을 둘 이상 지원하는 경우 사용자가 사용할 유형을 선택할 수 있습니다.