【Android 界面】剪切板的基本使用

【Android 界面】剪切板的基本使用,第1张

Android 提供了一个强大的基于剪贴板的框架,用于复制和粘贴。它支持简单和复杂的数据类型,包括文本字符串、复杂数据结构、文本、二进制流数据,甚至应用资源。简单的文本数据直接存储在剪贴板中,而复杂的数据则存储为引用,执行粘贴 *** 作的应用使用 Content Provider 对其进行解析。复制和粘贴既可以在应用内进行,也可以在实现了该框架的应用之间进行。

剪切板类 ClipboardManager

在 Android 系统中,系统剪贴板由全局 ClipboardManager 类表示。您不能直接实例化此类;而是应通过调用 getSystemService(CLIPBOARD_SERVICE)获取对它的引用。

ClipData、ClipData.Item 和 ClipDescription

如需将数据添加到剪贴板,您需要创建一个 ClipData 对象,其中包含数据的说明ClipDescription和数据本身ClipData.Item。剪贴板一次只保留一个 ClipData。ClipData 包含一个 ClipDescription 对象以及一个或多个 ClipData.Item 对象。

ClipDescription对象包含关于剪切的元数据。具体来说,它包含剪切的数据的可用 MIME 类型数组。当您将一个剪切放到剪贴板中时,系统会向粘贴应用提供此信息。粘贴应用可以检查该信息,以了解自己能否处理该剪贴数据。

ClipData.Item对象包含 text、URI 或 intent 数据:

Text

一个文本字符串。您可以直接将字符串放入剪贴对象中,然后将剪贴对象放到剪贴板中。如需粘贴字符串,您需要从剪贴板获取剪贴对象,然后将字符串复制到应用的存储空间。

URI

一个 Uri 对象,表示任何形式的 URI。它主要适用于从 Content Provider 复制复杂的数据。如需复制数据,您需要将一个 Uri 对象放入一个剪贴对象中,并将该剪贴对象放到剪贴板中。如需粘贴数据,您需要获取该剪贴对象,获取 Uri 对象,将其解析为数据源(例如某个 Content Provider),然后将数据从源中复制到应用的存储空间。

Intent

一个 Intent。它支持复制应用快捷方式。如需复制数据,您需要创建一个 Intent,将其放入一个剪贴对象中,然后将该剪贴对象放到剪贴板中。如需粘贴数据,您需要获取该剪贴对象,然后将 Intent 对象复制到您应用的内存区域。

注意:剪贴板一次只保留一个剪贴对象。当应用将一个剪贴对象放到剪贴板时,上一个剪贴对象会消失。不过,您可以向一个剪切添加多个 ClipData.Item 对象。这让用户可以将多个选择复制和粘贴为一个剪切。例如,如果您有一个列表微件,可让用户一次选择多个项,则您可以同时将所有这些项复制到剪贴板。为此,您需要分别为每个列表项创建一个 ClipData.Item,然后将 ClipData.Item 对象添加到 ClipData 对象。
此外,您可能还希望无论剪贴板中的数据采用何种形式,用户都可以粘贴文本。为此,您可以将剪贴板数据强制转换为文本表示形式,然后粘贴相应文本。

ClipData 便捷方法

ClipData 类提供静态便捷方法,用于创建具有单个 ClipData.Item 对象和一个简单 ClipDescription 对象的 ClipData 对象:

newPlainText(label, text)

返回一个ClipData 对象,该对象的单个 ClipData.Item对象包含一个文本字符串。ClipDescription 对象的标签设置为 label。ClipDescription 中的单一 MIME 类型为 MIMETYPE_TEXT_PLAIN
使用newPlainText()可从文本字符串创建剪切。

newUri(resolver, label, URI)

返回一个 ClipData 对象,该对象的单个 ClipData.Item 对象包含一个 URIClipDescription对象的标签设置为label。如果该 URI 是内容 URI(Uri.getScheme() 返回 content:),则此方法会使用 resolver 中提供的 ContentResolver对象从 Content Provider 检索可用的 MIME 类型,并将其存储在 ClipDescription 中。对于不是 content: URIURI,此方法会将 MIME 类型设置为 MIMETYPE_TEXT_URILIST。
使用newUri()可从 URI(尤其是 content: URI)创建剪切。

