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

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

  1. //获取剪贴板管理服务
  2. ClipboardManager cm =(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
  3. //将文本数据复制到剪贴板
  4. cm.setText(message);
  5. //读取剪贴板数据
  6. cm.getText();
  1. public void setClipboard(String text) {
  2. ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
  3. clipboard.setText(text);
  4. }
  5.  
  6. public String getClipboard() {
  7. ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
  8. return clipboard.getText().toString();
  9. }

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中,否则通过根据获取的文本内容作为新的笔记内容:

  1. /**
  2. * A helper method that replaces the note's data with the contents of the clipboard.
  3. */
  4. private final void performPaste() {
  5.  
  6. // Gets a handle to the Clipboard Manager
  7. ClipboardManager clipboard = (ClipboardManager)
  8. getSystemService(Context.CLIPBOARD_SERVICE);
  9.  
  10. // Gets a content resolver instance
  11. ContentResolver cr = getContentResolver();
  12.  
  13. // Gets the clipboard data from the clipboard
  14. ClipData clip = clipboard.getPrimaryClip();
  15. if (clip != null) {
  16.  
  17. String text=null;
  18. String title=null;
  19.  
  20. // Gets the first item from the clipboard data
  21. ClipData.Item item = clip.getItemAt(0);
  22.  
  23. // Tries to get the item's contents as a URI pointing to a note
  24. Uri uri = item.getUri();
  25.  
  26. // Tests to see that the item actually is an URI, and that the URI
  27. // is a content URI pointing to a provider whose MIME type is the same
  28. // as the MIME type supported by the Note pad provider.
  29. if (uri != null && NotePad.Notes.CONTENT_ITEM_TYPE.equals(cr.getType(uri))) {
  30.  
  31. // The clipboard holds a reference to data with a note MIME type. This copies it.
  32. Cursor orig = cr.query(
  33. uri, // URI for the content provider
  34. PROJECTION, // Get the columns referred to in the projection
  35. null, // No selection variables
  36. null, // No selection variables, so no criteria are needed
  37. null // Use the default sort order
  38. );
  39.  
  40. // If the Cursor is not null, and it contains at least one record
  41. // (moveToFirst() returns true), then this gets the note data from it.
  42. if (orig != null) {
  43. if (orig.moveToFirst()) {
  44. int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
  45. int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
  46. text = orig.getString(colNoteIndex);
  47. title = orig.getString(colTitleIndex);
  48. }
  49.  
  50. // Closes the cursor.
  51. orig.close();
  52. }
  53. }
  54.  
  55. // If the contents of the clipboard wasn't a reference to a note, then
  56. // this converts whatever it is to text.
  57. if (text == null) {
  58. text = item.coerceToText(this).toString();
  59. }
  60.  
  61. // Updates the current note with the retrieved title and text.
  62. updateNote(text, title);
  63. }
  64. }

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

如下面是Item.coerceToText的实现:

  1. public CharSequence coerceToText(Context context) {
  2. // If this Item has an explicit textual value, simply return that.
  3. if (mText != null) {
  4. return mText;
  5. }
  6.  
  7. // If this Item has a URI value, try using that.
  8. if (mUri != null) {
  9.  
  10. // First see if the URI can be opened as a plain text stream
  11. // (of any sub-type). If so, this is the best textual
  12. // representation for it.
  13. FileInputStream stream = null;
  14. try {
  15. // Ask for a stream of the desired type.
  16. AssetFileDescriptor descr = context.getContentResolver()
  17. .openTypedAssetFileDescriptor(mUri, "text/*", null);
  18. stream = descr.createInputStream();
  19. InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
  20.  
  21. // Got it... copy the stream into a local string and return it.
  22. StringBuilder builder = new StringBuilder(128);
  23. char[] buffer = new char[8192];
  24. int len;
  25. while ((len=reader.read(buffer)) > 0) {
  26. builder.append(buffer, 0, len);
  27. }
  28. return builder.toString();
  29.  
  30. } catch (FileNotFoundException e) {
  31. // Unable to open content URI as text... not really an
  32. // error, just something to ignore.
  33.  
  34. } catch (IOException e) {
  35. // Something bad has happened.
  36. Log.w("ClippedData", "Failure loading text", e);
  37. return e.toString();
  38.  
  39. } finally {
  40. if (stream != null) {
  41. try {
  42. stream.close();
  43. } catch (IOException e) {
  44. }
  45. }
  46. }
  47.  
  48. // If we couldn't open the URI as a stream, then the URI itself
  49. // probably serves fairly well as a textual representation.
  50. return mUri.toString();
  51. }
  52.  
  53. // Finally, if all we have is an Intent, then we can just turn that
  54. // into text. Not the most user-friendly thing, but it's something.
  55. if (mIntent != null) {
  56. return mIntent.toUri(Intent.URI_INTENT_SCHEME);
  57. }
  58.  
  59. // Shouldn't get here, but just in case...
  60. return "";
  61. }

复制数据

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

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

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

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

  1. /**
  2. * This describes the MIME types that are supported for opening a note
  3. * URI as a stream.
  4. */
  5. static ClipDescription NOTE_STREAM_TYPES = new ClipDescription(null,
  6. new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN });
  7.  
  8. /**
  9. * Returns the types of available data streams. URIs to specific notes are supported.
  10. * The application can convert such a note to a plain text stream.
  11. *
  12. * @param uri the URI to analyze
  13. * @param mimeTypeFilter The MIME type to check for. This method only returns a data stream
  14. * type for MIME types that match the filter. Currently, only text/plain MIME types match.
  15. * @return a data stream MIME type. Currently, only text/plan is returned.
  16. * @throws IllegalArgumentException if the URI pattern doesn't match any supported patterns.
  17. */
  18. @Override
  19. public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
  20. /**
  21. * Chooses the data stream type based on the incoming URI pattern.
  22. */
  23. switch (sUriMatcher.match(uri)) {
  24.  
  25. // If the pattern is for notes or live folders, return null. Data streams are not
  26. // supported for this type of URI.
  27. case NOTES:
  28. case LIVE_FOLDER_NOTES:
  29. return null;
  30.  
  31. // If the pattern is for note IDs and the MIME filter is text/plain, then return
  32. // text/plain
  33. case NOTE_ID:
  34. return NOTE_STREAM_TYPES.filterMimeTypes(mimeTypeFilter);
  35.  
  36. // If the URI pattern doesn't match any permitted patterns, throws an exception.
  37. default:
  38. throw new IllegalArgumentException("Unknown URI " + uri);
  39. }
  40. }
  41.  
  42. /**
  43. * Returns a stream of data for each supported stream type. This method does a query on the
  44. * incoming URI, then uses
  45. * {@link android.content.ContentProvider#openPipeHelper(Uri, String, Bundle, Object,
  46. * PipeDataWriter)} to start another thread in which to convert the data into a stream.
  47. *
  48. * @param uri The URI pattern that points to the data stream
  49. * @param mimeTypeFilter A String containing a MIME type. This method tries to get a stream of
  50. * data with this MIME type.
  51. * @param opts Additional options supplied by the caller. Can be interpreted as
  52. * desired by the content provider.
  53. * @return AssetFileDescriptor A handle to the file.
  54. * @throws FileNotFoundException if there is no file associated with the incoming URI.
  55. */
  56. @Override
  57. public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
  58. throws FileNotFoundException {
  59.  
  60. // Checks to see if the MIME type filter matches a supported MIME type.
  61. String[] mimeTypes = getStreamTypes(uri, mimeTypeFilter);
  62.  
  63. // If the MIME type is supported
  64. if (mimeTypes != null) {
  65.  
  66. // Retrieves the note for this URI. Uses the query method defined for this provider,
  67. // rather than using the database query method.
  68. Cursor c = query(
  69. uri, // The URI of a note
  70. READ_NOTE_PROJECTION, // Gets a projection containing the note's ID, title,
  71. // and contents
  72. null, // No WHERE clause, get all matching records
  73. null, // Since there is no WHERE clause, no selection criteria
  74. null // Use the default sort order (modification date,
  75. // descending
  76. );
  77.  
  78. // If the query fails or the cursor is empty, stop
  79. if (c == null || !c.moveToFirst()) {
  80.  
  81. // If the cursor is empty, simply close the cursor and return
  82. if (c != null) {
  83. c.close();
  84. }
  85.  
  86. // If the cursor is null, throw an exception
  87. throw new FileNotFoundException("Unable to query " + uri);
  88. }
  89.  
  90. // Start a new thread that pipes the stream data back to the caller.
  91. return new AssetFileDescriptor(
  92. openPipeHelper(uri, mimeTypes[0], opts, c, this), 0,
  93. AssetFileDescriptor.UNKNOWN_LENGTH);
  94. }
  95.  
  96. // If the MIME type is not supported, return a read-only handle to the file.
  97. return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
  98. }
  99.  
  100. /**
  101. * Implementation of {@link android.content.ContentProvider.PipeDataWriter}
  102. * to perform the actual work of converting the data in one of cursors to a
  103. * stream of data for the client to read.
  104. */
  105. @Override
  106. public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
  107. Bundle opts, Cursor c) {
  108. // We currently only support conversion-to-text from a single note entry,
  109. // so no need for cursor data type checking here.
  110. FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
  111. PrintWriter pw = null;
  112. try {
  113. pw = new PrintWriter(new OutputStreamWriter(fout, "UTF-8"));
  114. pw.println(c.getString(READ_NOTE_TITLE_INDEX));
  115. pw.println("");
  116. pw.println(c.getString(READ_NOTE_NOTE_INDEX));
  117. } catch (UnsupportedEncodingException e) {
  118. Log.w(TAG, "Ooops", e);
  119. } finally {
  120. c.close();
  121. if (pw != null) {
  122. pw.flush();
  123. }
  124. try {
  125. fout.close();
  126. } catch (IOException e) {
  127. }
  128. }
  129. }

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

  1. case R.id.context_copy:
  2. // Gets a handle to the clipboard service.
  3. ClipboardManager clipboard = (ClipboardManager)
  4. getSystemService(Context.CLIPBOARD_SERVICE);
  5.  
  6. // Copies the notes URI to the clipboard. In effect, this copies the note itself
  7. clipboard.setPrimaryClip(ClipData.newUri( // new clipboard item holding a URI
  8. getContentResolver(), // resolver to retrieve URI info
  9. "Note", // label for the clip
  10. noteUri) // the URI
  11. );
  12.  
  13. // Returns to the caller and skips further processing.
  14. return true;

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

Android应用开发之(通过ClipboardManager, ClipData进行复制粘贴)的更多相关文章

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

    Copy and Paste 版本:Android 4.0 r1  快速查看 用于复制粘贴数据的基于剪贴板的框架. 同时支持简单和复杂的数据,包括文本串.复杂的数据结构.文本和二进制流数据.程序 as ...

  2. Android N开发 你需要知道的一切

    title: Android N开发 你需要知道的一切 tags: Android N,Android7.0,Android --- 转载请注明出处:http://www.cnblogs.com/yi ...

  3. Android游戏开发实践(1)之NDK与JNI开发03

    Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...

  4. Android游戏开发实践(1)之NDK与JNI开发01

    Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码&q ...

  5. Android游戏开发实践(1)之NDK与JNI开发02

    Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI开发01分享完JNI的基础和简要开发流程之后,再来分享下在Android环境下的JNI ...

  6. 【转】Android 底层开发的几点

    我干了3年Android sdk开发,觉得到了瓶劲没法更进一步,于是花了一年多点时间,大概摸到点门径.根据前辈的经验,Android底层完全入门需要两年. 先说下我的入门过程:第零步,下载源码,我下的 ...

  7. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  8. Android安全开发之安全使用HTTPS

    Android安全开发之安全使用HTTPS 1.HTTPS简介 阿里聚安全的应用漏洞扫描器中有证书弱校验.主机名弱校验.webview未校验证书的检测项,这些检测项是针对APP采用HTTPS通信时容易 ...

  9. Android安全开发之通用签名风险

    Android安全开发之通用签名风险 作者:伊樵.舟海.呆狐@阿里聚安全 1 通用签名风险简介 1.1 Android应用签名机制 阿里聚安全漏洞扫描器有一项检测服务是检测APP的通用签名风险.And ...

