Android给图片添加水印
1. 前言
ps:最近在项目执行过程中有这样一个需求,要求拍完照的图片必须达到以上的效果。需求分析:
- 使用用预览布局surfaceview,在不局上方使用控件的方式来进行设计,最后通过截图的方式将画面进行保存。
- 使用图片添加水印的方式来完成。
2. 方法1 使用surfaceview
我心想这不简单吗?于是开始一顿balabala的操作,结果到最后一步时发现,surfaceview居然不能进行截图,截图下来的图片居然是一张黑色的。简单地说这是因为surfaceview的特性决定的,我们知道安卓中唯一可以在子线程中进行绘制的view就只有surfaceview了。他可以独立于子线程中绘制,不会导致主线程的卡顿,至于造成surfaceview黑屏的原因,可以移步这里 android视图surfaceview的实现原理分析。如果非要使用此方式时还是有三种思路来进行解决: 采用三种思路:
1. 获取源头视频的截图作为surfaceview的截图
2. 获取surfaceview的画布canvas,将canvas保存成bitmap
3. 直接截取整个屏幕,然后在截图surfaceview位置的图
但是我觉得这种方式太过繁琐,所以选择用添加水印的式来完成。
3. 方法2 给拍照下来的图片添加水印
第一步:获取拍照权限
<!--相机权限--> <uses-permission android:name="android.permission.camera" /> <!--访问外部权限--> <uses-permission android:name="android.permission.read_external_storage" />
这里使用到郭霖大佬的开源库permissionx获取权限:
permissionx.init(this) .permissions(manifest.permission.camera, manifest.permission.record_audio) .onexplainrequestreason { scope, deniedlist -> val message = "需要您同意以下权限才能正常使用" scope.showrequestreasondialog(deniedlist, message, "确定", "取消") } .request { allgranted, grantedlist, deniedlist -> if (allgranted) { opencamera() } else { toast.maketext(activity, "您拒绝了如下权限:$deniedlist", toast.length_short).show() } }
第二步:拍照
android 6.0以后,相机权限需要动态申请。
// 申请相机权限的requestcode private static final int permission_camera_request_code = 0x00000012; /** * 检查权限并拍照。 * 调用相机前先检查权限。 */ private void checkpermissionandcamera() { int hascamerapermission = contextcompat.checkselfpermission(getapplication(), manifest.permission.camera); if (hascamerapermission == packagemanager.permission_granted) { //有调起相机拍照。 opencamera(); } else { //没有权限,申请权限。 activitycompat.requestpermissions(this,new string[]{manifest.permission.camera}, permission_camera_request_code); } } /** * 处理权限申请的回调。 */ @override public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) { if (requestcode == permission_camera_request_code) { if (grantresults.length > 0 && grantresults[0] == packagemanager.permission_granted) { //允许权限,有调起相机拍照。 opencamera(); } else { //拒绝权限,弹出提示框。 toast.maketext(this,"拍照权限被拒绝",toast.length_long).show(); } } }
调用相机进行拍照
申请权限后,就可以调起相机拍照了。调用相机只需要调用startactivityforresult传一个intent就可以了,但是这个intent需要传递一个uri,用于保存拍出来的图片,创建这个uri时,各个android版本有所不同,需要进行版本兼容。
//用于保存拍照图片的uri private uri mcamerauri; // 用于保存图片的文件路径,android 10以下使用图片路径访问图片 private string mcameraimagepath; // 是否是android 10以上手机 private boolean isandroidq = build.version.sdk_int >= android.os.build.version_codes.q; /** * 调起相机拍照 */ private void opencamera() { intent captureintent = new intent(mediastore.action_image_capture); // 判断是否有相机 if (captureintent.resolveactivity(getpackagemanager()) != null) { file photofile = null; uri photouri = null; if (isandroidq) { // 适配android 10 photouri = createimageuri(); } else { try { photofile = createimagefile(); } catch (ioexception e) { e.printstacktrace(); } if (photofile != null) { mcameraimagepath = photofile.getabsolutepath(); if (build.version.sdk_int >= build.version_codes.n) { //适配android 7.0文件权限,通过fileprovider创建一个content类型的uri photouri = fileprovider.geturiforfile(this, getpackagename() + ".fileprovider", photofile); } else { photouri = uri.fromfile(photofile); } } } mcamerauri = photouri; if (photouri != null) { captureintent.putextra(mediastore.extra_output, photouri); captureintent.addflags(intent.flag_grant_write_uri_permission); startactivityforresult(captureintent, camera_request_code); } } } /** * 创建图片地址uri,用于保存拍照后的照片 android 10以后使用这种方法 */ private uri createimageuri() { string status = environment.getexternalstoragestate(); // 判断是否有sd卡,优先使用sd卡存储,当没有sd卡时使用手机存储 if (status.equals(environment.media_mounted)) { return getcontentresolver().insert(mediastore.images.media.external_content_uri, new contentvalues()); } else { return getcontentresolver().insert(mediastore.images.media.internal_content_uri, new contentvalues()); } } /** * 创建保存图片的文件 */ private file createimagefile() throws ioexception { string imagename = new simpledateformat("yyyymmdd_hhmmss", locale.getdefault()).format(new date()); file storagedir = getexternalfilesdir(environment.directory_pictures); if (!storagedir.exists()) { storagedir.mkdir(); } file tempfile = new file(storagedir, imagename); if (!environment.media_mounted.equals(environmentcompat.getstoragestate(tempfile))) { return null; } return tempfile; }
接收拍照结果
@override protected void onactivityresult(int requestcode, int resultcode, @nullable intent data) { super.onactivityresult(requestcode, resultcode, data); if (requestcode == camera_request_code) { if (resultcode == result_ok) { if (isandroidq) { // android 10 使用图片uri加载 ivphoto.setimageuri(mcamerauri); } else { // 使用图片路径加载 ivphoto.setimagebitmap(bitmapfactory.decodefile(mcameraimagepath)); } } else { toast.maketext(this,"取消",toast.length_long).show(); } } }
注意:
这两需要说明一下,android 10由于文件权限的关系,显示手机储存卡里的图片不能直接使用图片路径,需要使用图片uri加载。
另外虽然我在这里对android 10和10以下的手机使用了不同的方式创建uri 和加载图片,但其实android 10创建uri的方式和使用uri加载图片的方式在10以下的手机是同样适用的。 android 7.0需要配置文件共享。
<provider android:name="androidx.core.content.fileprovider" android:authorities="${applicationid}.fileprovider" android:exported="false" android:granturipermissions="true"> <meta-data android:name="android.support.file_provider_paths" android:resource="@xml/file_paths" /> </provider>
在res目录下创建文件夹xml ,放置一个文件file_paths.xml(文件名可以随便取),配置需要共享的文件目录,也就是拍照图片保存的目录。
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <!-- 这个是保存拍照图片的路径,必须配置。 --> <external-files-path name="images" path="pictures" /> </paths> </resources>
第三步:给拍照后得到的图片添加水印
@override protected void onactivityresult(int requestcode, int resultcode, @nullable intent data) { super.onactivityresult(requestcode, resultcode, data); if (requestcode == camera_request_code) { if (resultcode == result_ok) { bitmap mp; if (isandroidq) { // android 10 使用图片uri加载 mp = mediastore.images.media.getbitmap(this.contentresolver, t.uri); } else { // android 10 以下使用图片路径加载 mp = bitmapfactory.decodefile(uri); } //对图片添加水印 这里添加一张图片为示例: imageutil.drawtexttolefttop(this,mp,"示例文字",30,r.color.black,20,30) } else { toast.maketext(this,"取消",toast.length_long).show(); } } }
这里使用到一个imageutil工具类,我在这里贴上。如果需要使用可以直接拿走~
public class imageutil { /** * 设置水印图片在左上角 * * @param context 上下文 * @param src * @param watermark * @param paddingleft * @param paddingtop * @return */ public static bitmap createwatermasklefttop(context context, bitmap src, bitmap watermark, int paddingleft, int paddingtop) { return createwatermaskbitmap(src, watermark, dp2px(context, paddingleft), dp2px(context, paddingtop)); } private static bitmap createwatermaskbitmap(bitmap src, bitmap watermark, int paddingleft, int paddingtop) { if (src == null) { return null; } int width = src.getwidth(); int height = src.getheight(); //创建一个bitmap bitmap newb = bitmap.createbitmap(width, height, bitmap.config.argb_8888);// 创建一个新的和src长度宽度一样的位图 //将该图片作为画布 canvas canvas = new canvas(newb); //在画布 0,0坐标上开始绘制原始图片 canvas.drawbitmap(src, 0, 0, null); //在画布上绘制水印图片 canvas.drawbitmap(watermark, paddingleft, paddingtop, null); // 保存 canvas.save(canvas.all_save_flag); // 存储 canvas.restore(); return newb; } /** * 设置水印图片在右下角 * * @param context 上下文 * @param src * @param watermark * @param paddingright * @param paddingbottom * @return */ public static bitmap createwatermaskrightbottom(context context, bitmap src, bitmap watermark, int paddingright, int paddingbottom) { return createwatermaskbitmap(src, watermark, src.getwidth() - watermark.getwidth() - dp2px(context, paddingright), src.getheight() - watermark.getheight() - dp2px(context, paddingbottom)); } /** * 设置水印图片到右上角 * * @param context * @param src * @param watermark * @param paddingright * @param paddingtop * @return */ public static bitmap createwatermaskrighttop(context context, bitmap src, bitmap watermark, int paddingright, int paddingtop) { return createwatermaskbitmap(src, watermark, src.getwidth() - watermark.getwidth() - dp2px(context, paddingright), dp2px(context, paddingtop)); } /** * 设置水印图片到左下角 * * @param context * @param src * @param watermark * @param paddingleft * @param paddingbottom * @return */ public static bitmap createwatermaskleftbottom(context context, bitmap src, bitmap watermark, int paddingleft, int paddingbottom) { return createwatermaskbitmap(src, watermark, dp2px(context, paddingleft), src.getheight() - watermark.getheight() - dp2px(context, paddingbottom)); } /** * 设置水印图片到中间 * * @param src * @param watermark * @return */ public static bitmap createwatermaskcenter(bitmap src, bitmap watermark) { return createwatermaskbitmap(src, watermark, (src.getwidth() - watermark.getwidth()) / 2, (src.getheight() - watermark.getheight()) / 2); } /** * 给图片添加文字到左上角 * * @param context * @param bitmap * @param text * @return */ public static bitmap drawtexttolefttop(context context, bitmap bitmap, string text, int size, int color, int paddingleft, int paddingtop) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.settextsize(dp2px(context, size)); rect bounds = new rect(); paint.gettextbounds(text, 0, text.length(), bounds); return drawtexttobitmap(context, bitmap, text, paint, bounds, dp2px(context, paddingleft), dp2px(context, paddingtop) + bounds.height()); } /** * 绘制文字到右下角 * * @param context * @param bitmap * @param text * @param size * @param color * @return */ public static bitmap drawtexttorightbottom(context context, bitmap bitmap, string text, int size, int color, int paddingright, int paddingbottom) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.settextsize(dp2px(context, size)); rect bounds = new rect(); paint.gettextbounds(text, 0, text.length(), bounds); return drawtexttobitmap(context, bitmap, text, paint, bounds, bitmap.getwidth() - bounds.width() - dp2px(context, paddingright), bitmap.getheight() - dp2px(context, paddingbottom)); } /** * 绘制文字到右上方 * * @param context * @param bitmap * @param text * @param size * @param color * @param paddingright * @param paddingtop * @return */ public static bitmap drawtexttorighttop(context context, bitmap bitmap, string text, int size, int color, int paddingright, int paddingtop) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.settextsize(dp2px(context, size)); rect bounds = new rect(); paint.gettextbounds(text, 0, text.length(), bounds); return drawtexttobitmap(context, bitmap, text, paint, bounds, bitmap.getwidth() - bounds.width() - dp2px(context, paddingright), dp2px(context, paddingtop) + bounds.height()); } /** * 绘制文字到左下方 * * @param context * @param bitmap * @param text * @param size * @param color * @param paddingleft * @param paddingbottom * @return */ public static bitmap drawtexttoleftbottom(context context, bitmap bitmap, string text, int size, int color, int paddingleft, int paddingbottom) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.settextsize(dp2px(context, size)); rect bounds = new rect(); paint.gettextbounds(text, 0, text.length(), bounds); return drawtexttobitmap(context, bitmap, text, paint, bounds, dp2px(context, paddingleft), bitmap.getheight() - dp2px(context, paddingbottom)); } /** * 绘制文字到中间 * * @param context * @param bitmap * @param text * @param size * @param color * @return */ public static bitmap drawtexttocenter(context context, bitmap bitmap, string text, int size, int color) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.settextsize(dp2px(context, size)); rect bounds = new rect(); paint.gettextbounds(text, 0, text.length(), bounds); return drawtexttobitmap(context, bitmap, text, paint, bounds, (bitmap.getwidth() - bounds.width()) / 2, (bitmap.getheight() + bounds.height()) / 2); } //图片上绘制文字 private static bitmap drawtexttobitmap(context context, bitmap bitmap, string text, paint paint, rect bounds, int paddingleft, int paddingtop) { android.graphics.bitmap.config bitmapconfig = bitmap.getconfig(); paint.setdither(true); // 获取跟清晰的图像采样 paint.setfilterbitmap(true);// 过滤一些 if (bitmapconfig == null) { bitmapconfig = android.graphics.bitmap.config.argb_8888; } bitmap = bitmap.copy(bitmapconfig, true); canvas canvas = new canvas(bitmap); canvas.drawtext(text, paddingleft, paddingtop, paint); return bitmap; } /** * 缩放图片 * * @param src * @param w * @param h * @return */ public static bitmap scalewithwh(bitmap src, double w, double h) { if (w == 0 || h == 0 || src == null) { return src; } else { // 记录src的宽高 int width = src.getwidth(); int height = src.getheight(); // 创建一个matrix容器 matrix matrix = new matrix(); // 计算缩放比例 float scalewidth = (float) (w / width); float scaleheight = (float) (h / height); // 开始缩放 matrix.postscale(scalewidth, scaleheight); // 创建缩放后的图片 return bitmap.createbitmap(src, 0, 0, width, height, matrix, true); } } /** * dip转pix * * @param context * @param dp * @return */ public static int dp2px(context context, float dp) { final float scale = context.getresources().getdisplaymetrics().density; return (int) (dp * scale + 0.5f); } }
4. 最终实现的效果如下
5.总结
整体来说没有什么太大的问题,添加水印的原理就是通过canvas绘制的方式将文字/图片添加到图片上。最后再将修改之后的图片呈现给用户。同时也记录下surfaceview截图黑屏的问题。
以上就是android实现添加水印功能的详细内容,更多关于android 添加水印的资料请关注其它相关文章!
上一篇: R语言给曲线添加标注和点注