最近项目提出新需求,要求在拍照后在图片上添加用户信息的水印,so,楼主重新整理封装了一个简单的拍照+水印的工具类,需求不同,仅供参考。文件操作及权限做了适配,支持androidN及以上版本,放心使用。

效果演示:

如何调用相机这里就不介绍了,因为小伙伴们基本都使用过,主要介绍下实现水印的方式,实现水印效果也很简单,利用paint及canvas操作bitmap在对应位置进行绘制文本即可,首先定义画笔,设置水印颜色、大小及文本:

Paint paint = newPaint(Paint.ANTI_ALIAS_FLAG);paint.setColor(color);paint.setTextSize(size);Rect bounds = newRect();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 = newCanvas(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.**@paramtextThe text to be drawn*@paramxThe x-coordinate of the origin of the text being drawn*@paramyThe y-coordinate of the baseline of the text being drawn*@parampaintThe paint used for the text (e.g. color, size, style)*/public voiddrawText(@NonNull String text, floatx, floaty,@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绘制水印,参考代码如下:

/*** 绘制水印**@paramcontext*@parambitmap*@paramwaterMaskParam*@return*/private staticBitmap drawTxt(Context context,Bitmap bitmap,WaterMaskParam waterMaskParam) {

intmaxHeight = 0;//计算总行数List> msg = newArrayList<>();//文本for(String str : waterMaskParam.txt) {

intcount = str.length() / waterMaskParam.itemCount;if(count == 0) {

maxHeight++;Listlist = newArrayList<>();list.add(str);msg.add(list);} else{

if(str.length() % waterMaskParam.itemCount!= 0) {

count++;}

Listlist = newArrayList<>();for(inti = 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;}

}

inttxtSize = bitmap.getWidth() / waterMaskParam.itemCount;intindex = msg.size() - 1;bitmap = checkBackground(waterMaskParam.location,bitmap,msg.size() * txtSize * 2.0f);for(Liststrings : msg) {

for(inti = 0;i < strings.size();i++) {

bitmap = checkType(context,bitmap,strings.get(i),txtSize,waterMaskParam,txtSize * (maxHeight--) + index * txtSize / 2);}

index--;}

returnbitmap;} 我在最后绘制水印时,list中嵌套了一个list,外循环用来处理多段落的换行问题,内循环用来处理每个段落中的换行问题,所以我在调用绘制文本外部类传值时用的是list而不是string,及list中每个元素为一个段落,model如下:

public static classWaterMaskParam {

publicListtxt= newArrayList<>();//水印文字public intitemCount= DefWaterMaskParam.ITEM_COUNT;//每行的文字数public inttxtColor= DefWaterMaskParam.TEXT_COLOR;//文字颜色public intlocation= DefWaterMaskParam.Location.left_bottom;//水印位置publicWaterMaskParam() {

}

publicWaterMaskParam(Listtxt) {

this.txt= txt;}

publicWaterMaskParam(Listtxt, intitemCount, inttxtColor) {

this.txt= txt;this.itemCount= itemCount;this.txtColor= txtColor;}

} 为了优化水印,我在水印背景处又加了印象效果,模拟器拍摄的图片,不美观见谅

背景色的实现方式与水印绘制方式大同小异:

/*** 绘制背景色**@parambitmap*@parammaxHeight水印最大高度*@return*/private staticBitmap drawBackground(Bitmap bitmap, floatmaxHeight) {

Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),Bitmap.Config.ARGB_8888);Canvas canvas = newCanvas(newBitmap);Paint paint_b = newPaint();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);returnnewBitmap;}

我封装了一个helper类用来简化调用:

public classWaterMaskHelper {

privateContext context;privatePhotoListener photoListener;privateWaterMask.WaterMaskListener waterMarkListener;publicWaterMaskHelper(Context context,PhotoListener photoListener,WaterMask.WaterMaskListener waterMarkListener) {

this.context= context;this.photoListener= photoListener;this.waterMarkListener= waterMarkListener;}

public voidstartCapture() {

context.startActivity(newIntent(context,PhotoCaptureActivity.class));PhotoCaptureActivity.setWaterListener(waterMarkListener);PhotoCaptureActivity.setPhotoListener(photoListener);}

}

在activity中实例helper并实现接口:

//初始化水印工具waterMaskHelper= newWaterMaskHelper(this, this, this);

调用startCapture调用相机拍照并添加水印:

waterMaskHelper.startCapture();

暴露的接口有两个,都是在相机拍照后调用,选择照片:

//选择照片的uri,默认为下标1的元素voidonChoose(ArrayListphotos); 添加水印:

//拍照后调用,设置水印的基本参数WaterMaskParam onDraw();

具体实现,注:txt为空或param为空时不绘制水印

@OverridepublicWaterMask.WaterMaskParam onDraw() {

WaterMask.WaterMaskParam param = newWaterMask.WaterMaskParam();param.txt.add("我是一个小标题");param.txt.add(binding.edt.getText().toString().trim());param.location= maskLocation;param.itemCount= 30;returnparam;}

@Overridepublic voidonChoose(ArrayListphotos) {

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调用相机拍照并添加水印相关推荐

  1. Android实现图片加水印,视频水印

    Android实现图片加水印,视频加水印 推荐经典用例,超级简单的视频加水印,图片加水印功能. 视频加水印: Android拍视频加水印功能 图片加水印 Android轻松实现拍照加水印

  2. 控制头像大小php,php图片加水印,切头像图和自动缩放_PHP教程

    上传文件测试 "> http://code.google.com/p/queryphp/downloads/list frameworklibimg.class.php中的img类 / ...

  3. android intent拍照,Android通过Intent方式调用相机拍照取得图片

    Android通过Intent方式调用相机拍照取得图片 AndroidManifest.XML 权限设置: XML布局设置: 代码: public classMainActivityextendsAp ...

  4. Android开发之调用相机拍照与本地图库选择图片

    引用链接 Android开发之调用相机拍照与本地图库选择图片 Android调用相机实现拍照功能 部分截图 引言 小项目有一个访问相册的需求,在网上查找得到两位大神博客指点,但博客发布时间过旧,难免因 ...

  5. java安卓图片全屏_在Android中全屏显示GIF图片(演示代码)

    [实例简介] 在Android中全屏显示GIF图片(演示代码),详细内容请参考:http://blog.csdn.net/u012939909/article/details/77418173 [实例 ...

  6. vue中写svg组件svg图片加载不出来

    vue中写svg组件svg图片加载不出来 结构 首先要安装3个插件:svg-sprite-loader,svgo,svgo-loader npm install svg-sprite-loader - ...

  7. Android 中自定义View 裁剪扇形图片

    Android 中自定义View 裁剪扇形图片 当需要裁剪图片为扇形区域时,使用Canvas.clipPath(path)方法可以裁剪为扇形区域 ps:此方法会导致绘制图片边缘有锯齿,暂无解决方法(知 ...

  8. thumbnails java_在JAVA中使用Thumbnails为图片加水印

    在JAVA中使用Thumbnails为图片加水印 将D盘下面的cat.jpg作为水印加在2.jpg上面,输出新的图片2_cat.jpg到D盘下面 1.java类import java.io.File; ...

  9. Android 10调用相机拍照

    最近对以前的Android项目进行开发时,因为新的平板Android中系统版本为10,将旧项目的Android API升级为26.然后旧代码中的拍照权限失效,经过查询资料,发现权限并未打开,因此需要提 ...

最新文章

  1. 只花5-10分钟评审,还不提供拒稿理由,IJCAI就“枪毙”42%论文,网友:一脸懵逼...
  2. 职场不需要中年,但中年需要职场
  3. php框架m方法详细,Thinkphp框架中D方法与M方法的区别是什么
  4. pytorch学习笔记(十七):Read-Write
  5. 搜狗推出庭审语音识别系统 人工智能下的全新应用
  6. 大数据开发笔记(十):Hbase列存储数据库总结
  7. thinkphp5项目--企业单车网站(二)
  8. Apache CXF 入门第一个示例
  9. 谷歌浏览器无法同步问题解决方案
  10. 3dmax9.0 简体中文正式版(官方非汉化版本)下载网址
  11. 译文 [ROM][多国语言][2015.06.11] Lenovo S750 (MTK6589) - andrea_d86-lenovos750-4.2.2
  12. 怎样有效整理碎片化信息,提高学习效率
  13. 【PC】自制QQ机器人(python+VB6)
  14. windows 如何快速锁定计算机,Win7电脑锁定计算机快捷键的方法
  15. js每日一题(12)
  16. (笔记)Mac下耳机左右声道不平衡(左边小右边大或者右边大左边小)的解决方法
  17. vba中MsgBox的参数及用法
  18. pos机骗局收取押金如何投诉-真实案列解答
  19. 显示服务器事件,服务器事件查看
  20. 技巧3 vue3项目 axios前后端交互 图片地址拼接方法

热门文章

  1. 扑克与投资哲学,活着最重要
  2. 时间序列预测的评估指标补遗
  3. 《GEB-EGB》-人工智能
  4. 我的第一个工程-一个台球游戏
  5. python画带权重的图
  6. 理想L9/路特斯Eletre /高合HiPHi Z首发,谁能捅破智能豪华天花板?
  7. 计算机策略组 网络,组策略
  8. 腾讯、字节、蘑菇街、阿里实习生面经(面试记录)(2)
  9. 【报告分享】2020年短视频及电商直播趋势报告-飞瓜数据(附下载)
  10. 乙炔黑-离子液体复合修饰玻碳电极(AB-ILs/GCE)|离子液体修饰改性的多壁碳纳米管(MWNTs)