最近工作中遇到一个这样的功能,自动对焦拍照后将照片显示在当前页面,并出现两个长方形框体对照片的选中区域进行定位,获取其坐标。
这里就主要涉及到以下四个功能:拍照、照片显示、可移动的长方形框体、定位。
开始没有什么思路,于是从界面下手,
1.首先做出可移动框体,自定义一个view重写ondraw方法;
2.然后在这界面添加控制方向的按钮;
3.为了拍照,需要有一个SurfaceView,为了显示拍照的结果,需要一个ImageView来装载照片,这个view为了画图所以应当包含在之前的自定义view中;
4.为了让照片完全遮挡surfaceView所以使用帧布局,两个view设置成完全一致的宽高。
当上面的布局出来之后发现思路已经完全打开了。
一方面响应按钮事件,每点击一次按钮就重画一次,另一方面拍照就可以了。
一些关键代码如下:
<span style="font-size:12px;">自定义View DrawImageView.java
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
Log.e("====>>>>","开始绘图");
super.onDraw(canvas);
Paint cpaint = new Paint();
cpaint.setAntiAlias(true);
cpaint.setColor(Color.RED);
cpaint.setStyle(Style.STROKE);
cpaint.setStrokeWidth(2.5f);// 设置线宽
cpaint.setAlpha(100);
canvas.drawRect(cx, cy, cx+width, cy+height, cpaint);// 绘制c矩形
Paint tpaint = new Paint();
tpaint.setAntiAlias(true);
tpaint.setColor(Color.GREEN);
tpaint.setStyle(Style.STROKE);
tpaint.setStrokeWidth(2.5f);// 设置线宽
tpaint.setAlpha(100);
canvas.drawRect(tx, ty, tx+width, ty+height, tpaint);// 绘制t矩形
Log.e("====>>>>","结束绘图");
}</span>
MainActivity.java:
package com.example.camera;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity implements SurfaceHolder.Callback,
AutoFocusCallback, PictureCallback {
private static final String TAG = "camera";
private Button c_move_to_left;
private Button c_move_to_right;
private Button t_move_to_left;
private Button t_move_to_right;
private Button move_to_top;
private Button move_to_bottom;
private Button retake_picture;
private Button commit;
private SurfaceView surfaceView;
private DrawImageView drawView;
private ResultImageView resultView;
private ImageView iv;
private Camera camera;
private SurfaceHolder surfaceHolder;
private AutoFocusCallback autoFocusCallback;
private int cx = 184;
private int cy = 123;
private int tx = 246;
private int ty = 123;
private int trueWidth;
private int trueHeight;
private double a = 0.625;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initListener();
}
private void initView() {
surfaceView = (SurfaceView) findViewById(R.id.sv_preview);
resultView = (ResultImageView) findViewById(R.id.resultView);
drawView = (DrawImageView) resultView.findViewById(R.id.drawView);
c_move_to_left = (Button) findViewById(R.id.c_move_to_left);
c_move_to_right = (Button) findViewById(R.id.c_move_to_right);
t_move_to_left = (Button) findViewById(R.id.t_move_to_left);
t_move_to_right = (Button) findViewById(R.id.t_move_to_right);
move_to_top = (Button) findViewById(R.id.move_to_top);
move_to_bottom = (Button) findViewById(R.id.move_to_bottom);
retake_picture = (Button) findViewById(R.id.retake_picture);
commit = (Button) findViewById(R.id.commit);
}
@SuppressWarnings("deprecation")
private void initData() {
surfaceHolder = surfaceView.getHolder();
// 设置SurfaceHolder类型
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// translucent半透明
surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
// 屏幕常亮
surfaceHolder.setKeepScreenOn(true);
// 为SurfaceView的句柄添加一个回调函数
surfaceHolder.addCallback(this);
surfaceView.postDelayed(new Runnable() {
@Override
public void run() {
if (camera != null) {
camera.autoFocus(MainActivity.this);
}
}
}, 2000);
}
private void initListener() {
c_move_to_left.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
cx = drawView.getCx() - 5;
drawView.setCx(cx);
drawView.invalidate();
}
});
c_move_to_right.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
cx = drawView.getCx() + 5;
drawView.setCx(cx);
drawView.invalidate();
}
});
t_move_to_left.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tx = drawView.getTx() - 5;
drawView.setTx(tx);
drawView.invalidate();
}
});
t_move_to_right.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tx = drawView.getTx() + 5;
drawView.setTx(tx);
drawView.invalidate();
}
});
move_to_top.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
cy = drawView.getCy() - 5;
ty = drawView.getTy() - 5;
drawView.setCy(cy);
drawView.setTy(ty);
drawView.invalidate();
}
});
move_to_bottom.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
cy = drawView.getCy() + 5;
ty = drawView.getTy() + 5;
drawView.setCy(cy);
drawView.setTy(ty);
drawView.invalidate();
}
});
retake_picture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
MainActivity.class);
startActivity(intent);
finish();
}
});
commit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.e("=====>>>>", "" + drawView.getCx());
Log.e("=====>>>>", "" + drawView.getCy());
Log.e("=====>>>>", "" + drawView.getTx());
Log.e("=====>>>>", "" + drawView.getTy());
Log.e("=====>>>>", "" + drawView.getHeight());
Log.e("=====>>>>", "" + drawView.getWidth());
Log.e("=====>>>>", "" + ((double) drawView.getCx() / a));
Log.e("=====>>>>", "" + ((double) drawView.getCy() / a));
}
});
}
/**
* 拍照状态发生改变
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
/**
* 开始拍照时调用该方法
**/
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
try {
Log.e("====>>>>", "surfaceCreated开始");
// 打开摄像头
camera = Camera.open();
// 设置用于显示拍照影像的SurfaceHolder对象
camera.setPreviewDisplay(holder);
updateCameraParameters();
// 开始预览
camera.startPreview();
Log.e("====>>>>", "surfaceCreated结束");
} catch (Exception e) {
e.printStackTrace();
}
}
private void updateCameraParameters() {
if (camera != null) {
Camera.Parameters p = camera.getParameters();
long time = new Date().getTime();
p.setGpsTimestamp(time);
Camera.Size previewSize = findBestPreviewSize(p);
p.setPreviewSize(previewSize.width, previewSize.height);
p.setPictureSize(previewSize.width, previewSize.height);
if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
camera.setDisplayOrientation(90);
p.setRotation(90);
}
camera.setParameters(p);
}
}
/**
* 找到最合适的显示分辨率 (防止预览图像变形)
*
* @param parameters
* @return
*/
private Camera.Size findBestPreviewSize(Camera.Parameters parameters) {
// 系统支持的所有预览分辨率
String previewSizeValueString = null;
previewSizeValueString = parameters.get("preview-size-values");
if (previewSizeValueString == null) {
previewSizeValueString = parameters.get("preview-size-value");
}
if (previewSizeValueString == null) { // 有些手机例如m9获取不到支持的预览大小 就直接返回屏幕大小
DisplayMetrics metrics = getResources().getDisplayMetrics();
return camera.new Size(metrics.widthPixels, metrics.heightPixels);
}
float bestX = 0;
float bestY = 0;
float tmpRadio = 0;
float viewRadio = 0;
if (surfaceView.getWidth() != 0 && surfaceView.getHeight() != 0) {
viewRadio = Math.min((float) surfaceView.getWidth(),
(float) surfaceView.getHeight())
/ Math.max((float) surfaceView.getWidth(),
(float) surfaceView.getHeight());
}
String[] COMMA_PATTERN = previewSizeValueString.split(",");
for (String prewsizeString : COMMA_PATTERN) {
prewsizeString = prewsizeString.trim();
int dimPosition = prewsizeString.indexOf('x');
if (dimPosition == -1) {
continue;
}
float newX = 0;
float newY = 0;
try {
newX = Float.parseFloat(prewsizeString
.substring(0, dimPosition));
newY = Float.parseFloat(prewsizeString
.substring(dimPosition + 1));
} catch (NumberFormatException e) {
continue;
}
float radio = Math.min(newX, newY) / Math.max(newX, newY);
if (tmpRadio == 0) {
tmpRadio = radio;
bestX = newX;
bestY = newY;
} else if (tmpRadio != 0
&& (Math.abs(radio - viewRadio)) < (Math.abs(tmpRadio
- viewRadio))) {
tmpRadio = radio;
bestX = newX;
bestY = newY;
}
}
if (bestX > 0 && bestY > 0) {
// System.out.println("CustomCameraView previewSizeValueString bestX = "
// +
// bestX + ", bestY = " + bestY);
return camera.new Size((int) bestX, (int) bestY);
}
return null;
}
/**
* 停止拍照时调用该方法
**/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("====>>>>", "surfaceDestroyed开始");
// TODO Auto-generated method stub
if (camera != null) {
// 释放照相机
camera.setPreviewCallback(null);
camera.release();
camera = null;
}
Log.e("====>>>>", "surfaceDestroyed结束");
}
/**
* 自动对焦
**/
@Override
public void onAutoFocus(boolean success, Camera camera) {
Log.e("====>>>>", "onAutoFocus开始");
// TODO Auto-generated method stub
camera.takePicture(null, null, MainActivity.this);
Log.e("====>>>>", "onAutoFocus结束");
}
/**
* 拍照完成时调用
**/
@SuppressLint("WrongCall")
@Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.e("====>>>>", "onPictureTaken开始");
// TODO Auto-generated method stub
// drawView.setCx(cx);
// drawView.setCy(cy);
// drawView.onDraw(new Canvas());
try {
Bundle bundle = new Bundle();
bundle.putByteArray("bytes", data); // 将图片字节数据保存在bundle当中,实现数据交换
saveToSDCard(data); // 保存图片到sd卡中
Toast.makeText(getApplicationContext(), "成功", Toast.LENGTH_SHORT)
.show();
// camera.startPreview(); // 拍完照后,重新开始预览
Log.e("====>>>>", "onPictureTaken结束");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将拍下来的照片存放在SD卡中
*
* @param data
* @throws IOException
*/
public void saveToSDCard(byte[] data) throws IOException {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化时间
String filename = format.format(date) + ".png";
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
File jpgFile = Utils.createFile(
Environment.getExternalStorageDirectory() + "/readingmeter/",
filename);
Utils.savaBimapToFile(bitmap, jpgFile);
// Intent intent = new Intent();
// intent.putExtra(RESULT_PIC_PATH,jpgFile.getAbsolutePath());
// setResult(RESULT_OK, intent);
String picPath = jpgFile.getAbsolutePath();
Bitmap bitmapr = Utils.convertToBitmap(picPath, 640, 480);
Bitmap cmpcut = ImageCut.cut(bitmapr, 155, 90, 480, 300);
String filename1 = format.format(date) + "_640*480" + ".png";
File jpgFile1 = Utils.createFile(
Environment.getExternalStorageDirectory() + "/readingmeter/",
filename1);
String filename2 = format.format(date) + "_400*300" + ".png";
File jpgFile2 = Utils.createFile(
Environment.getExternalStorageDirectory() + "/readingmeter/",
filename2);
Utils.savaBimapToFile(bitmapr, jpgFile1);
Utils.savaBimapToFile(cmpcut, jpgFile2);
if (camera != null) {
// 释放照相机
camera.setPreviewCallback(null);
camera.release();
camera = null;
Log.e("====>>>>", "camera != null");
}
// 显示切割后的图片
resultView.setVisibility(View.VISIBLE);
drawView.setImageBitmap(cmpcut);
resultView.setBackgroundColor(Color.BLACK);
Log.e("====>>>>", "显示切割后的图片");
}
}
布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/LinearLayout01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/c_move_to_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:text="左移" />
<Button
android:id="@+id/c_move_to_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:text="右移" />
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<SurfaceView
android:id="@+id/sv_preview"
android:layout_width="fill_parent"
android:layout_height="450dp" />
<com.example.camera.ResultImageView
android:id="@+id/resultView"
android:layout_width="fill_parent"
android:layout_height="450dp"
android:gravity="center"
android:visibility="invisible"/>
</FrameLayout>
<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/t_move_to_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:text="左移" />
<Button
android:id="@+id/t_move_to_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:text="右移" />
<Button
android:id="@+id/move_to_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:text="上移" />
<Button
android:id="@+id/move_to_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:text="下移" />
<Button
android:id="@+id/retake_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="重拍" />
<Button
android:id="@+id/commit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="确定" />
</LinearLayout>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="800dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#000000"
android:gravity="center"
android:orientation="vertical" >
<com.example.camera.DrawImageView
android:id="@+id/drawView"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:gravity="center" />
</LinearLayout>
在确定按钮的OnClick事件,会打印出选择框左上角的位置。
这里面还有一个变化,就是ImageView是fill_parent还是wrap_content对坐标是有影响的,不同的方式坐标原点是不同的。另外稍加修改可以得到相对于屏幕左上角的位置。
完整DEMO下载: