[Android]通过ClipboardManager, ClipData进行复制粘贴[转]

Android应用开发之(通过ClipboardManager, ClipData进行复制粘贴)

Android Developer:

在开发一些系统应用的时候,我们会用到Android的剪贴板功能,比如将文本文件、或者其他格式的内容复制到剪贴板或者从剪贴板获取数据等操作。Android平台中每个常规的应用运行在自己的进程空间中,相对于Win32而言Android上之间的进程间传递主要有IPC、剪切板。当然今天我们说下最简单的ClipboardManager。使用剪切板可以直接实现数据的传输。整个实现比较简单,注意剪切板中的类型判断。

使用起来很简单,系统给我们提供了很方便的接口,如下文本信息复制如下所示:

//获取剪贴板管理服务
ClipboardManager cm =(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
//将文本数据复制到剪贴板
cm.setText(message);
//读取剪贴板数据
cm.getText();
public void setClipboard(String text) {
     ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
     clipboard.setText(text);
   }

   public String getClipboard() {
      ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
     return clipboard.getText().toString();
   }

ClipData代表剪贴板中剪切数据。它有一个或多个Item实例,每个可容纳一个或多个数据项。 ClipData包含ClipDescription,用来描述剪贴内容的重要元数据。尤其是getDescription().getMimeType(INT)必须返回正确的MIME类型。为了正确的设置剪贴内容的MIME类型,建议使用newPlainText(CharSequence,CharSequence的),newUri(ContentResolver,CharSequence中,URI),newIntent(CharSequence, Intent)构造ClipData。每个Item的实例可以是三大数据类型之一:text,intent,URI。详情请参阅ClipData.Item

粘贴数据

为了获取剪贴板中的数据,应用程序必须正确解析数据;如果CipData.Item包含的信息为文本或者Intent类型,有一点需要说明:文本只能解析为文本,intent通常用来当中快捷方式或者其他的动作类型;如果你只是想获取文本内容,你可以通过Item.coerceToText()方法强制获取,这样就不需要考虑MIME类型,应为所有的item都会被强制转换为文本。

复杂的数据类型通常用URL来完成粘贴。允许接受者以URI方式从ContentProvider的获取数据。剪贴时需要填写正确的MIME类型; 如:newUri(ContentResolver,CharSequence,URI)这样才能被正确的处理。

下面是NotePad应用粘贴的例子。当从剪贴板中接受数据时,如果剪贴板中包含已有note的URI引用时,根据URI复制其结构到新的Note中,否则通过根据获取的文本内容作为新的笔记内容:

/**
 * A helper method that replaces the note‘s data with the contents of the clipboard.
 */
private final void performPaste() {

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

    // Gets a content resolver instance
    ContentResolver cr = getContentResolver();

    // Gets the clipboard data from the clipboard
    ClipData clip = clipboard.getPrimaryClip();
    if (clip != null) {

        String text=null;
        String title=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 pointing to a note
        Uri uri = item.getUri();

        // Tests to see that the item actually is an URI, and that the URI
        // is a content URI pointing to a provider whose MIME type is the same
        // as the MIME type supported by the Note pad provider.
        if (uri != null && NotePad.Notes.CONTENT_ITEM_TYPE.equals(cr.getType(uri))) {

            // The clipboard holds a reference to data with a note MIME type. This copies it.
            Cursor orig = cr.query(
                    uri,            // URI for the content provider
                    PROJECTION,     // Get the columns referred to in the projection
                    null,           // No selection variables
                    null,           // No selection variables, so no criteria are needed
                    null            // Use the default sort order
            );

            // If the Cursor is not null, and it contains at least one record
            // (moveToFirst() returns true), then this gets the note data from it.
            if (orig != null) {
                if (orig.moveToFirst()) {
                    int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
                    int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
                    text = orig.getString(colNoteIndex);
                    title = orig.getString(colTitleIndex);
                }

                // Closes the cursor.
                orig.close();
            }
        }

        // If the contents of the clipboard wasn‘t a reference to a note, then
        // this converts whatever it is to text.
        if (text == null) {
            text = item.coerceToText(this).toString();
        }

        // Updates the current note with the retrieved title and text.
        updateNote(text, title);
    }
}

很多应用可以处理多种类型的数据,例如:E_mail应用希望用户粘贴图片或者其他二进制文件作为附件。这就需要通过ContentResolver的getStreamTypes(Uri, String)和openTypedAssetFileDescriptor(Uri,String,android.os.Bundle)方法处理。这需要客户端检测一个特定的内容URI以流的方式处理数据。

如下面是Item.coerceToText的实现:

public CharSequence coerceToText(Context context) {
    // If this Item has an explicit textual value, simply return that.
    if (mText != null) {
        return mText;
    }

    // If this Item has a URI value, try using that.
    if (mUri != null) {

        // First see if the URI can be opened as a plain text stream
        // (of any sub-type).  If so, this is the best textual
        // representation for it.
        FileInputStream stream = null;
        try {
            // Ask for a stream of the desired type.
            AssetFileDescriptor descr = context.getContentResolver()
                    .openTypedAssetFileDescriptor(mUri, "text/*", null);
            stream = descr.createInputStream();
            InputStreamReader reader = new InputStreamReader(stream, "UTF-8");

            // Got it...  copy the stream into a local string and return it.
            StringBuilder builder = new StringBuilder(128);
            char[] buffer = new char[8192];
            int len;
            while ((len=reader.read(buffer)) > 0) {
                builder.append(buffer, 0, len);
            }
            return builder.toString();

        } catch (FileNotFoundException e) {
            // Unable to open content URI as text...  not really an
            // error, just something to ignore.

        } catch (IOException e) {
            // Something bad has happened.
            Log.w("ClippedData", "Failure loading text", e);
            return e.toString();

        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                }
            }
        }

        // If we couldn‘t open the URI as a stream, then the URI itself
        // probably serves fairly well as a textual representation.
        return mUri.toString();
    }

    // Finally, if all we have is an Intent, then we can just turn that
    // into text.  Not the most user-friendly thing, but it‘s something.
    if (mIntent != null) {
        return mIntent.toUri(Intent.URI_INTENT_SCHEME);
    }

    // Shouldn‘t get here, but just in case...
    return "";
}

复制数据

做为复制的源数据,应用要构造容易被接受解析的剪贴数据。如果要复制包含文本,Intent,或者URI,简单的方式是使用ClipData.Item包含相应的类型数据;

复杂的数据类型要求支持以ContentProvide方式描述和生成被接受的数据,常用的解决方案是以URI的方式复制数据,URI有复杂结构的数据组成,只有理解这种结果的应用才能接受处理这样的数据;

对于不具有内在的数据结构知识的应用,可使用任意可接受的数据流类型。这是通过实现ContentProvider的getStreamTypes(URI,String)和openTypedAssetFile(URI字符串,android.os.Bundle)方法进行获取。

回到记事本应用程序的例子,它是将要复制的内容以URI的传递的

/**
 * This describes the MIME types that are supported for opening a note
 * URI as a stream.
 */
static ClipDescription NOTE_STREAM_TYPES = new ClipDescription(null,
        new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN });

/**
 * Returns the types of available data streams.  URIs to specific notes are supported.
 * The application can convert such a note to a plain text stream.
 *
 * @param uri the URI to analyze
 * @param mimeTypeFilter The MIME type to check for. This method only returns a data stream
 * type for MIME types that match the filter. Currently, only text/plain MIME types match.
 * @return a data stream MIME type. Currently, only text/plan is returned.
 * @throws IllegalArgumentException if the URI pattern doesn‘t match any supported patterns.
 */
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
    /**
     *  Chooses the data stream type based on the incoming URI pattern.
     */
    switch (sUriMatcher.match(uri)) {

        // If the pattern is for notes or live folders, return null. Data streams are not
        // supported for this type of URI.
        case NOTES:
        case LIVE_FOLDER_NOTES:
            return null;

        // If the pattern is for note IDs and the MIME filter is text/plain, then return
        // text/plain
        case NOTE_ID:
            return NOTE_STREAM_TYPES.filterMimeTypes(mimeTypeFilter);

            // If the URI pattern doesn‘t match any permitted patterns, throws an exception.
        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
}

