自动对焦拍照,以及可移动方框定位

最近工作中遇到一个这样的功能,自动对焦拍照后将照片显示在当前页面,并出现两个长方形框体对照片的选中区域进行定位,获取其坐标。

这里就主要涉及到以下四个功能:拍照、照片显示、可移动的长方形框体、定位。

开始没有什么思路,于是从界面下手,

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下载:





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值