当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