/**
 * Returns a stream of data for each supported stream type. This method does a query on the
 * incoming URI, then uses
 * {@link android.content.ContentProvider#openPipeHelper(Uri, String, Bundle, Object,
 * PipeDataWriter)} to start another thread in which to convert the data into a stream.
 *
 * @param uri The URI pattern that points to the data stream
 * @param mimeTypeFilter A String containing a MIME type. This method tries to get a stream of
 * data with this MIME type.
 * @param opts Additional options supplied by the caller.  Can be interpreted as
 * desired by the content provider.
 * @return AssetFileDescriptor A handle to the file.
 * @throws FileNotFoundException if there is no file associated with the incoming URI.
 */
@Override
public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
        throws FileNotFoundException {

    // Checks to see if the MIME type filter matches a supported MIME type.
    String[] mimeTypes = getStreamTypes(uri, mimeTypeFilter);

    // If the MIME type is supported
    if (mimeTypes != null) {

        // Retrieves the note for this URI. Uses the query method defined for this provider,
        // rather than using the database query method.
        Cursor c = query(
                uri,                    // The URI of a note
                READ_NOTE_PROJECTION,   // Gets a projection containing the note‘s ID, title,
                                        // and contents
                null,                   // No WHERE clause, get all matching records
                null,                   // Since there is no WHERE clause, no selection criteria
                null                    // Use the default sort order (modification date,
                                        // descending
        );

        // If the query fails or the cursor is empty, stop
        if (c == null || !c.moveToFirst()) {

            // If the cursor is empty, simply close the cursor and return
            if (c != null) {
                c.close();
            }

            // If the cursor is null, throw an exception
            throw new FileNotFoundException("Unable to query " + uri);
        }

        // Start a new thread that pipes the stream data back to the caller.
        return new AssetFileDescriptor(
                openPipeHelper(uri, mimeTypes[0], opts, c, this), 0,
                AssetFileDescriptor.UNKNOWN_LENGTH);
    }

    // If the MIME type is not supported, return a read-only handle to the file.
    return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
}

/**
 * Implementation of {@link android.content.ContentProvider.PipeDataWriter}
 * to perform the actual work of converting the data in one of cursors to a
 * stream of data for the client to read.
 */
@Override
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
        Bundle opts, Cursor c) {
    // We currently only support conversion-to-text from a single note entry,
    // so no need for cursor data type checking here.
    FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
    PrintWriter pw = null;
    try {
        pw = new PrintWriter(new OutputStreamWriter(fout, "UTF-8"));
        pw.println(c.getString(READ_NOTE_TITLE_INDEX));
        pw.println("");
        pw.println(c.getString(READ_NOTE_NOTE_INDEX));
    } catch (UnsupportedEncodingException e) {
        Log.w(TAG, "Ooops", e);
    } finally {
        c.close();
        if (pw != null) {
            pw.flush();
        }
        try {
            fout.close();
        } catch (IOException e) {
        }
    }
}

not复制操作现在只是简单的构造UPI:

case R.id.context_copy:
    // Gets a handle to the clipboard service.
    ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);

    // Copies the notes URI to the clipboard. In effect, this copies the note itself
    clipboard.setPrimaryClip(ClipData.newUri(   // new clipboard item holding a URI
            getContentResolver(),               // resolver to retrieve URI info
            "Note",                             // label for the clip
            noteUri)                            // the URI
    );

    // Returns to the caller and skips further processing.
    return true;

注 如果粘贴操作需要文本(例如粘贴到编程器中)coerceToText(Context)方式会通知内容提供者将URI转换为URL;

