프로그래밍/안드로이드

안드로이드 - 시스템 클립보드를 활용한 데이터 복사 붙여넣기

가카리 2015. 11. 7. 14:39
반응형

 

클립보드는 임의의 응용 프로그램끼리 데이터를 교환하는 가장 기본적인 방법이다.

 

이것은 드래그&드롭에 의한 데이터 교환을 위해 범용적인 데이터 포맷을 정의할 필요도 있는데 클립보드 포맷이 그 역할을 맡게 된다.

 

시스템 클립보드는 운영체제가 관리하는 임시적인 자료 저장소이다. 임의의 프로그램이 클립보드를 자유롭게 액세스 할 수 있으므로 프로그램 내부의

 

뷰끼리는 물론이고 응용 프로그램끼리도 약속된 방법으로 데이터를 교환할 수 있다.

 

시스템 클립보드는 다음 4가지 포맷을 지원한다.

 

    1. 텍스트 : 가장 일반적인 교환 대상

    2. URI : 복잡한 형태의 데이터는 CP가 제공하며 데이터의 위치를 가리키는 URI를 클립보드에 저장한다.

    3. 인텐트 : 앱을 실행하는 명령과 관련 데이터를 클립도르를 통해 전달한다. 액티비티나 서비스 등에 대한 바로 가기를 교환할 때 유용하다.

    4. HTML 텍스트 : 단순 문자열이 아닌 서식 있는 문자열을 교환한다.

 

클립보드는 시스템 전역적인 자원이다. 운영체제를 전체를 통틀어 딱 하나밖에 없으며 클립보드에 저장되는 클립 데이터도 하나 뿐이다.

 

클립보드를 통한 데이터 교환은 일회적이고 임시적인 것이라 한 번에 하나의 데이터만 교환할 수 있다.

 

클립 데이터에는 데이터뿐만 아니라 데이터의 성격을 명시하는 설명이 포함되어 있다. 설명에는 데이터의 이름인 레이블과 형태를 지정하는 MIME 타입의 배열이 저장된다.

 

응용프로그램은 MIME타입을 통해 클립보드에 저장된 데이터가 문자열인지 URI인지를 판별한다. 텍스트를 붙여 넣은 프로그램에게 클립보드의 인텐트 정보는 무용지물이다.

 

ClipboardManager

 

위 클래스는 시스템 클립보드를 관리하며 클립보드 입출력을 담당한다. 시스템 서비스이므로 객체를 따로 생성할 필요는 없으며 getSystemService메소드로 언제든지 구할 수 있다.

 

다음 메소드로 클립 데이터가 있는지 조사하고 클립데이터를 읽거나 쓴다.

 

    boolean hasPrimaryClip()

    ClipData getPrimaryClip()

    ClipDescription getPrimaryClipDescription()

    void setPrimaryClip(ClipData clip)

 

hasPrimaryClip 메소드는 클립 데이터가 있는지 조사하며 비어 있으면 false를 리턴한다. 이 메소드가 true를 리턴하면 getPrimaryClip 메소드로 클립데이터를 읽는다.

 

물론 원하는 타입이 맞는지 조사한 후 사용해야 하는데 클립 데이터를 읽어 분석하기 전에 설명을 먼저 읽어 보고 타입을 점검할 수 있다.

 

클립보드에 데이터를 저장할 때는 클립 데이터를 만든 후 setPrimaryClip 메소드로 전달한다.

 

다음 메소드는 클립보드가 변경되는지 조사하는 리스너를 등록 및 제거한다. 클립보드의 데이터가 변경될 때마다 리스너의 onPrimaryClipChanged() 메소드가 호출된다.

 

    void addPrimaryClipChangedListener(ClipboardManager.OnPrimaryClipChangedListener what)

    void removePrimaryClipChangedListener(ClipboardManager.OnPrimaryClipChangedListener what)

 

클립보드가 변경되는 시점에 특정한 시점에 작업을 해야 한다면 이 리스너를 등록하고 핸들러에 코드를 작성한다.

 

ClipData클래스

 

클립보드에 저장되는 데이터를 표현하며 클립보드에 들어가는 내용물이다. 앞에서 알아본 바대로 설명과 데이터의 컬렉션이 저장된다.

 

다음 메소드로 항목의 개수를 조사하거나 항목을 읽고 추가한다.

 

    int getItemCount()

    ClipData.Item getItemAt(int index)

    void addItem(ClipData.Item item)

    CipDescription getDescription()

 

하나의 항목만 저장되는 경우 getItemAt(0) 호출로 항목을 바로 구할 수 있다.

 

클립 데이터를 생성할 때는 저장할 항목의 타입에 따라 다음 정적 메소드를 사용한다.

 

    ClipData newPlainText(CharSequence label, CharSequence text)

    ClipData newRawUri(CharSequence label, Uri uri)

    CilpData newUri(ContentResolver resolver, CharSequence label, Uri uri)

    ClipData newIntent(CharSequence label, Intent intent)

    ClipData newHtmlText(CharSequence label, CharSequence text, String htmlText)

 

데이터에 대한 간략한 설명인 레이블과 실제 데이터를 인수로 전달하면 클립 데이터 객체가 생성된다.

 

이렇게 만든 클립 데이터를 관리자의 setPrimaryClip 메소드로 클립보드에 넣어 복사한다.

 

ClipDescription 클래스

 

