基于SurfaceView封装相机,并添加自定义水印效果
根据项目需要,android自带的相机不能满足自己需求,需要在拍完相机后能处理照片添加水印,并且照相机界面能自己定义风格,在网上找了现有代码自己加以重构(PS:可能封装的不够好),基本满足相机功能和业务逻辑分离,到达代码复用效果。
重构代码遇到3个问题这里和大家分享下:
1.自定义相机设置尺寸不对导致SurfaceView显示画面变形
2.手动点击对焦
3.水印添加美观满足用户自定义
问题一:
针对这个问题也在网上找了资料,基本解释是 由于预览图像大小跟SurfaceView 大小不一致引起
于是找了可行代码块
代码块语法遵循标准markdown代码,例如:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
int rotation = getDisplayOrientation();
mCamera.setDisplayOrientation(rotation);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRotation(rotation);
mCamera.setParameters(parameters);
adjustDisplayRatio(rotation);
}
public int getDisplayOrientation() {
android.hardware.Camera.CameraInfo camInfo = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(
Camera.CameraInfo.CAMERA_FACING_BACK, camInfo);
Display display = ((WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result = (camInfo.orientation - degrees + 360) % 360;
return result;
}
private void adjustDisplayRatio(int rotation) {
ViewGroup parent = ((ViewGroup) getParent());
Rect rect = new Rect();
parent.getLocalVisibleRect(rect);
int width = rect.width();
int height = rect.height();
Parameters mParameters = mCamera.getParameters();
Camera.Size previewSize = mParameters.getPreviewSize();
int previewWidth = previewSize.width;
int previewHeight = previewSize.height;
if (rotation == 90 || rotation == 270) {
previewWidth = previewSize.height;
previewHeight = previewSize.width;
} else {
previewWidth = previewSize.width;
previewHeight = previewSize.height;
}
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height / previewHeight;
layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width / previewWidth;
layout(0, (height - scaledChildHeight) / 2, width,
(height + scaledChildHeight) / 2);
}
}
在surfaceChanged方法里面设置相机的显示,里面最重要的方法是adjustDisplayRatio,这个方法是适配预览图像大小跟SurfaceView 大小,目前魅族手机显示是正常。
问题二:手动点击对焦
手动点击对焦需要捕获onTouchEvent方法的event.getPointerCount(),若event.getPointerCount() == 1则表示SurfaceView 点击回调然后调camera.autoFocus完成手动对焦
public boolean onTouchEvent(MotionEvent event) {
if (event.getPointerCount() == 1) {
handleFocusMetering(event, mCamera);
}}
private void handleFocusMetering(MotionEvent event, Camera camera) {
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
Camera.Parameters params = camera.getParameters();
params.setFocusMode(params.getFocusMode());
camera.setParameters(params);
}
});
}
问题三:优化水印显示
刚拿到代码时候发现,该功能和业务绑定很严重,其他项目拿到这份代码需要改动很大,达不到复用的效果。重构是时候主要的思想是返回可用的水印照片就行,至于什么业务逻辑生成照片代码里面不用关心。
将PhotoGraphyView自定义一个公共view,在xml直接引用,获得对象后然后设置照片顶部和底部水印
代码如下
List<TextView> listTextView_up = new ArrayList<TextView>();
TextView tv1 = new TextView(this);
tv1.setTextColor(Color.GREEN);
tv1.setText("报案号:XXXXXXXXXXXXXXXX");
tv1.setTextSize(18);
TextView tv2 = new TextView(this);
tv2.setText("姓名:张三");
tv2.setTextSize(18);
tv2.setTextColor(Color.GREEN);
listTextView_up.add(tv1);
listTextView_up.add(tv2);
photoGraphyView.setImageUpView(listTextView_up);//设置头部水印
List<TextView> listTextView_down = new ArrayList<TextView>();
TextView tv3 = new TextView(this);
tv3.setTextColor(Color.RED);
tv3.setText("经度:138.298374");
tv3.setTextSize(18);
TextView tv4 = new TextView(this);
tv4.setTextColor(Color.RED);
tv4.setText("纬度:64.293772");
tv4.setTextSize(18);
listTextView_down.add(tv3);
listTextView_down.add(tv4);
TextView tv5 = new TextView(this);
tv5.setTextColor(Color.RED);
tv5.setText("时间:2016-08-15 15:45:47");
tv5.setTextSize(18);
listTextView_down.add(tv5);
photoGraphyView.setImageDownView(listTextView_down);//设置底部水印
设置水印的代码主要是运用Canvas对象
private Paint drawUpText(Canvas cv) {
Paint paint = new Paint();
StringBuffer sb = new StringBuffer();
if (listTextView_top.size() > 0) {
cv.setDrawFilter(new PaintFlagsDrawFilter(0, 3));
for (int i = 0; i < listTextView_top.size(); i++) {
paint.setColor(listTextView_top.get(i).getTextColors() == null ? Color.BLACK
: listTextView_top.get(i).getTextColors()
.getDefaultColor());
paint.setTextSize((float) (listTextView_top.get(i)
.getTextSize() + 4));
paint.setTypeface(fontFace);
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
paint.setDither(true);
sb.append(listTextView_top.get(i).getText().toString());
if (i > 0) {
cv.drawText(listTextView_top.get(i).getText().toString(),
200f + getTextWidth(paint, sb.toString()), 100f,
paint);
} else {
cv.drawText(listTextView_top.get(i).getText().toString(),
500f, 100f, paint);
}
}
}
return paint;
}
Canvas 生成如下
Bitmap newb = null;
newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas cv = new Canvas(newb);