大家好,欢迎来到IT知识分享网。
1 Android剪切板简介
Android 剪贴板是一个系统级服务,它允许应用程序之间共享文本、图像、二进制数据等多种形式的信息。用户可以通过常见的复制和粘贴操作,在不同的应用之间传递数据。该设计考虑到了易用性和灵活性,使得开发者可以轻松地为自己的应用实现复制粘贴功能,同时它也强调了数据的安全性,确保剪贴板内容不会被未授权的应用访问。
接下来从剪切板的框架、数据类型处理、剪切板局限性、MIME类型说明、剪切板高效复制粘贴设计角度来先详细介绍剪切板。
剪贴板框架说明:Android的剪贴板框架由几个关键类组成,包括ClipboardManager、ClipData、ClipData.Item和ClipDescription。具体如下:
- ClipboardManager:这是系统剪贴板的代表,通过调用getSystemService(CLIPBOARD_SERVICE)来获取对它的引用。
- ClipData:这是一个包含数据说明(ClipDescription)和数据本身(ClipData.Item)的容器,代表了剪贴板中的一组数据。
- ClipData.Item:这是实际的数据项,可以包含文本、URI或Intent数据。
- ClipDescription:这个类包含关于ClipData的元数据,例如它包含的可用MIME类型数组。
数据类型处理:根据数据的类型(文本、URI、Intent等),可能需要执行不同的操作来处理或使用这些数据。例如,如果数据是文本,可以直接使用;如果数据是URI,可能需要解析它以获取实际的数据源;如果数据是Intent,可能需要执行相应的操作。
剪贴板的局限性:剪贴板只能保留一个ClipData对象。当一个新的ClipData对象被放入剪贴板时,旧的ClipData对象将被自动清除,这意味着需要确保每次只放置一个有效的ClipData对象在剪贴板上。
MIME类型说明:在Android剪贴板中,MIME类型用于表示数据的格式。例如,文本数据通常使用text/plain MIME类型,而HTML文本使用text/html。对于URI列表,使用的是text/uri-list,而对于Intent数据,则使用text/vnd.android.intent。
剪切板高效复制粘贴设计:设计有效的复制粘贴功能时,需要注意以下几点:
- 任何时间都只有一个clip对象在剪贴板里,新的复制操作都会覆盖前一个clip对象。
- 一个clip对象中的多个ClipData.Item对象是为了支持多选项的复制粘贴,而不是为了支持单选的多种形式。
- 当提供数据时,可以提供不同的MIME表达方式,并将支持的MIME类型加入到ClipDescription中。
- 安全和隐私:在使用剪贴板时,开发者应注意数据的安全性和隐私性,避免敏感信息的不当共享。
2 剪切板设计实战
实现功能:实现2个按键:一个功能是复制内容(文本和图片)到剪切板,另一个功能是从剪切板中获取粘贴内容到本地并通过TextView和Image来显示。
关于该程序,自定义 ClipboardUtils.java 的代码实现如下所示:
package com.example.myapplication3; import android.content.Context; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ClipDescription; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.concurrent.atomic.AtomicReference; import android.graphics.BitmapFactory; import android.graphics.Bitmap; import android.provider.MediaStore; import android.net.Uri; import android.util.Log; public class ClipboardUtils { private static final String TAG = "ClipboardUtils"; public static final int CLIPBOARD_DATA_TYPE_TEXT = 0; public static final int CLIPBOARD_DATA_TYPE_IMAGE = 1; public static final int CLIPBOARD_DATA_TYPE_UNSUPPORT = -1; //private static final int ERROR_INDEX_OVERRIDE = -2; //private long mCallbackPtr = 0; private ClipboardManager mClipboardManager = null; //private ClipData mSetClipData = null; //private ClipData mGetClipData = null; static Context context; public ClipboardUtils() { if(context == null){ Log.e(TAG,"set Content first"); return; } mClipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); if(mClipboardManager == null){ Log.e(TAG, "get ClipboardManager error"); } } public static void setContext(Context inContext) { context = inContext; } / * 获取 ClipboardUtils 单例 */ public static ClipboardUtils getInstance() { return Holder.sInstance; } private static class Holder { private static ClipboardUtils sInstance = new ClipboardUtils(); } public static AtomicReference<ClipData> createClipdataRef(){ return new AtomicReference<>(null); } / * 剪切板是否有数据 */ public boolean hasClip() { Log.d(TAG, "java call:hasclip"); return mClipboardManager.hasPrimaryClip(); } / * 清除剪切板数据 */ public int clearClip() { Log.d(TAG, "java call:clearClip"); mClipboardManager.clearPrimaryClip(); return 0; } / * 添加文本类型Item数据 */ public int addTextItem(AtomicReference<ClipData> clipDataRef, String text){ try { if (clipDataRef.get() == null) { ClipData clipData = ClipData.newPlainText("text_label", text); clipDataRef.set(clipData); }else{ ClipData.Item item = ClipData.newPlainText("text_label", text).getItemAt(0); clipDataRef.get().addItem(item); } Log.e(TAG,"lenTextItem1="+clipDataRef.get().getItemCount()); return 0; } catch (Exception e) { Log.e(TAG, "Error adding text item to ClipData"); return -1; } } / * 添加图片类型Item数据 */ public int addImageItem(AtomicReference<ClipData> clipDataRef, Bitmap image) { try { ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.PNG, 100, stream); String path = MediaStore.Images.Media.insertImage(context.getContentResolver(),image,"ImageX",null); if (clipDataRef.get() == null) { ClipData clipData = ClipData.newRawUri("image_label", Uri.parse(path)); clipDataRef.set(clipData); }else{ ClipData.Item item = ClipData.newRawUri("image_label", Uri.parse(path)).getItemAt(0); clipDataRef.get().addItem(item); } return 0; } catch (Exception e) { Log.e(TAG, "Error adding image item to ClipData"); return -1; } } / * 根据索引获取剪贴板中的文本项 */ public String getTextItem(AtomicReference<ClipData> clipDataRef, int index) { try { if (clipDataRef.get() != null && index >= 0 && index < clipDataRef.get().getItemCount()) { ClipData.Item item = clipDataRef.get().getItemAt(index); int type = getItemType(clipDataRef,index); if(type!=CLIPBOARD_DATA_TYPE_TEXT){ return null; } // 直接返回文本内容,如果获取成功 return item.getText().toString(); }else { Log.d(TAG, "index override"); } } catch (Exception e) { Log.e(TAG, "Error getting text item from ClipData"); } // 如果索引无效或出现异常,返回null表示获取失败 return null; } / * 根据索引获取剪贴板中的图片项 */ public Bitmap getImageItem(AtomicReference<ClipData> clipDataRef, int index) { try { if (clipDataRef.get()!= null && index >= 0 && index < clipDataRef.get().getItemCount()) { ClipData.Item item = clipDataRef.get().getItemAt(index); int type = getItemType(clipDataRef,index); if(type!=CLIPBOARD_DATA_TYPE_IMAGE){ return null; } if (item.getUri() != null) { InputStream inputStream = context.getContentResolver().openInputStream(item.getUri()); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); if (bitmap != null) { return bitmap; } } }else { Log.d(TAG, "index override"); } } catch (Exception e) { Log.e(TAG, "Error getting image item from ClipData"); } return null; // 索引无效或数据类型不匹配 } / * 获取剪切板中Item的数量 */ public int getItemCount(AtomicReference<ClipData> clipDataRef){ return clipDataRef.get().getItemCount(); } / * 将当前的mGetClipData设置为剪贴板的主内容 */ public void setPrimaryClip(AtomicReference<ClipData> clipDataRef) { if (mClipboardManager != null && clipDataRef.get() != null) { mClipboardManager.setPrimaryClip(clipDataRef.get()); } } / * 获取剪贴板中主剪贴板的内容 */ public void getPrimaryClip(AtomicReference<ClipData> clipDataRef) { if (mClipboardManager != null && mClipboardManager.hasPrimaryClip()) { ClipData clipdata= mClipboardManager.getPrimaryClip(); clipDataRef.set(clipdata); } } / * 获取Item类型 */ public int getItemType(AtomicReference<ClipData> clipDataRef,int index) { if (clipDataRef.get() != null && index >= 0 && index < clipDataRef.get().getItemCount()) { ClipData.Item item = clipDataRef.get().getItemAt(index); Uri uri = item.getUri(); if(uri == null){ return CLIPBOARD_DATA_TYPE_TEXT; }else{ String mimeType = context.getContentResolver().getType(item.getUri()); if (mimeType != null) { if (mimeType.startsWith("image/")) { return CLIPBOARD_DATA_TYPE_IMAGE; }else{ return CLIPBOARD_DATA_TYPE_UNSUPPORT; } } } } return -2; } }
基于对 ClipboardUtils 的调用,MainActivity.java实现为:
package com.example.myapplication3; import androidx.appcompat.app.AppCompatActivity; import android.content.ClipData; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import java.util.concurrent.atomic.AtomicReference; public class MainActivity extends AppCompatActivity { private Button btnGetImage; private Button btnSetImage; private ImageView imageView; private TextView textView; Bitmap bitmap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnGetImage = findViewById(R.id.btnGetImage); btnSetImage = findViewById(R.id.btnSetImage); imageView = findViewById(R.id.imageView); textView = findViewById(R.id.textView); bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test5); //imageView.setImageBitmap(bitmap); ClipboardUtils.setContext(getApplication()); // 设置点击监听器,从剪贴板获取图片 btnGetImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getClipFromClipboard(); } }); // 设置点击监听器,将图片设置到剪贴板 btnSetImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setClipToClipboard(); } }); } private void getClipFromClipboard() { AtomicReference<ClipData> clipDataRef = new AtomicReference<>(null); ClipboardUtils clipboardUtils = ClipboardUtils.getInstance(); clipboardUtils.getPrimaryClip(clipDataRef); Log.e("clip","len="+clipboardUtils.getItemCount(clipDataRef)); for(int i =0;i<clipboardUtils.getItemCount(clipDataRef);i++){ if(clipboardUtils.getItemType(clipDataRef,i) == clipboardUtils.CLIPBOARD_DATA_TYPE_TEXT){ String text = clipboardUtils.getTextItem(clipDataRef,i); textView.setText(text); }else if(clipboardUtils.getItemType(clipDataRef,i) == clipboardUtils.CLIPBOARD_DATA_TYPE_IMAGE){ Bitmap bitmap1 = clipboardUtils.getImageItem(clipDataRef,i); imageView.setImageBitmap(bitmap); }else{ Log.e("clip","not support format"); } } } private void setClipToClipboard() { ClipboardUtils clipboardUtils = ClipboardUtils.getInstance(); AtomicReference<ClipData> clipDataRef = ClipboardUtils.createClipdataRef(); clipboardUtils.addTextItem(clipDataRef, "test text1"); clipboardUtils.addImageItem(clipDataRef,bitmap); clipboardUtils.setPrimaryClip(clipDataRef); } }
对应的layout xml代码配置为:
<?xml version="1.0" encoding="utf-8"?> <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="match_parent" android:orientation="vertical" android:padding="16dp" tools:context=".MainActivity"> <Button android:id="@+id/btnGetImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Get Image from Clipboard" /> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_gravity="center" /> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:scaleType="centerInside" /> <Button android:id="@+id/btnSetImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Set Image to Clipboard" /> </LinearLayout>
最后在drawable中添加一张图片用于测试,一个基本的剪切板功能就设计完成了。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/127505.html