时间: 2024-10-18 06:14:36

[Android]通过ClipboardManager, ClipData进行复制粘贴[转]的相关文章

Android应用开发之(通过ClipboardManager, ClipData进行复制粘贴)

在开发一些系统应用的时候,我们会用到Android的剪贴板功能,比如将文本文件.或者其他格式的内容复制到剪贴板或者从剪贴板获取数据等操作.Android平台中每个常规的应用运行在自己的进程空间中,相对于Win32而言Android上之间的进程间传递主要有IPC.剪切板.当然今天我们说下最简单的ClipboardManager.使用剪切板可以直接实现数据的传输.整个实现比较简单,注意剪切板中的类型判断. 使用起来很简单,系统给我们提供了很方便的接口,如下文本信息复制如下所示: //获取剪贴板管理服

Android 复制 粘贴 剪贴板的使用 ClipboardManager

Copy and Paste 版本:Android 4.0 r1 快速查看 用于复制粘贴数据的基于剪贴板的框架. 同时支持简单和复杂的数据,包括文本串.复杂的数据结构.文本和二进制流数据.程序 asset. 直接从剪贴板复制粘贴简单文本. 用content provider复制粘贴复杂数据. 需要API 11版本. 在本文中 剪贴板框架 剪贴板类 ClipboardManager ClipData.ClipDescription和ClipData.Item ClipData常用方法 将剪贴板中数

Android中的复制粘贴

The Clipboard Framework 当使用clipboard framework时,把数据放在一个剪切对象(clip object)里,然后这个对象会放在系统的剪贴板里. clip object可以有三种形式: Text:文字字符串. 文字是直接放在clip对象中,然后放在剪贴板里:粘贴这个字符串的时候直接从剪贴板拿到这个对象,把字符串放入你的应用存储中. URI:一个Uri 对象. 表示任何形式的URI.这种形式主要用于从一个content provider中复制复杂的数据. 复制

android EditText禁止复制粘贴完整代码

<!-- 定义基础布局LinearLayout --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="matc

Android Studio 复制粘贴图片到drawable文件夹没有效果 - 解决方法

我想放一些图片到drawable文件夹里面,但是简单的复制文件,粘贴文件,或者拖拽文件,都不起作用.不知道为什么,之前是可以的,突然就不行了. 解决方案 在drawable文件夹的目录上右键,选择Reveal in Finder,打开drawable文件夹,再把图片复制粘贴或拖拽进来就可以啦.

EditText禁止复制粘贴

1,自定义EditText package com.example.ui; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; import android.wid

表情复制粘贴

package com.syswin.toon.view; import android.content.ClipboardManager; import android.content.Context; import android.text.Editable; import android.text.SpannableString; import android.util.AttributeSet; import android.widget.EditText; import com.sys

[FMX]在你的跨平台应用中使用剪贴板进行复制粘贴

[FMX]在你的跨平台应用中使用剪贴板进行复制粘贴 2017-08-10 ? Android.C++ Builder.Delphi.iOS.教程 ? 暂无评论 ? swish ?浏览 516 次 VCL 中如何使用剪贴板咱就不说了,FMX 做为一个新的框架,提供了跨平台的剪贴板支持.FMX 对剪贴板的支持来自两个接口: IFMXClipboardService:位于 FMX.Platform.pas 中 1 2 3 4 5 6 7 8 9 10 11 IFMXClipboardService =

在你的跨平台应用中使用剪贴板进行复制粘贴

[FMX]在你的跨平台应用中使用剪贴板进行复制粘贴 2017-08-10 ? Android.C++ Builder.Delphi.iOS.教程 ? 暂无评论 ? swish ?浏览 681 次 VCL 中如何使用剪贴板咱就不说了,FMX 做为一个新的框架,提供了跨平台的剪贴板支持.FMX 对剪贴板的支持来自两个接口: IFMXClipboardService:位于 FMX.Platform.pas 中 1 2 3 4 5 6 7 8 9 10 11 IFMXClipboardService =