클립보드에 저장된 데이터에 대한 설명을 제공한다. 데이터에 대한 짧은 이름인 레이블과 데이터의 형태인 마임 타입이 저장되어 있다.

 

현재 지원하는 타입은 다음과 같다.

 

마임 타입

설명

MIMETYPE_TEXT_PLAIN(text/plain)

평범한 일반 문자열이다.

MIMETYPE_TEXT_INTENT(text/vnd.android.intent)

인텐트이다.

MIMETYPE_TEXT_URILIST(text/uri-list)

URI 목록이다.

MIMETYPE_TEXT_HTML(text/html)

서식있는 HTML 문자열이다.

 

다음 메소드는 레이블, 마임 타입을 조사하거나 특정한 타입이 저장되어 있는지 조사한다.

 

    CharSequence getLabel()

    int getMimeTypeCount()

    String getMimeType(int index)

    boolean hasMimeType(String mimeType)

 

클립보드를 읽기 전에 hasMimeType 메소드로 원하는 포맷이 있는지 조사해야 한다.

 

ClipData.Item 클래스

 

클립보드에 저장되는 데이터 하나이며 응용 프로그램끼리 교환할 실제 데이터이다. 클립보드에 복사할 때는 이 객체를 직접 생성하는 것보다 ClipData의 new* 정적 메소드를 사용하는 것이 일반적이다.

 

복수개의 항목을 저장할 때는 생성자로 객체를 직접 생성한 후 addItem 메소드로 항목을 추가한다. 붙여 넣을 때는 데이터 타입에 따라 다음 메소드 중 하나로 실제 값을 읽는다.

 

    CharSequence getText()

    Uri getUri()

    Intent getIntent()

    String getHtmlText()

 

문자열은 클립보드 자체에 저장되므로 읽어서 바로 사용할 수 있지만 Uri나 Intent는 읽은 후 원하는 데이터를 구하기 위해 추가적인 작업이 필요하다.

 

다음 메소드는 타입에 상관없이 강제로 문자열형태로 바꿔서 읽는다.

 

    CharSequence coerceToText(Context context)

    CharSequence coerceToStyledText(Context context)

    String coerceToHtmlText(Context context)

 

이 메소드는 붙여 넣기보다는 디버깅용이나 클립보드 관리 목적으로 주로 사용된다.

 

다음 예제는 간단한 텍스트를 클립보드에 저장하고 가져와서 붙여넣기를 해본다.

 

다음과 같이 프로젝트를 구성한다.

 

copytext.xml

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

    >

 

    <EditText

     android:id="@+id/copyedit"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="Clipboard Test"/>

    <Button

     android:id="@+id/btncopy"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:onClick="mOnClick"

     android:text="Copy"

     />

    <TextView

     android:id="@+id/pastetext"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:text="paste here"

     />

    <Button

     android:id="@+id/btnpaste"

     android:layout_width="wrap_content"

     android:layout_height="wrap_content"

     android:onClick="mOnClick"

     android:text="Paste"/>

    

</LinearLayout>

 

 

CopyText.java

 

package com.example.ch27_copytext;

 

import android.app.Activity;

import android.content.ClipData;

import android.content.ClipDescription;

import android.content.ClipboardManager;

import android.content.Context;

import android.os.Bundle;

import android.view.View;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;

 

public class CopyText extends Activity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.copytext);

    }

 

    

    //버튼 클릭시

    public void mOnClick(View v){

        switch(v.getId()){

        case R.id.btncopy:

            copyText();

            break;

        case R.id.btnpaste:

            pasteText();

            break;

        }

    }

    

    void copyText(){

        EditText copyedit = (EditText)findViewById(R.id.copyedit);

        String text= copyedit.getText().toString();//에디트텍스트에 입력된 가져옴

        

        if(text.length() != 0){

            //문자열을 클립보드에 넣는수 있는 클립데이터 형태로 포장

            ClipData clip = ClipData.newPlainText("text", text);

            

            //클립보드 관리자 객체를 가져옴

            ClipboardManager cm = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);

            cm.setPrimaryClip(clip);//클립보드에 저장하는 부분

            Toast.makeText(this, "Text Copied", 0).show();

        }

    }

    

    void pasteText(){

        //클립보드 관리자 객체를 가져옴

        ClipboardManager cm = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);

        //클립보드에 값이 없으면

        if(cm.hasPrimaryClip() == false){

            Toast.makeText(this, "clipboard empty", 0).show();

            return;

        }

        

        //클립보드의 값이 텍스트가 아니면?

        if(cm.getPrimaryClipDescription().hasMimeType(

                ClipDescription.MIMETYPE_TEXT_PLAIN)==false){

            Toast.makeText(this, "clip is not text", 0).show();

            return;

        }

                

        //클립데이터를 읽는다.

        ClipData clip = cm.getPrimaryClip();

        ClipData.Item item = clip.getItemAt(0);//첫번째 데이터를 가져옴

        TextView pastetext = (TextView)findViewById(R.id.pastetext);

        pastetext.setText(item.getText());//텍스트뷰에 세팅해줌

        

    }

    

}

 

 

 

실행 화면

에디트 텍스트에 값을 쓰고 Copy버튼을 누르고 Paste버튼을 누르면 다음과 같이 텍스트뷰가 바뀐다.

 

다른 앱에 가서도 붙여넣기를 하면 위에 클립보드에 저장한 값을 붙여넣기 할 수 있다.