`
阿尔萨斯
  • 浏览: 4182885 次
社区版块
存档分类
最新评论

Android双SurfaceView实现底部拍照,顶部绘图

 
阅读更多

当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。
为了既能通过SurfaceView拍照又能在上面绘制图形,可以通过双SurfaceView层叠的变通方式如下:
用于绘制的SurfaceView,使其透明并位于顶部:
package com.test;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class SVDraw extends SurfaceView implements SurfaceHolder.Callback {

	private Bitmap bmp;
	private String imgPath = "";
	protected SurfaceHolder sh; // 专门用于控制surfaceView的
	private int width;
	private int height;

	// XML文件解析需要调用View的构造函数View(Context , AttributeSet)
	// 因此自定义SurfaceView中也需要该构造函数
	public SVDraw(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		sh = getHolder();
		sh.addCallback(this);
		sh.setFormat(PixelFormat.TRANSPARENT); // 设置为透明
		setZOrderOnTop(true);// 设置为顶端
	}

	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int w, int h) {
		// TODO Auto-generated method stub
		width = w;
		height = h;
	}

	@Override
	public void surfaceCreated(SurfaceHolder arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) {
		// TODO Auto-generated method stub

	}

	void clearDraw() {

		Canvas canvas = sh.lockCanvas();
		canvas.drawColor(Color.BLUE);// 清除画布
		sh.unlockCanvasAndPost(canvas);
	}

	/**
	 * 绘制
	 */
	public void doDraw() {
		if (bmp != null) {
			Canvas canvas = sh.lockCanvas();
			canvas.drawColor(Color.TRANSPARENT);// 这里是绘制背景
			Paint p = new Paint(); // 笔触
			p.setAntiAlias(true); // 反锯齿
			p.setColor(Color.RED);
			p.setStyle(Style.STROKE);
			canvas.drawBitmap(bmp, 0, 0, p);
			canvas.drawLine(width / 2 - 100, 0, width / 2 - 100, height, p);
			canvas.drawLine(width / 2 + 100, 0, width / 2 + 100, height, p);
			// ------------------------ 画边框---------------------
			Rect rec = canvas.getClipBounds();
			rec.bottom--;
			rec.right--;
			p.setColor(Color.GRAY); // 颜色
			p.setStrokeWidth(5);
			canvas.drawRect(rec, p);
			// 提交绘制
			sh.unlockCanvasAndPost(canvas);
		}

	}

	public void drawLine() {

		Canvas canvas = sh.lockCanvas();

		canvas.drawColor(Color.TRANSPARENT);// 这里是绘制背景
		Paint p = new Paint(); // 笔触
		p.setAntiAlias(true); // 反锯齿
		p.setColor(Color.RED);
		p.setStyle(Style.STROKE);
		canvas.drawLine(width / 2 - 100, 0, width / 2 - 100, height, p);
		canvas.drawLine(width / 2 + 100, 0, width / 2 + 100, height, p);

		// 提交绘制
		sh.unlockCanvasAndPost(canvas);
	}

	public String getImgPath() {
		return imgPath;
	}

	public void setImgPath(String imgPath) {
		this.imgPath = imgPath;
		// 根据路径载入目标图像
		bmp = BitmapFactory.decodeFile(imgPath);
	}

}
用于在SurfaceView(使其位于绘制SurfaceView底部)上拍照及预览的Activity:
package com.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.content.ContentValues;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;

import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.Size;

import android.media.AudioManager;
import android.media.ToneGenerator;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.SyncStateContract.Constants;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;

public class SurfaceViewDraw extends Activity implements
		SurfaceHolder.Callback, Camera.PictureCallback {
	/** Called when the activity is first created. */
	private SVDraw svDraw = null;
	private SurfaceView svCamera = null;
	protected SurfaceHolder mSurfaceHolder;

	private Button btnClear;
	private Button btnOpen;
	private Button btnClose;
	private Button btnTakePic;
	private Button btnDraw;

	private Camera mCamera; // 这个是hardware的Camera对象
	private boolean isOpen = false;// 相机是否打开
	private ToneGenerator tone;
	private String imgPath;

	private int width;
	private int height;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		hideStatusBar();
		setContentView(R.layout.main);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 强制为横屏

		svDraw = (com.test.SVDraw) findViewById(R.id.svDraw);
		svCamera = (SurfaceView) findViewById(R.id.svCamera);

		btnClear = (Button) findViewById(R.id.btnClear);
		btnOpen = (Button) findViewById(R.id.btnOpen);
		btnClose = (Button) findViewById(R.id.btnClose);
		btnTakePic = (Button) findViewById(R.id.btnTakePic);
		btnDraw = (Button) findViewById(R.id.btnDraw);

		btnClear.setOnClickListener(new ClickEvent());
		btnOpen.setOnClickListener(new ClickEvent());
		btnClose.setOnClickListener(new ClickEvent());
		btnTakePic.setOnClickListener(new ClickEvent());
		btnDraw.setOnClickListener(new ClickEvent());

		mSurfaceHolder = svCamera.getHolder();
		mSurfaceHolder.addCallback(this);
		// 当设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS后就不能绘图了
		mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

	}

	class ClickEvent implements View.OnClickListener {

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			if (v == btnClear) {

				stopPreview(); // 停止预览后清屏速度会快一点
				svDraw.setVisibility(View.INVISIBLE);
				startPreview();// 清屏后启动预览

			} else if (v == btnOpen) {

				initCamera();

			} else if (v == btnClose) {

				closeCamera();

			} else if (v == btnTakePic) {
				if (isOpen) {
					startPreview();// 防止异常
					mCamera.takePicture(mShutterCallback, null, null,
							mjpegCallback);
					svDraw.setVisibility(View.VISIBLE);
					svDraw.drawLine();// 拍照后绘制测线
				}
			} else if (v == btnDraw) {

				svDraw.setVisibility(View.VISIBLE);
				svDraw.doDraw();
			}
		}

	}

	ShutterCallback mShutterCallback = new ShutterCallback() {
		@Override
		public void onShutter() {
			// TODO Auto-generated method stub
			if (tone == null)
				// 发出提示用户的声音
				tone = new ToneGenerator(AudioManager.STREAM_MUSIC,
						ToneGenerator.MAX_VOLUME);
			tone.startTone(ToneGenerator.TONE_PROP_BEEP);

		}
	};
	/**
	 * Jpeg格式压缩
	 */
	PictureCallback mjpegCallback = new PictureCallback() {
		@Override
		// 取得拍照图片
		public void onPictureTaken(byte[] data, Camera camera) {
			// TODO Auto-generated method stub
			// 拍照前关闭预览
			mCamera.stopPreview();
			// 取得图像路径
			imgPath = saveFile2(data);
			svDraw.setImgPath(imgPath);

		}

	};

	/**
	 * draw information on the picture
	 * 
	 * @param imgPath
	 */
	public void drawInfo(String imgPath) {

		Bitmap bmp = BitmapFactory.decodeFile(imgPath);

		if (bmp != null) {
			Bitmap drawBmp = Bitmap.createBitmap(640, 480, Config.ARGB_8888);
			Canvas c = new Canvas(drawBmp);
			Paint p = new Paint();
			c.drawBitmap(bmp, 0, 0, p);
			String familyName = "Arial";
			Typeface font = Typeface.create(familyName, Typeface.NORMAL);
			p.setColor(Color.RED);
			p.setTypeface(font);
			p.setTextSize(20);
			p.setStyle(Paint.Style.STROKE);
			SimpleDateFormat dateFormat = new SimpleDateFormat(
					"yyyy-MM-dd hh:mm:ss");
			String strDate = dateFormat.format(new Date());
			c.drawText(strDate, 10, 30, p);

			try {
				saveBmp(drawBmp, imgPath);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	/**
	 * save bmp as jpg by path
	 * 
	 * @param bmpPath
	 * @param bmp
	 * @throws IOException
	 */
	public void saveBmp(Bitmap bmp, String fileName) throws IOException {

		File f = new File(fileName);
		f.createNewFile();
		FileOutputStream fOut = null;
		try {
			fOut = new FileOutputStream(f);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		bmp.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
		try {
			fOut.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			fOut.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * return imgFilePath
	 * 
	 * @param data
	 * @return
	 */
	private String saveFile2(byte[] data) {
		File imgFileDir = getDir();
		if (!imgFileDir.exists() && !imgFileDir.mkdirs()) {
			Log.v("directory", "Can't create directory to save image.");
			return null;
		}
		// 图像名称
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
		String strDate = dateFormat.format(new Date());
		String imgFileName = "img_" + strDate + ".jpg";
		// 图像路径
		String imgFilePath = imgFileDir.getPath() + File.separator
				+ imgFileName;
		File imgFile = new File(imgFilePath);
		try {
			FileOutputStream fos = new FileOutputStream(imgFile);
			fos.write(data);
			fos.close();
			Log.v("directory", "New Image saved:" + imgFile);

		} catch (Exception error) {
			Log.d(Constants.ACCOUNT_NAME,
					imgFileName + " not saved: " + error.getMessage());

		}
		//绘制拍照日期等
		drawInfo(imgFilePath);
        return imgFilePath;
	}

	/**
	 * 
	 * @return
	 */
	private File getDir() {
		File sdDir = Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
		// 创建图像需要保存的文件夹
		return new File(sdDir, "Photo");

	}

	@Override
	public void onPictureTaken(byte[] data, Camera camera) {
		// TODO Auto-generated method stub
		// data是一个原始的JPEG图像数据,
		// 在这里我们可以存储图片,很显然可以采用MediaStore
		// 注意保存图片后,再次调用stopPreview()停止预览,等待测量
		Uri imageUri = this.getContentResolver().insert(
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
				new ContentValues());
		try {
			OutputStream os = this.getContentResolver().openOutputStream(
					imageUri);
			os.write(data);
			os.flush();
			os.close();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		// 拍照后停止预览
		mCamera.stopPreview();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		// TODO Auto-generated method stub
		width = w;
		height = h;
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO Auto-generated method stub

	}

	/**
	 * 关闭相机
	 */
	public void closeCamera() {
		if (isOpen) {
			mCamera.stopPreview();
			mCamera.release();
			mCamera = null;
			isOpen = false;
		}
	}

	/**
	 * 停止拍照预览
	 */
	public void stopPreview() {
		if (isOpen) {
			mCamera.stopPreview();
		}
	}

	/**
	 * 启动拍照预览
	 */
	public void startPreview() {
		if (isOpen) {
			mCamera.startPreview();
		}
	}

	/**
	 * 初始化相机
	 */
	public void initCamera() {
		if (!isOpen) {
			mCamera = Camera.open();
		}
		if (mCamera != null && !isOpen) {
			try {
				Camera.Parameters mParameters = mCamera.getParameters();
				mParameters.setPictureFormat(PixelFormat.JPEG); // 设置照片格式
				List<Size> sizes = mParameters.getSupportedPreviewSizes();
				Size optimalSize = getOptimalPreviewSize(sizes, width, height);
				mParameters.setPreviewSize(optimalSize.width,
						optimalSize.height); // 大小
				mParameters.setPictureSize(optimalSize.width,
						optimalSize.height);

				mParameters.set("jpeg-quality", 100);// 照片质量
				// 首先获取系统设备支持的所有颜色特效,有复合我们的,则设置;否则不设置
				List<String> colorEffects = mParameters
						.getSupportedColorEffects();
				Iterator<String> colorItor = colorEffects.iterator();
				while (colorItor.hasNext()) {
					String currColor = colorItor.next();
					if (currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)) {
						mParameters
								.setColorEffect(Camera.Parameters.EFFECT_SOLARIZE);
						break;
					}
				}
				mCamera.setParameters(mParameters);
				mCamera.setPreviewDisplay(mSurfaceHolder);
				mCamera.startPreview();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			isOpen = true;
		}
	}

	private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
		final double ASPECT_TOLERANCE = 0.05;
		double targetRatio = (double) w / h;
		if (sizes == null)
			return null;

		Size optimalSize = null;
		double minDiff = Double.MAX_VALUE;

		int targetHeight = h;

		// Try to find an size match aspect ratio and size
		for (Size size : sizes) {
			double ratio = (double) size.width / size.height;
			if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
				continue;
			if (Math.abs(size.height - targetHeight) < minDiff) {
				optimalSize = size;
				minDiff = Math.abs(size.height - targetHeight);
			}
		}

		// Cannot find the one match the aspect ratio, ignore the requirement
		if (optimalSize == null) {
			minDiff = Double.MAX_VALUE;
			for (Size size : sizes) {
				if (Math.abs(size.height - targetHeight) < minDiff) {
					optimalSize = size;
					minDiff = Math.abs(size.height - targetHeight);
				}
			}
		}
		return optimalSize;
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		if (isOpen) {
			closeCamera();
		}
	}

	// 在 Activity.setCurrentView()之前调用
	public void hideStatusBar() {
		// 隐藏标题
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		// 定义全屏参数
		int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
		// 获得窗口对象
		Window curWindow = this.getWindow();
		// 设置Flag标示
		curWindow.setFlags(flag, flag);
	}
}
主界面main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >
   <!-- 对于自定义控件要指明的控件的包名与空间名,系统自带的控件不需要指定包名 -->
   <FrameLayout
       android:layout_width="640dip" 
       android:layout_height="480dip" 
       android:orientation="vertical" >
    <SurfaceView
      android:id="@+id/svCamera"
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent"/>
    
    <com.test.SVDraw
     android:id="@+id/svDraw"
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent"/>
    
   </FrameLayout>
    
    <LinearLayout 
      android:id="@+id/LinearLayout01"
      android:layout_width="158dip" 
      android:layout_height="fill_parent"
      android:layout_marginLeft="1dip"
      android:layout_marginRight="1dip"
      android:orientation="vertical"
      android:background="@drawable/main_right_bg">
	    <Button 
		  android:id="@+id/btnOpen" 
		  android:layout_width="fill_parent"
		  android:layout_height="wrap_content" 
		  android:layout_marginTop="10dip"
		  android:text="打开相机"/>
	     <Button 
		  android:id="@+id/btnClose" 
		  android:layout_width="fill_parent"
		  android:layout_height="wrap_content" 
		  android:text="关闭相机"/>
	     <Button 
	        android:id="@+id/btnTakePic" 
		 android:layout_width="fill_parent"
		 android:layout_height="wrap_content" 
		 android:text="拍照"   />
	    <Button 
		 android:id="@+id/btnClear" 
		 android:layout_width="fill_parent"
		 android:layout_height="wrap_content" 
		 android:text="预览"/>	  
	    <Button 
		 android:id="@+id/btnDraw" 
		 android:layout_width="fill_parent"
		 android:layout_height="wrap_content" 
		 android:text="绘制"/>	     
	 
    </LinearLayout>
</LinearLayout>
在res下新建文件夹drawable,并在其下面新建面板背景main_right_bg.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <gradient
      android:startColor = "#666666"
      android:centerColor="#000FFF"
      android:endColor = "#666666"  
      android:angle = "270"/> 
  <corners 
      android:radius="4dip"/>
  
</shape>
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />
    
<!-- 照相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".SurfaceViewDraw"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


开发环境:XP3+Eclipse+Android2.2+JDK6.0
测试环境:Android2.2,5寸屏,分辨率640X480
源代码:http://download.csdn.net/detail/xinzheng_wang/4409755


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics