- 浏览: 4182885 次
最新评论
Android双SurfaceView实现底部拍照,顶部绘图
当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。
为了既能通过SurfaceView拍照又能在上面绘制图形,可以通过双SurfaceView层叠的变通方式如下:
用于绘制的SurfaceView,使其透明并位于顶部:
为了既能通过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
相关推荐
Android双SurfaceView底部拍照,顶部绘图,当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。为了既能通过SurfaceView拍照又能在上面绘制图形,可以通过双...
Android的SurfaceView实现图片缩放、滑动,网上资源不多,目前还有些问题,仅供参考。
基于android的SurfaceView实现人物动画的一个简单示例程序。
Android SurfaceView 实现实时显示摄像头视频,详见博客:https://blog.csdn.net/qq_30297763/article/details/90241102
安卓Android源码——安卓Android使用SurfaceView实现墨迹天气的风车效果.zip
这是在上网收集的,能够截取当前Activity中surfaceView中图片。本人亲测可以通过。
Android SurfaceView 实现实时显示摄像头视频,详见博客:https://blog.csdn.net/qq_30297763/article/details/90241102
Android使用SurfaceView实现墨迹天气的风车效果 http://blog.csdn.net/xyz_lmn/article/details/20483709
Android开发摄像头SurfaceView预览 背景画图(矩形和圆形) 实现(双surfaceview,顶层画矩形框,底层预览视频); UI:三个按钮 预览 摄像 图片保存,预览界面 可以显示(矩形和圆形等图画)
Android SurfaceView实现动画效果
Android SurfaceView实现加载GIF动画
android学习 SurfaceView实现人物动画,代码通过测试并且附有APK编译文件,可直接在手机上运行
Android中使用surfaceview开发的Camera相机,内容含有注释,非常清楚
Android使用SurfaceView实现墨迹天气的风车效果.zip
Android应用源码之Android使用SurfaceView实现墨迹天气的风车效果.zip
android使用双缓冲辨析及surfaceview使用例子
Android使用SurfaceView实现墨迹天气的风车效果.zip源码资源下载Android使用SurfaceView实现墨迹天气的风车效果.zip源码资源下载
android之surfaceview学习示例
Android圆形相机预览窗口,圆形SurfaceView
这是一个android SurfaceView双缓冲Demo,希望对大家理解android SurfaceView双缓冲有所帮助。