newIntent(label, intent)

返回一个 ClipData对象,该对象的单个ClipData.Item对象包含一个 IntentClipDescription对象的标签设置为 labelMIME类型设置为 MIMETYPE_TEXT_INTENT
使用 newIntent() 可从 Intent 对象创建剪切。

将剪贴板数据强制转换为文本

即使您的应用仅处理文本,您也可以从剪贴板复制非文本数据,只需使用 ClipData.Item.coerceToText()方法对其进行转换即可。

此方法可将ClipData.Item中的数据转换为文本并返回一个 CharSequence。ClipData.Item.coerceToText() 返回的值基于 ClipData.Item 中的数据的形式:

Text

如果 ClipData.Item是一个文本(getText() 不为 null),则 coerceToText() 返回该文本。

URI

如果ClipData.Item 是一个 URI(getUri() 不为 null),则 coerceToText() 会尝试将其作为内容 URI 使用:

如果此 URI 是内容 URI,并且提供程序可以返回文本流,则 coerceToText() 返回文本流。如果此 URI 不是内容 URI,或者是内容 URI 但提供程序不提供文本流,则 coerceToText() 返回此 URI 的 Uri.toString() 返回的表示形式。 Intent

如果ClipData.Item 是一个 Intent(getIntent() 不为 null),则 coerceToText() 会将其转换为 Intent URI 并返回。该表示形式与 Intent.toUri(URI_INTENT_SCHEME) 返回的表示形式相同。

剪切板框架


图 1 汇总了剪贴板框架。如需复制数据,应用需要将一个 ClipData 对象放到 ClipboardManager 全局剪贴板中。ClipData 包含一个或多个 ClipData.Item 对象和一个 ClipDescription 对象。如需粘贴数据,应用需要获取 ClipData,从 ClipDescription 获取其 MIME 类型,然后从 ClipData.Item 或 ClipData.Item 引用的 Content Provider 获取数据。

复制到剪贴板

如前所述,如需将数据复制到剪贴板,您需要获取全局 ClipboardManager 对象的句柄,创建一个 ClipData 对象,向其中添加一个 ClipDescription 和一个或多个 ClipData.Item 对象,然后将已完成的 ClipData 对象添加到 ClipboardManager 对象。以下过程对此进行了详细介绍:

如果您要使用内容 URI 复制数据,请设置一个 Content Provider。

获取系统剪贴板:

// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)
        getSystemService(Context.CLIPBOARD_SERVICE);
将数据复制到新的 ClipData 对象:
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");

——————————————————————————————————————————————————————

// Creates a Uri based on a base Uri and a record ID based on the contact's last name
// Declares the base URI string
private static final String CONTACTS = "content://com.example.contacts";

// Declares a path string for URIs that you use to copy data
private static final String COPY_PATH = "/copy";

// Declares the Uri to paste to the clipboard
Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);

...

// Creates a new URI clip object. The system uses the anonymous getContentResolver() object to
// get MIME types from provider. The clip object's label is "URI", and its data is
// the Uri previously created.
ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
——————————————————————————————————————————————————————
// Creates the Intent
Intent appIntent = new Intent(this, com.example.demo.myapplication.class);

...

// Creates a clip object with the Intent in it. Its label is "Intent" and its data is
// the Intent object created previously
ClipData clip = ClipData.newIntent("Intent", appIntent);

将新的剪贴对象放到剪贴板中
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
从剪贴板粘贴

如前所述,您可以通过以下方法从剪贴板中粘贴数据:获取全局剪贴板对象,获取剪贴对象,查看其数据,然后将数据从剪贴对象复制到您自己的存储空间(如果可以)。本部分将介绍在应用粘贴数据时可能会出现的通知,并且还会详细说明了如何粘贴三种形式的剪贴板数据。

在应用访问剪贴板数据时显示系统通知

在 Android 12(API 级别 31)及更高版本中,系统通常会在应用调用 getPrimaryClip() 时显示消息框消息。该消息包含以下格式的文本:

	APP pasted from your clipboard

在应用执行以下某一项 *** 作时,系统不会显示消息框消息:

通过您自己的应用访问 ClipData。通过特定应用反复访问ClipData。只有在您的应用首次访问该应用的数据时,系统才会显示消息框。检索剪贴对象的元数据,例如,通过调用getPrimaryClipDescription()(而非 getPrimaryClip())进行检索。 粘贴纯文本

如需粘贴纯文本,首先请获取全局剪贴板并验证它能否返回纯文本。然后获取剪贴对象,并使用 getText() 将其文本复制到您自己的存储空间,如以下过程所述:

使用 getSystemService(CLIPBOARD_SERVICE)获取全局 ClipboardManager 对象。另外,声明一个全局变量以包含粘贴的文本:
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String pasteData = "";
接下来,确定您是否应在当前 Activity 中启用或停用“粘贴”选项。您应验证剪贴板是否包含剪切,以及您是否可以处理剪切所代表的数据类型
// Gets the ID of the "paste" menu item
MenuItem pasteItem = menu.findItem(R.id.menu_paste);

// If the clipboard doesn't contain data, disable the paste menu item.
// If it does contain data, decide if you can handle the data.
if (!(clipboard.hasPrimaryClip())) {

    pasteItem.setEnabled(false);

} else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {

    // This disables the paste menu item, since the clipboard has data but it is not plain text
    pasteItem.setEnabled(false);
} else {

    // This enables the paste menu item, since the clipboard contains plain text.
    pasteItem.setEnabled(true);
}
从剪贴板复制数据。只有在“粘贴”菜单项处于启用状态时,才能在项目中执行到这一步,因此您可以假设剪贴板包含纯文本。您目前还不知道它是否包含文本字符串或指向纯文本的 URI。以下代码段对此进行了测试,但它仅显示用于处理纯文本的代码:
// Responds to the user selecting "paste"
case R.id.menu_paste:

// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
 ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);

// Gets the clipboard as text.
pasteData = item.getText();

// If the string contains data, then the paste operation is done
if (pasteData != null) {
    return true;

// The clipboard does not contain text. If it contains a URI, attempts to get data from it
} else {
    Uri pasteUri = item.getUri();

    // If the URI contains something, try to get text from it
    if (pasteUri != null) {

        // calls a routine to resolve the URI and get data from it. This routine is not
        // presented here.
        pasteData = resolveUri(Uri);
        return true;
    } else {

        // Something is wrong. The MIME type was plain text, but the clipboard does not contain either
        // text or a Uri. Report an error.
        Log.e(TAG, "Clipboard contains an invalid data type");
        return false;
    }
}
从内容 URI 中粘贴数据

如果 ClipData.Item 对象包含内容 URI,并且您已确定自己可以处理它的某种 MIME 类型,请创建一个 ContentResolver,然后调用相应 Content Provider 方法以检索数据。

以下过程说明了如何基于剪贴板中的内容 URI 从 Content Provider 获取数据。它会检查提供程序中是否有应用可以使用的 MIME 类型:

声明一个全局变量以包含 MIME 类型:
// Declares a MIME type constant to match against the MIME types offered by the provider
public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
获取全局剪贴板。另外,获取一个内容解析器,以便您可以访问 Content Provider:
// Gets a handle to the Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

// Gets a content resolver instance
ContentResolver cr = getContentResolver();
从剪贴板中获取主要剪切,并获取其内容作为 URI:
// Gets the clipboard data from the clipboard
ClipData clip = clipboard.getPrimaryClip();

if (clip != 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
    Uri pasteUri = item.getUri();
通过调用 getType(Uri) 测试该 URI 是否为内容 URI。如果 Uri 不指向有效的 Content Provider,则此方法会返回 null:
// If the clipboard contains a URI reference
    if (pasteUri != null) {

        // Is this a content URI?
        String uriMimeType = cr.getType(pasteUri);
测试 Content Provider 是否支持当前应用可以理解的 MIME 类型。如果支持,请调用 ContentResolver.query() 以获取数据。返回值是 Cursor:
// If the return value is not null, the Uri is a content Uri
        if (uriMimeType != null) {

            // Does the content provider offer a MIME type that the current application can use?
            if (uriMimeType.equals(MIME_TYPE_CONTACT)) {

                // Get the data from the content provider.
                Cursor pasteCursor = cr.query(uri, null, null, null, null);

                // If the Cursor contains data, move to the first record
                if (pasteCursor != null) {
                    if (pasteCursor.moveToFirst()) {

                    // get the data from the Cursor here. The code will vary according to the
                    // format of the data model.
                    }
                }

                // close the Cursor
                pasteCursor.close();
             }
         }
     }
}
粘贴 Intent

