Android 剪貼板詳解

轉自:https://github.com/MyLifeMyTravel/AndroidDemo
Android提供了一個強大的剪貼板框架,用於複製和粘貼。它支持文本、二進制數據流或其它複雜的數據。
Android 剪貼板框架如圖:
Android 剪貼板詳解
從圖中可以看出,Android 剪貼板框架主要涉及到ClipboardManager,ClipData,ClipData.item,ClipDescription四個類。
這四個類的簡介如下:
1.ClipboardManager是系統全局的剪切板對象,通過context.getSystemService(CLIPBOARD_SERVICE)獲得。
2.ClipData,即clip對象,在系統剪切板中只能存在一個,當另一個clip對象進來時,前一個自動銷燬。
3.ClipData.item,即data item,它可以包含文本,Uri,或intent數據,一個clip對象可以包括一個或多個item對象。通過addItem(ClipData.Item item)可以實現在clip對象中添加item。

文本:直接放在clip對象中,然後放在剪切板裏,粘貼文本時直接從剪切板裏拿到clip對象,把字符串 放入應用存儲中。
Uri:對於複雜數據的剪貼拷貝並不是直接將數據放入內存,而是通過uri來實現,通過uri定位手機上的 資源,從而實現拷貝。
intent:複製的時候intent後被直接放入clip對象中,相當於拷貝了一個快捷方式。
4.ClipDescription:即clip metadata,它包括了ClipData對象的metadata信息。可以通過getMimeType(int index)獲取,一般index=0.MimeType一般有以下四種類型:

// 對應 ClipData newPlainText(label, text) 的 MimeType
public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
// 對應 ClipData.newHtmlText(label, text, htmlText) 的 MimeType
public static final String MIMETYPE_TEXT_HTML = "text/html";
// 對應 ClipData.newUri(cr, label, uri) 的 MimeType
public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
// 對應 ClipData.newIntent(label, intent) 的 MimeType
public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";

但MIMETYPE_TEXT_URILIST有點特殊,當Uri爲content://uri時,她會轉爲具體的MimeType,後面有例子。

剪貼板的簡單使用

以拷貝文本爲例,剪貼板的使用可以分爲以下幾步:

//1.獲得ClipboardManager
        ClipboardManager clipboardManager=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
//2.將數據放到clip對象中 
        ClipData clipData=ClipData.newPlainText("textCopy",getResources().getString(R.string.source));
                            // 類似的方法還有
                            //創建一個包含 htmlText 的 ClipData
                            //一般在瀏覽器中對網頁進行拷貝的時候會調用此方法
                            //其中 htmlText 是包含 HTML 標籤的字符串
                            //static public ClipData newHtmlText(CharSequence label, CharSequence text, String htmlText);
                            //創建一個包含 Intent 的 ClipData
                            //static public ClipData newIntent(CharSequence label, Intent intent);
                            //創建一個包含 Uri 的 ClipData,MimeType 會根據 Uri 進行修改
                            //static public ClipData newUri(ContentResolver resolver, CharSequence label, Uri uri);
                            //與 newUri 相對應,但是並不會根據 Uri 修改 MimeType
                            //static public ClipData newRawUri(CharSequence label, Uri uri);
//3.將clipdata放入剪切板中
         clipboardManager.setPrimaryClip(clipData);
//4.從剪切板中取出數據
        if(clipboardManager.hasPrimaryClip()){
            ClipData clipData1=clipboardManager.getPrimaryClip();
            ClipDescription description=clipboardManager.getPrimaryClipDescription();
            String label= (String) description.getLabel();
            String text=clipData.getItemAt(0).getText().toString();
            tv.setText(text);
        }

其他數據的拷貝參考:
https://www.jianshu.com/p/213d7062cdbe

對於剪貼板大部分操作都封裝在 ClipboardUtil.java 裏,使用時請記錄調用 init(Context context) 方法進行初始化,建議在 Application.onCreate() 中進行,否則會造成內存泄漏。
/**

  • 剪貼板工具類
  • http://developer.android.com/guide/topics/text/copy-paste.html
  • Created by littlejie on 2016/4/15.
    */

    
    public class ClipboardUtil {
    
    public static final String TAG = ClipboardUtil.class.getSimpleName();
    
    private static final String MIME_CONTACT = "vnd.android.cursor.dir/person";
    /**
     * 由於系統剪貼板在某些情況下會多次調用,但調用間隔基本不會超過5ms
     * 考慮到用戶操作,將閾值設爲100ms,過濾掉前幾次無效回調
     */
    private static final int THRESHOLD = 100;
    
    private Context mContext;
    private static ClipboardUtil mInstance;
    private ClipboardManager mClipboardManager;
    
    private Handler mHandler = new Handler();
    
    private ClipboardManager.OnPrimaryClipChangedListener onPrimaryClipChangedListener = new ClipboardManager.OnPrimaryClipChangedListener() {
        @Override
        public void onPrimaryClipChanged() {
            mHandler.removeCallbacks(mRunnable);
            mHandler.postDelayed(mRunnable, THRESHOLD);
        }
    };
    
    private ClipboardChangedRunnable mRunnable = new ClipboardChangedRunnable();
    
    private List<OnPrimaryClipChangedListener> mOnPrimaryClipChangedListeners = new ArrayList<>();
    
    /**
     * 自定義 OnPrimaryClipChangedListener
     * 用於處理某些情況下系統多次調用 onPrimaryClipChanged()
     */
    public interface OnPrimaryClipChangedListener {
        void onPrimaryClipChanged(ClipboardManager clipboardManager);
    }
    
    private class ClipboardChangedRunnable implements Runnable {
    
        @Override
        public void run() {
            for (OnPrimaryClipChangedListener listener : mOnPrimaryClipChangedListeners) {
                listener.onPrimaryClipChanged(mClipboardManager);
            }
        }
    }
    
    private ClipboardUtil(Context context) {
        mContext = context;
        mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        mClipboardManager.addPrimaryClipChangedListener(onPrimaryClipChangedListener);
    }
    
    /**
     * 單例。暫時不清楚此處傳 Activity 的 Context 是否會造成內存泄漏
     * 建議在 Application 的 onCreate 方法中實現
     *
     * @param context
     * @return
     */
    public static ClipboardUtil init(Context context) {
        if (mInstance == null) {
            mInstance = new ClipboardUtil(context);
        }
        return mInstance;
    }
    
    /**
     * 獲取ClipboardUtil實例,記得初始化
     *
     * @return
     */
    public static ClipboardUtil getInstance() {
        return mInstance;
    }
    
    public void addOnPrimaryClipChangedListener(OnPrimaryClipChangedListener listener) {
        if (!mOnPrimaryClipChangedListeners.contains(listener)) {
            mOnPrimaryClipChangedListeners.add(listener);
        }
    }
    
    public void removeOnPrimaryClipChangedListener(OnPrimaryClipChangedListener listener) {
        mOnPrimaryClipChangedListeners.remove(listener);
    }
    
    /**
     * 判斷剪貼板內是否有數據
     *
     * @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 android.content.ClipDescription#MIMETYPE_TEXT_PLAIN},
     *                 {@link android.content.ClipDescription#MIMETYPE_TEXT_HTML},
     *                 {@link android.content.ClipDescription#MIMETYPE_TEXT_URILIST},
     *                 {@link android.content.ClipDescription#MIMETYPE_TEXT_INTENT}.
     * @param items    放入剪貼板中的數據
     */
    public void copyMultiple(String label, String mimeType, List<ClipData.Item> items) {
        if (items == null) {
            throw new NullPointerException("items is null");
        }
        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 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(null);
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章