随机推荐

  1. 17.1.1.7 Setting Up Replication with New Master and Slaves 设置复制对于新的Master和Slaves:

    17.1.1.7 Setting Up Replication with New Master and Slaves 设置复制对于新的Master和Slaves: 最简单和最直接的方法是设置复制用于使 ...

  2. MFC全局函数开局——AfxGetApp解剖

    MFC中有不少的全局函数,方便在不同对象中获取不同的内容或创建不同的对象.主要全局函数有: AfxWinInit() AfxBeginThread() AfxEndThread() AfxFormat ...

  3. JVM调优总结(七)-典型配置举例1

    以下配置主要针对分代垃圾回收算法而言. 堆大小设置 年轻代的设置很关键 JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理 ...

  4. IE浏览器下web调试工具之--IE WebDeveloper介绍

    做Web项目的架构设计.开发.测试,免不了要熟悉Web页面调试工具,以此来获知哪些浏览器支持Web页面的显示,哪些浏览器下显示有问题. 目前市面上比较火爆的浏览器内核提供商,有微软的IE.mozill ...

  5. ORA-19815,ORA-19809 :limit exceeded for recovery files

    数据库重新启动的时候,收到了ORA-19815的错误.从错误的提示来看,是由于闪回区的空间被填满导致无法成功启动.这种情形我们通常考虑的是清除归档日志,那就直接在OS层面rm了,真的是这样吗?客官,如 ...

  6. 8 cocos2dx加入场景切换效果,控制场景切换彻底完毕之后再运行动画

     1 加入场景切换效果 供场景切换的类: CCTransitionJumpZoom CCTransitionProgressRadialCCW CCTransitionProgressRadial ...

  7. git项目同时支持多个远端仓库

    git项目同时支持多个远端仓库 为了防止github被墙,最好在国内的托管商做一个备份,这就需要同时提交到多个远端仓库,例如一个open source项目同时要提交csdn和github,url分别是 ...

  8. linux系统挂掉问题的分析

    玩linux系统,经常遇到的一件事就是做了某个操作之后系统会突然挂掉,这要怎么办? 1. 首先我们要看log,看看是否会留下一些蛛丝马迹,比如PC/LR是否有留下来. PC是ARM的一个寄存器,即程序 ...

  9. Eclipse用法和技巧六:自动生成get和set方法1

    java的类中,除了常量声明为静态且公有的,一般的对象数据作用域,都是声明为私有的.这样做能保护对象的属性不会被随意改变,调试的时候也会方便很多:在类的公有方法中大一个调用栈就能看到哪里改了属性值.声 ...

  10. MFC类中获得其它类指针

    当用VC++的Application Wizard生成除了CDialog Basiced以外的应用程序时,将自动产生视图类.文档类.主帧窗口类.应用程序类等等.一般来说,程序的核心数据及操作在文档类中 ...