如需粘贴 Intent,首先请获取全局剪贴板。检查 ClipData.Item 对象以了解它是否包含 Intent。然后调用 getIntent(),以将相应 Intent 复制到您自己的存储空间。以下代码段演示了此过程:

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

// Checks to see if the clip item contains an Intent, by testing to see if getIntent() returns null
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();

if (pasteIntent != null) {

    // handle the Intent

} else {

    // ignore the clipboard, or issue an error if your application was expecting an Intent to be
    // on the clipboard
}
使用Content Provider复制复杂的数据

https://developer.android.com/guide/topics/text/copy-paste.html#Provider

注意

如需为您的应用设计有效的复制和粘贴功能,请谨记以下几点:

在任何时候,剪贴板中都只能有一个剪切。任何应用在系统中执行的新的复制 *** 作都会覆盖上一个剪切。由于用户可能会离开您的应用并在执行复制 *** 作后返回,因此您不能假设剪贴板中包含用户之前在您的应用中复制的剪切。每个剪切有多个 ClipData.Item 对象的预期目的是,支持复制和粘贴多个选择,而不是一个选择的不同引用形式。您通常希望一个剪切中的所有 ClipData.Item 对象都采用相同的形式,也就是说,它们应该都是简单文本、内容 URI 或 Intent,而不是这些形式的混合。提供数据时,您可以提供不同的 MIME 表示形式。将您支持的 MIME 类型添加到 ClipDescription,然后在Content Provider中实现这些 MIME 类型。当您从剪贴板获取数据时,您的应用负责检查可用的 MIME 类型,并决定使用哪种类型(如果有)。即使剪贴板中有剪切且用户请求粘贴,您的应用也无需执行粘贴 *** 作。您应该在 MIME 类型兼容时执行粘贴 *** 作。您可以选择使用 coerceToText()(如果已选择)将剪贴板中的数据强制转换成文本。如果您的应用支持多种可用的 MIME 类型,您可以允许用户选择要使用哪一种。在android Q以上的手机上当 activity 页面失去焦点的时候是获取不到 剪贴板的数据的,直接返回null。Android Q中只有当应用处于可交互情况(默认输入法本身就可交互)才能访问剪切板和监听剪切板变化,在onResume回调也无法直接访问剪切板,这么做的好处是避免了一些应用后台疯狂监听响应剪切板的内容,疯狂d窗。因此如果还需要监听剪切板,可以使用应用生命周期回调,监听APP后台返回,延迟几毫秒访问剪切板,再保存最后一次访问得到的剪切板内容,每次都比较一下是否有变化,再进行下一步 *** 作。
new Handler().postDelayed(new Runnable() {
    
    public void run() {
    
        cm = (ClipboardManager) MainActivity.this.getSystemService(Context.CLIPBOARD_SERVICE);
        if (!cm.hasPrimaryClip()) {
    
            return;
        }
        //剪切板 *** 作
        ...
}, 1000);

或者:

public class StatisticActivityLifecycleCallback implements Application.ActivityLifecycleCallbacks {
    private boolean isNullData = false; // 是否拿到剪贴板数据

  @Override
  public void onActivityStarted(final Activity activity){
    ....
  
    ClipboardManager clipboardManager = (ClipboardManager) MSApplication.mContext.getSystemService(Context.CLIPBOARD_SERVICE);
    ClipData clipData = clipboardManager.getPrimaryClip();
    
    if(clipData == null) {
    	isNullData = true;
        // 这里获取剪贴板数据
    } else {
    	isNullData = false;
    }
    ....
  }
  
  @Override
  public void onActivityResumed(Activity activity) {
  	// 这里重新获取一次剪贴板数据
  }
}

参考文章:
《Android 官方文档》
android 剪切板遇到的坑
android Q以上剪贴板的坑
Android10填坑适配指南,实际经验代码,拒绝翻译

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/992343.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存