Android调用相机拍照并添加水印
最近项目提出新需求,要求在拍照后在图片上添加用户信息的水印,so,楼主重新整理封装了一个简单的拍照+水印的工具类,需求不同,仅供参考。文件操作及权限做了适配,支持androidN及以上版本,放心使用。
效果演示:
如何调用相机这里就不介绍了,因为小伙伴们基本都使用过,主要介绍下实现水印的方式,实现水印效果也很简单,利用paint及canvas操作bitmap在对应位置进行绘制文本即可,首先定义画笔,设置水印颜色、大小及文本:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); paint.setTextSize(size); Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds);
定义好画笔后新建canvas对bitmap进行绘制,这里应该注意的是在Android代码里是不允许直接修改Bitmap资源文件,如果不copy一份的话会抛异常
Caused by: java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
绘制代码如下:
Bitmap.Config bitmapConfig = bitmap.getConfig(); paint.setDither(true); // 获取跟清晰的图像采样 paint.setFilterBitmap(true);// 过滤一些 if (bitmapConfig == null) {bitmapConfig = Bitmap.Config.ARGB_8888; } bitmap = bitmap.copy(bitmapConfig, true); Canvas canvas = new Canvas(bitmap); canvas.drawText(text, paddingLeft, paddingTop, paint);
我们看下drawText的源码
/** * Draw the text, with origin at (x,y), using the specified paint. The * origin is interpreted based on the Align setting in the paint. * * @param text The text to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); }
第一个和第四个参数显而易见,第二个和第三个看注释也能看懂,当然是对应的坐标轴x轴和y轴了,但是这个坐标轴到底原点在哪呢,楼主自己做了一张简单的图片供小伙伴参考,如图:
坐标原点为手机左上角,横向向右为X轴正极,纵向向下为Y轴正极,这样我们可以计算left及top来设置水印的不同位置了,楼主在util中封装了几个常用的位置,小伙伴可以查看demo中的代码。
绘制文本问题解决了,但是又一个新问题出现了,那就是如何实现多段落显示及段落间的换行,我们都知道每部手机的相机像素是不一样的,如果是把textSize写死的话,在不同分辨率的图片上显示效果很差,所以我定义了一个itemCount,每行显示的文字数,通过itemCount及图片宽度计算出textSize,比如一部手机拍出的图片宽为1080像素,itemCount为20,那么文字大小为54px,根据textSize计算总文本行数并分段,把每段文字放在list中,最后遍历List绘制水印,参考代码如下:
/** * 绘制水印 * * @param context * @param bitmap * @param waterMaskParam * @return */ private static Bitmap drawTxt(Context context, Bitmap bitmap, WaterMaskParam waterMaskParam) {int maxHeight = 0; //计算总行数 List<List<String>> msg = new ArrayList<>(); //文本 for (String str : waterMaskParam.txt) {int count = str.length() / waterMaskParam.itemCount; if (count == 0) {maxHeight++; List<String> list = new ArrayList<>(); list.add(str); msg.add(list); } else {if (str.length() % waterMaskParam.itemCount != 0) {count++; }List<String> list = new ArrayList<>(); for (int i = 0; i < count; i++) {String s = str.substring(i * waterMaskParam.itemCount, i == count - 1 ? str.length() : (i + 1) * waterMaskParam.itemCount); list.add(s); }msg.add(list); maxHeight += count; }}int txtSize = bitmap.getWidth() / waterMaskParam.itemCount; int index = msg.size() - 1; bitmap = checkBackground(waterMaskParam.location, bitmap, msg.size() * txtSize * 2.0f); for (List<String> strings : msg) {for (int i = 0; i < strings.size(); i++) {bitmap = checkType(context, bitmap, strings.get(i), txtSize, waterMaskParam, txtSize * (maxHeight--) + index * txtSize / 2); }index--; }return bitmap; }
我在最后绘制水印时,list中嵌套了一个list,外循环用来处理多段落的换行问题,内循环用来处理每个段落中的换行问题,所以我在调用绘制文本外部类传值时用的是list而不是string,及list中每个元素为一个段落,model如下:
public static class WaterMaskParam {public List<String> txt = new ArrayList<>(); //水印文字 public int itemCount = DefWaterMaskParam.ITEM_COUNT; //每行的文字数 public int txtColor = DefWaterMaskParam.TEXT_COLOR; //文字颜色 public int location = DefWaterMaskParam.Location.left_bottom; //水印位置 public WaterMaskParam() {}public WaterMaskParam(List<String> txt) {this.txt = txt; }public WaterMaskParam(List<String> txt, int itemCount, int txtColor) {this.txt = txt; this.itemCount = itemCount; this.txtColor = txtColor; } }
为了优化水印,我在水印背景处又加了印象效果,模拟器拍摄的图片,不美观见谅
背景色的实现方式与水印绘制方式大同小异:
/** * 绘制背景色 * * @param bitmap * @param maxHeight 水印最大高度 * @return */ private static Bitmap drawBackground(Bitmap bitmap, float maxHeight) {Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(newBitmap); Paint paint_b = new Paint(); paint_b.setDither(true); paint_b.setFilterBitmap(true); paint_b.setColor(Color.BLACK); paint_b.setDither(true); paint_b.setFilterBitmap(true); paint_b.setAlpha(100); canvas.drawBitmap(bitmap, 0, 0, null); canvas.drawRect(0, newBitmap.getHeight() - maxHeight, newBitmap.getWidth(), newBitmap.getHeight(), paint_b); return newBitmap; }
我封装了一个helper类用来简化调用:
public class WaterMaskHelper {private Context context; private PhotoListener photoListener; private WaterMask.WaterMaskListener waterMarkListener; public WaterMaskHelper(Context context, PhotoListener photoListener, WaterMask.WaterMaskListener waterMarkListener) {this.context = context; this.photoListener = photoListener; this.waterMarkListener = waterMarkListener; }public void startCapture() {context.startActivity(new Intent(context, PhotoCaptureActivity.class)); PhotoCaptureActivity.setWaterListener(waterMarkListener); PhotoCaptureActivity.setPhotoListener(photoListener); } }
在activity中实例helper并实现接口:
//初始化水印工具 waterMaskHelper = new WaterMaskHelper(this, this, this);
调用startCapture调用相机拍照并添加水印:
waterMaskHelper.startCapture();
暴露的接口有两个,都是在相机拍照后调用,选择照片:
//选择照片的uri,默认为下标1的元素 void onChoose(ArrayList<String> photos);
添加水印:
//拍照后调用,设置水印的基本参数 WaterMaskParam onDraw();
具体实现,注:txt为空或param为空时不绘制水印
@Override public WaterMask.WaterMaskParam onDraw() {WaterMask.WaterMaskParam param = new WaterMask.WaterMaskParam(); param.txt.add("我是一个小标题"); param.txt.add(binding.edt.getText().toString().trim()); param.location = maskLocation; param.itemCount = 30; return param; }@Override public void onChoose(ArrayList<String> photos) {uris = photos; Glide.with(MainActivity.this).load(photos.get(0)).placeholder(R.mipmap.ic_launcher).centerCrop().error(R.mipmap.ic_launcher).crossFade().into(binding.img); }
相关Demo已上传至Github, Git地址
Demo中只支持文本绘制,添加图片的实现方式其实与文字差不多,有兴趣的小伙伴可以尝试。我结合了相机及水印,想单独调用的是可以代码分离的哦,耦合度较低。
Android调用相机拍照并添加水印相关推荐
- Android中拍照完就给图片加水印,Android调用相机拍照并添加水印
最近项目提出新需求,要求在拍照后在图片上添加用户信息的水印,so,楼主重新整理封装了一个简单的拍照+水印的工具类,需求不同,仅供参考.文件操作及权限做了适配,支持androidN及以上版本,放心使用. ...
- android 调用相机拍照。适配到 Android 10
Photograph 项目地址:donkingliang/Photograph 简介: android 调用相机拍照.适配到 Android 10 更多:作者 提 Bug 标签: android ...
- Android调用相机拍照高清原图(兼容7.0)
在安卓更新7.0的版本后,要调用相机拍照获取原图则需要先把拍摄后的内容保存到目录,然后再借助provider调出来显示,相比以前可以说十分繁琐,但为了摆脱马赛克画质的困扰,为了更好的用户体验,还是硬着 ...
- Android 调用相机拍照并保存
不知不觉已经两年多已经没有写文章了,转眼间大学都要毕业了,也是有些唏嘘,今后会定期发表些文章,应该会以Android为主,也会夹杂其他领域的一些文章. 话不多说,今天做了一个小demo,就是调用相机拍 ...
- android相机拍照代码,Android 调用相机拍照,适配到Android 10
今天写的博客是关于Android调用手机相机拍照并显示图片.这是一个很常用的功能,并且这个功能在Android6.0.7.0.10.0等版本上实现都有所不同,需要对Android各个版本进行兼容适配, ...
- android 拍照换头像,Android调用相机拍照,裁剪及更换头像功能的实现
1,点击弹出popwindow,选择相机或者相册 这个就不多说了,在OnclickListener里写弹出的窗口位置和样式. 2,选择拍照功能,调用手机相机. ``` //调用相机 Intent in ...
- Android 调用相机拍照,适配到Android 10,2021必看
//拒绝权限,弹出提示框. Toast.makeText(this,"拍照权限被拒绝",Toast.LENGTH_LONG).show(); } } } 申请权限后,就可以调起相机 ...
- android调用相机拍照返回的照片大小太小,变得模糊
1.使用相机拍照默认情况下调用相机的方式: Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);startActivityForRe ...
- android 调用系统相机拍照并返回路径,Android调用相机拍照并返回路径和…
调用系统图库: Intent intent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI); ...
最新文章
- java冒泡排序_Java中的经典算法之冒泡排序(Bubble Sort)
- [SCOI2008]奖励关(期望dp)
- shell学习之常用命令总结
- NodeJS开发环境配置
- Hadoop入门(二十一)Mapreduce的求和程序
- Android Ubuntu 安装问题FAQ
- Dubbo消费者代理的调用
- java 内存溢出分析_用一段时间后java内存溢出问题分析(转)
- 十面阿里Java程序员终拿下阿里P6offer
- PyTorch中hook函数的学习笔记
- c语言课程设计报告猜数字,猜数字游戏C语言课程设计报告书.docx
- 云服务器部署 Web 项目
- sql1428N错误
- Banner打造广告自动轮播图
- 新版Jenkins关闭CSRF“HTTP ERROR 403 No valid crumb was included in the request“
- 又是一个相当 带劲的招聘起事
- php怎么检测数据类型,PHP检测数据类型的几种方法总结和技巧
- 抓取淘宝司法拍卖数据
- java中构造方法的理解,super()与构造方法,无参,有参构造方法,this()与构造方法
- 编程实用工具大全(前后端皆可用,不来瞅瞅?)
热门文章
- 这些IT经典书都是蓝色封面,你读了吗?
- 早上9.10分左右上高架被拍了!
- ygo游戏王卡组_游戏王卡组第六弹:游戏衍生卡组最风光的卡组之一,完全碾压社长...
- android videoview 播放之前短暂黑屏
- 阿里云AI训练营-机器学习3:LightGBM
- 全局描述符表GDT-第一部分
- 设计模式剖析——抽象工厂模式Abstract Factory Pattern
- mailto 参数讲解
- 用Python解析WinMerge生成的Patch文件
- android华为和小米,同样是升安卓9.0,为何华为和小米的差距这么大?