Android剪切板

作者: WilliamIT | 来源:发表于2018-12-09 16:26 被阅读2次

    介绍

    Android提供了一个强大的基于剪贴板的复制和粘贴框架。它既支持简单的数据类型,也支持复杂的数据类型,包括文本字符串、复杂的数据结构、文本和二进制流数据,甚至还支持应用程序资源。

    如下图所示:


    clip.png

    由上图可以简单的得到Android剪切板模版主要由四个类构成:ClipboardManagerClipDataClipData.ItemClipDescription.

    简单的描述:系统复制数据,就是创建一个ClipData对象放在ClipboardManager全局上.ClipData可以包括多条Item子数据,子数据中复制内容可以是text,url,intent,但是都是这些子数据都是来自一次复制,每次复制会覆盖之前的复制内容.同时,ClipData中包含一个ClipDescription,用于描述本次复制内容的MimeType.

    核心类

    • ClipboardManager

    系统服务全局的剪切板类.如何得到如下:

    ClipboardManager mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
    

    定义当剪贴板上的主剪辑发生更改时调用的侦听器回调:OnPrimaryClipChangedListener.

    // 添加剪贴板数据改变监听器
    mClipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
        @Override
        public void onPrimaryClipChanged() {
            // 剪贴板中的数据被改变,此方法将被回调
            System.out.println("onPrimaryClipChanged()");
        }
    });
    
    // 移除指定的剪贴板数据改变监听器
     mClipboardManager.removePrimaryClipChangedListener(listener);
    
    • ClipData.Item
      剪切板子数据类,它包含了texthtmlUri或者Intent数据,一个clip对象可以包含一个或多个Item对象。
      一起来看看它的属性:
            final CharSequence mText;
            final String mHtmlText;
            final Intent mIntent;
            Uri mUri;
    

    就是一个数据类.

    • ClipDescription
      剪切板的描述类.包含了ClipData对象的metadata信息,一般情况mimeType只有一个.
      一起看看它的属性就知道干什么的类了.
    public class ClipDescription implements Parcelable {
        //默认的MimeTYpe
        public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
    
        public static final String MIMETYPE_TEXT_HTML = "text/html";
        
        public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
    
        public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
    
        public static final String EXTRA_TARGET_COMPONENT_NAME =
                "android.content.extra.TARGET_COMPONENT_NAME";
        public static final String EXTRA_USER_SERIAL_NUMBER =    "android.content.extra.USER_SERIAL_NUMBER";
        //包含一个标签
        final CharSequence mLabel;
        //mimeType数组
        final String[] mMimeTypes;
        //可以保存额外的数据
        private PersistableBundle mExtras;
        ......
        
    }
    

    一般使用前面四种:text、html、uri、intent.其中url比较特殊.如果使用Android资源MimeType需要由ContentResolver提供.
    什么是uri:

    通用资源标志符(Universal Resource Identifier, 简称"URI")。
    Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。
    Android的Uri由以下三部分组成: "content://"、数据的路径、标示ID(可选)

    • ClipData
      剪切对象,在有且仅有一个剪切板对象在系统服务中.言外之意,每一次复制前一次复制内容都会消失.
      一起来看看它的属性:
    public class ClipData implements Parcelable {
        //默认mimetype。 text/plain
        static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
            ClipDescription.MIMETYPE_TEXT_PLAIN };
        //text/html
        static final String[] MIMETYPES_TEXT_HTML = new String[] {
            ClipDescription.MIMETYPE_TEXT_HTML };
        //urllist
        static final String[] MIMETYPES_TEXT_URILIST = new String[] {
            ClipDescription.MIMETYPE_TEXT_URILIST };
        //intent
        static final String[] MIMETYPES_TEXT_INTENT = new String[] {
            ClipDescription.MIMETYPE_TEXT_INTENT };
        
        //剪切板描述类
        final ClipDescription mClipDescription;
        
        final Bitmap mIcon;
        
        //用于存放剪切板子数据
        final ArrayList<Item> mItems;
    
        .......
    }
    

    创建方式:

    /**
         * Create a new ClipData holding data of the type
         * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
         *
         * @param label User-visible label for the clip data.
         * @param text The actual text in the clip.
         * @return Returns a new ClipData containing the specified data.
         */
        static public ClipData newPlainText(CharSequence label, CharSequence text) {
            Item item = new Item(text);
            return new ClipData(label, MIMETYPES_TEXT_PLAIN, item);
        }
    
        /**
         * Create a new ClipData holding data of the type
         * {@link ClipDescription#MIMETYPE_TEXT_HTML}.
         *
         * @param label User-visible label for the clip data.
         * @param text The text of clip as plain text, for receivers that don't
         * handle HTML.  This is required.
         * @param htmlText The actual HTML text in the clip.
         * @return Returns a new ClipData containing the specified data.
         */
        static public ClipData newHtmlText(CharSequence label, CharSequence text,
                String htmlText) {
            Item item = new Item(text, htmlText);
            return new ClipData(label, MIMETYPES_TEXT_HTML, item);
        }
    
        /**
         * Create a new ClipData holding an Intent with MIME type
         * {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
         *
         * @param label User-visible label for the clip data.
         * @param intent The actual Intent in the clip.
         * @return Returns a new ClipData containing the specified data.
         */
        static public ClipData newIntent(CharSequence label, Intent intent) {
            Item item = new Item(intent);
            return new ClipData(label, MIMETYPES_TEXT_INTENT, item);
        }
    
        /**
         * Create a new ClipData holding a URI.  If the URI is a content: URI,
         * this will query the content provider for the MIME type of its data and
         * use that as the MIME type.  Otherwise, it will use the MIME type
         * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
         *
         * @param resolver ContentResolver used to get information about the URI.
         * @param label User-visible label for the clip data.
         * @param uri The URI in the clip.
         * @return Returns a new ClipData containing the specified data.
         */
        static public ClipData newUri(ContentResolver resolver, CharSequence label,
                Uri uri) {
            //创建item
            Item item = new Item(uri);
            /*获取mimeType*/
            String[] mimeTypes = null;
            if ("content".equals(uri.getScheme())) {
                String realType = resolver.getType(uri);
                mimeTypes = resolver.getStreamTypes(uri, "*/*");
                if (realType != null) {
                    if (mimeTypes == null) {
                        mimeTypes = new String[] { realType };
                    } else {
                        String[] tmp = new String[mimeTypes.length + 1];
                        tmp[0] = realType;
                        System.arraycopy(mimeTypes, 0, tmp, 1, mimeTypes.length);
                        mimeTypes = tmp;
                    }
                }
            }
            if (mimeTypes == null) {
                mimeTypes = MIMETYPES_TEXT_URILIST;
            }
            return new ClipData(label, mimeTypes, item);
        }
    
        /**
         * Create a new ClipData holding an URI with MIME type
         * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
         * Unlike {@link #newUri(ContentResolver, CharSequence, Uri)}, nothing
         * is inferred about the URI -- if it is a content: URI holding a bitmap,
         * the reported type will still be uri-list.  Use this with care!
         *
         * @param label User-visible label for the clip data.
         * @param uri The URI in the clip.
         * @return Returns a new ClipData containing the specified data.
         */
        static public ClipData newRawUri(CharSequence label, Uri uri) {
            //创建item
            Item item = new Item(uri);
            return new ClipData(label, MIMETYPES_TEXT_URILIST, item);
        }
    

    clipData对象创建后塞入Clipboardmanager即可:

    //Clipboardmanager方法
    /**
         * Sets the current primary clip on the clipboard.  This is the clip that
         * is involved in normal cut and paste operations.
         *
         * @param clip The clipped data item to set.
         */
        public void setPrimaryClip(ClipData clip) {
            try {
                if (clip != null) {
                    clip.prepareToLeaveProcess(true);
                }
                getService().setPrimaryClip(clip, mContext.getOpPackageName());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    
    
    • 转换成字符串
    //任何作为HTML格式返回的文本都将作为具有样式跨度的文本返回。
    CharSequence coerceToStyledText(Context context);
    
    //如果getText()是非空的,则返回该值。
    //如果getUri()非null,则尝试从其内容提供程序检索其数据作为文本流。如果成功,将文本复制到字符串中并返回。如果它不是内容:URI或内容提供程序不提供文本表示,则将原始URI作为字符串返回。
    //如果getIntent()非null,则将其转换为intent: URI并返回。
    //否则,返回一个空字符串。
    CharSequence coerceToText(Context context) ;
    
    //如果getHtmlText()非null,则返回该值。
    //如果getText()是非空的,返回它,转换为有效的HTML文本。如果此文本包含样式跨度,则使用HTML . tohtml (span)将其转换为HTML格式。
    //如果getUri()非null,则尝试从其内容提供程序检索其数据作为文本流。
    //如果提供程序可以提供文本/html数据,则首选该数据并按原样返回。否则,将返回任何文本/*数据并转义到HTML。
    //如果它不是内容:URI或内容提供程序不提供文本表示,将返回包含到URI链接的HTML文本。
    //如果getIntent()非null,则将其转换为intent: URI并以HTML链接的形式返回。
    //否则,返回一个空字符串。
    String coerceToHtmlText(Context context) 
    

    详细的内容可以查看官网,地址我也写出来了,在最下面,哈哈哈.

    • 注意
      1、剪切板只会保存最近一次复制的内容.
      2、MimeType一般只有一个.(可以有多个)
      3、系统全局的剪切板,其他应用也可以使用.

    工具类

    
    package com.rnx.react.modules.clip;
    
    import android.content.ClipData;
    import android.content.ClipDescription;
    import android.content.ClipboardManager;
    import android.content.ContentResolver;
    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    
    import java.util.List;
    
    /**
     * @Auther: weiwei.zhang06
     * @Date: 2018/12/5 18:59
     */
    
    public class ClipboardHelper {
    
      public static final String TAG = ClipboardHelper.class.getSimpleName();
    
      private Context mContext;
      private volatile static ClipboardHelper mInstance;
      private ClipboardManager mClipboardManager;
    
      private ClipboardHelper(Context context) {
        mContext = context;
        mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
      }
    
      /**
       * 获取ClipboardUtil实例,记得初始化
       *
       * @return 单例
       */
      public static ClipboardHelper getInstance(Context context) {
        if (mInstance == null) {
          synchronized (ClipboardHelper.class) {
            if (mInstance == null) {
              mInstance = new ClipboardHelper(context.getApplicationContext());
            }
          }
        }
        return mInstance;
      }
    
      /**
       * 判断剪贴板内是否有数据
       *
       * @return
       */
      public boolean hasPrimaryClip() {
        return mClipboardManager.hasPrimaryClip();
      }
    
      /**
       * 获取剪贴板中第一条String
       *
       * @return
       */
      public String getClipText() {
        if (!hasPrimaryClip()) {
          return null;
        }
        ClipData data = mClipboardManager.getPrimaryClip();
        if (data != null
          && mClipboardManager.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
          return data.getItemAt(0).getText().toString();
        }
        return null;
      }
    
      /**
       * 获取剪贴板中第一条String
       *
       * @param context
       * @return
       */
      public String getClipText(Context context) {
        return getClipText(context, 0);
      }
    
      /**
       * 获取剪贴板中指定位置item的string
       *
       * @param context
       * @param index
       * @return
       */
      public String getClipText(Context context, int index) {
        if (!hasPrimaryClip()) {
          return null;
        }
        ClipData data = mClipboardManager.getPrimaryClip();
        if (data == null) {
          return null;
        }
        if (data.getItemCount() > index) {
          return data.getItemAt(index).coerceToText(context).toString();
        }
        return null;
      }
    
      /**
       * 将文本拷贝至剪贴板
       *
       * @param text
       */
      public void copyText(String label, String text) {
        ClipData clip = ClipData.newPlainText(label, text);
        mClipboardManager.setPrimaryClip(clip);
      }
    
      /**
       * 将HTML等富文本拷贝至剪贴板
       *
       * @param label
       * @param text
       * @param htmlText
       */
      public void copyHtmlText(String label, String text, String htmlText) {
        ClipData clip = ClipData.newHtmlText(label, text, htmlText);
        mClipboardManager.setPrimaryClip(clip);
      }
    
      /**
       * 将Intent拷贝至剪贴板
       *
       * @param label
       * @param intent
       */
      public void copyIntent(String label, Intent intent) {
        ClipData clip = ClipData.newIntent(label, intent);
        mClipboardManager.setPrimaryClip(clip);
      }
    
      /**
       * 将Uri拷贝至剪贴板
       * If the URI is a content: URI,
       * this will query the content provider for the MIME type of its data and
       * use that as the MIME type.  Otherwise, it will use the MIME type
       * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
       * 如 uri = "content://contacts/people",那么返回的MIME type将变成"vnd.android.cursor.dir/person"
       *
       * @param cr    ContentResolver used to get information about the URI.
       * @param label User-visible label for the clip data.
       * @param uri   The URI in the clip.
       */
      public void copyUri(ContentResolver cr, String label, Uri uri) {
        ClipData clip = ClipData.newUri(cr, label, uri);
        mClipboardManager.setPrimaryClip(clip);
      }
    
      /**
       * 将多组数据放入剪贴板中,如选中ListView多个Item,并将Item的数据一起放入剪贴板
       *
       * @param label    User-visible label for the clip data.
       * @param mimeType mimeType is one of them:{@link ClipDescription#MIMETYPE_TEXT_PLAIN},
       *                 {@link ClipDescription#MIMETYPE_TEXT_HTML},
       *                 {@link ClipDescription#MIMETYPE_TEXT_URILIST},
       *                 {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
       * @param items    放入剪贴板中的数据
       */
      public void copyMultiple(String label, String mimeType, List<ClipData.Item> items) {
        if (items == null || items.size() == 0) {
          throw new IllegalArgumentException("argument: items error");
        }
        int size = items.size();
        ClipData clip = new ClipData(label, new String[]{mimeType}, items.get(0));
        for (int i = 1; i < size; i++) {
          clip.addItem(items.get(i));
        }
        mClipboardManager.setPrimaryClip(clip);
      }
    
      public void copyMultiple(String label, String[] mimeTypes, List<ClipData.Item> items) {
        if (items == null || items.size() == 0) {
          throw new IllegalArgumentException("argument: items error");
        }
        int size = items.size();
        ClipData clip = new ClipData(label, mimeTypes, items.get(0));
        for (int i = 1; i < size; i++) {
          clip.addItem(items.get(i));
        }
        mClipboardManager.setPrimaryClip(clip);
      }
    
      public CharSequence coercePrimaryClipToText() {
        if (!hasPrimaryClip()) {
          return null;
        }
        return mClipboardManager.getPrimaryClip().getItemAt(0).coerceToText(mContext);
      }
    
      public CharSequence coercePrimaryClipToStyledText() {
        if (!hasPrimaryClip()) {
          return null;
        }
        return mClipboardManager.getPrimaryClip().getItemAt(0).coerceToStyledText(mContext);
      }
    
      public CharSequence coercePrimaryClipToHtmlText() {
        if (!hasPrimaryClip()) {
          return null;
        }
        return mClipboardManager.getPrimaryClip().getItemAt(0).coerceToHtmlText(mContext);
      }
    
      /**
       * 获取当前剪贴板内容的MimeType
       *
       * @return 当前剪贴板内容的MimeType
       */
      public String getPrimaryClipMimeType() {
        if (!hasPrimaryClip()) {
          return null;
        }
        return mClipboardManager.getPrimaryClipDescription().getMimeType(0);
      }
    
      /**
       * 获取剪贴板内容的MimeType
       *
       * @param clip 剪贴板内容
       * @return 剪贴板内容的MimeType
       */
      public String getClipMimeType(ClipData clip) {
        return clip.getDescription().getMimeType(0);
      }
    
      /**
       * 获取剪贴板内容的MimeType
       *
       * @param clipDescription 剪贴板内容描述
       * @return 剪贴板内容的MimeType
       */
      public String getClipMimeType(ClipDescription clipDescription) {
        return clipDescription.getMimeType(0);
      }
    
      /**
       * 清空剪贴板
       */
      public void clearClip() {
        mClipboardManager.setPrimaryClip(ClipData.newPlainText(null, ""));
      }
    
      public ClipData getClipData() {
        if (!hasPrimaryClip()) {
          return null;
        }
        return mClipboardManager.getPrimaryClip();
      }
    }
    
    

    官网: https://developer.android.com/guide/topics/text/copy-paste#java

    ClipboardManger api: https://developer.android.com/reference/android/content/ClipboardManager

    ClipData api: https://developer.android.com/reference/android/content/ClipData

    ClipData.item api: https://developer.android.com/reference/android/content/ClipData.Item

    ClipDescription api: https://developer.android.com/reference/android/content/ClipDescription


    感谢阅读

    相关文章

      网友评论

        本文标题:Android剪切板

        本文链接:https://www.haomeiwen.com/subject/tdckhqtx.html