當SurfaceHolder對象的類型設置為SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS時就只能拍照不能繪制了。
為了既能通過SurfaceView拍照又能在上面繪制圖形,可以通過雙SurfaceView層疊的變通方式如下:
用於繪制的SurfaceView,使其透明並位於頂部:
[java]
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:
[java]
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:
[html]
<?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:
[html]
<?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:
[html] view plaincopy
<?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>
作者:xinzheng_wang