水印相机是自定义相机的一种,实现方法有很多,我看了很多别人的做的很漂亮,我做的就很普通了,不过总算是实现了拍照加水印的功能。
我这边用到了SurfaceView
,有人没用这个也做出来水印相机,个人觉得还是SurfaceView
更方便一点(不接受反驳)。
先看看效果:
原图太大,我做了压缩,所以动图显得模糊。
第一步,我们想一进入就打开相机预览,这个怎么做呢?
相机功能由android.hardware.Camera
类实现,但是需要有一个预览载体,这里就用SurfaceView
,而且需要辅助类SurfaceHolder
,首先,我们的 Activity 要实现SurfaceHolder.Callback
接口:
1 | public class WaterCameraActivity extends AppCompatActivity implements SurfaceHolder.Callback |
第二步,关联SurfaceHolder
:
1 | private SurfaceView mSv; |
实现SurfaceHolder.Callback
接口有三个方法需要重写:
1 |
|
只要SurfaceView
显示,就会调用surfaceCreated()
,不显示就会调用surfaceDestroyed()
。因此可以在surfaceCreated()
中初始化相机,并展示预览界面;在surfaceDestroyed()
中释放相机资源。
第三步,初始化相机
1 | mCamera = Camera.open(0);//0-后摄像头,1-前摄像头 |
这样就可以预览相机界面了,多说一点,我是在小米 8 手机调试的,照片很清晰,拍出来的照片有 8M 多大,但是换成荣耀 8,图片只有几十 Kb,很不清楚。单步调试的时候可以发现,parameters.getSupportedPictureSizes()
这里获取的集合,小米和荣耀排序方式是不一样的,一个是清晰度由低到高,另一个由高到低。所以才改成上面代码中都取最大值:
1 | int width = 0, height = 0; |
第四步,拍照
1 | mCamera.takePicture(null, null, new android.hardware.Camera.PictureCallback() { |
手动调用相机拍出来的照片是旋转了 270 度的,所以要再旋转 90 度,才是正常视角m.postRotate(90)
。
第五步,加水印操作 addWater(bitmap):
1 | android.graphics.Bitmap.Config bitmapConfig = |
说明几点:
1.一开始设置字体大小是 22sp,但是没有显示水印,后来近距离仔细看有水印,只是字体太小,用了 sp 转 px,还是很小,最后发现图片的宽比手机屏宽要大得多,考虑这个倍数,计算出来,字体就可以正常显示了:
times = mBitmapWidthF / screenWidth
2.字体居中显示:
paint.measureText(text)
可以计算水印的宽度,屏宽一半减水印宽的一半,就是水印最左端的 x 坐标:
高度我这边是从屏高 3/4 处开始绘制,所以最终就是居中显示在屏幕中下方:1
2float x = (mBitmapWidth / 2) - (textW / 2);
canvas.drawText(text, x, (mBitmapHeight * 3 / 4), paint);3.显示两行水印,并且都居中:
下面水印的 y 坐标 = 上面水印 y 坐标 + 上面水印的高度
,上面水印高度计算:float textH = -paint.ascent() + paint.descent()
4.图片拍出来很大,压缩一下:
1
2
3
4
5
6
7
8
9
10
11ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 98;
while (baos.toByteArray().length / 1024 > 3072) { // 循环判断如果压缩后图片是否大于 3Mb,大于继续压缩
baos.reset(); // 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
options -= 2;// 每次都减少2
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
return bitmap;
第六步,保存水印图:
1 | FileOutputStream outStream = null; |
效果图:
清晰度可以的。
附上源码:点击获取
谢谢!