Android--相机预览及拍照临时文件/SurfaceView

如果做游戏或者视频相关开发的时候,将会用到SurfaceView。对于SurfaceView,首先需要了解一下它自己的位置:

extends View

java.lang.Object

   ↳

android.view.View

 

   ↳

android.view.SurfaceView

由手册可知:

SurfaceView是View类的继承类,这个View里内嵌了一个专门用于绘制的Surface,这个可以类似的理解成为一个在View里的Canvas。你可以控制这个Surface的格式和尺寸。Surfaceview类则控制这个Surface在屏幕上的正确位置。

《Android高级编程》里这样说:

在一般情况下,应用程序的View都是在相同的GUI线程中绘制的。这个主应用程序线程同时也用来处理所有的用户交互(例如按钮单击或者文本输入)。

对于一个View的onDraw()方法,不能够满足将其移动到后台线程中去。因为从后台线程修改一个GUI元素会被显式地禁止的。

当需要快速地更新View的UI,或者当前渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

       1. 何时应该使用SurfaceView

       SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。

       SurfaceView封装的Surface支持所有标准的Canvas方法进行绘图,同时也支持完全的OpenGL ES 库。

       使用OpenGL,你可以在Surface上绘制任何支持2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。

       对于显示动态的3D图像来说,例如,那些使用Google Earth 功能的应用程序,或者那些提供沉浸体验的交互式游戏,Surface特别有用。它还是实时显示摄像头预览的最佳选择。

2. 创建一个新的SurfaceView控件

创建一个新的SurfaceView控件需要创建一个新的扩展了SurfaceView的类,并实现SurfaceHolder.Callback。

SurfaceHolder回调可以在底层的Surface被创建和销毁的时候通知View,并传递给它SurfaceHolder对象的引用,其中包含了当前有效的Surface。

一个典型的SurfaceView 设计模型包括一个由Thread所派生的类,它可以接收对当前的SurfaceHolder的引用,并独立地更新它。

3. 使用SurfaceView创建3D控件

Android完全支持OpenGL ES 3D 渲染框架,其中包含了对设备的硬件加速的支持。SurfaceView控件提供了一个表面,可以在它上面渲染你的OpenGL场景。

那么我们在使用的时候可以这样使用:

被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

 

可以直接从内存或硬件设备比如相机等取得图像数据,是个非常重要的绘图容器。

它的特性是:可以在主线程之外的线程中向屏幕绘图。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。

如何去使用一个SurfaceView:

首先继承SurfaceView并实现SurfaceHolder.Callback接口。因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始。可以被直接复制到显存从而显示出来,这使得显示速度会非常快,而在Surface 被销毁之前必须结束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

需要重写的方法

 (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

     //在surface的大小发生改变时激发

 (2)public void surfaceCreated(SurfaceHolder holder){}

     //在创建时激发,一般在这里调用画图的线程。

 (3)public void surfaceDestroyed(SurfaceHolder holder) {}

     //销毁时激发,一般在这里将画图的线程停止、释放。

整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。

关于SurfaceHolder:

这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。
几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。


下面给出一个应用的实例,本实例实现的是相机的预览和拍照功能,其中,为了节省存储空间,当退出应用程序时,拍照文件会自动删除,这里把它作为临时文件保存。

下面是它的实现截图:

Android--相机预览及拍照临时文件/SurfaceView_第1张图片

由于是在模拟器中运行,所以显示的是无内存卡。

下面给出实现本实例的源码:

1.首先,我们需要做权限的声明

[html]  view plain copy print ?
  1. <uses-permission android:name="android.permission.CAMERA"/>  
  2.   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  

2.给出布局文件

[html]  view plain copy print ?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:background="@drawable/white"  
  5.   android:orientation="vertical"  
  6.   android:layout_width="fill_parent"  
  7.   android:layout_height="fill_parent"  
  8. >  
  9.   <TextView  
  10.     android:id="@+id/myTextView1"  
  11.     android:layout_width="fill_parent"   
  12.     android:layout_height="wrap_content"  
  13.     android:textColor="@drawable/blue"   
  14.     android:text="@string/hello"  
  15.   />  
  16.   <SurfaceView  
  17.     android:id="@+id/mSurfaceView1"  
  18.     android:visibility="visible"   
  19.     android:layout_width="320px"   
  20.     android:layout_height="240px">  
  21.   </SurfaceView>   
  22.   <LinearLayout  
  23.     android:orientation="horizontal"  
  24.     android:layout_width="wrap_content"  
  25.     android:layout_height="wrap_content"  
  26.   >  
  27.   <Button  
  28.     android:id="@+id/myButton1"  
  29.     android:layout_width="wrap_content"  
  30.     android:layout_height="wrap_content"  
  31.     android:text="@string/str_button1"/>  
  32.   <Button  
  33.     android:id="@+id/myButton2"  
  34.     android:layout_width="wrap_content"  
  35.     android:layout_height="wrap_content"  
  36.     android:text="@string/str_button2"/>  
  37.   <Button  
  38.     android:id="@+id/myButton3"  
  39.     android:layout_width="wrap_content"  
  40.     android:layout_height="wrap_content"  
  41.     android:text="@string/str_take_picture"/>  
  42.   </LinearLayout>  
  43.   <ImageView  
  44.     android:id="@+id/myImageView1"   
  45.     android:layout_width="wrap_content"   
  46.     android:layout_height="wrap_content"   
  47.     android:layout_gravity="center" />  
  48. </LinearLayout>  

3.主程序代码

[java]  view plain copy print ?
  1. package irdc.ex07_15;  
  2.   
  3. import java.io.BufferedOutputStream;  
  4. import java.io.File;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.util.List;  
  8.   
  9. import android.app.Activity;  
  10. /* 延含学习 */  
  11. //import android.content.pm.ActivityInfo;  
  12. import android.graphics.Bitmap;  
  13. import android.graphics.BitmapFactory;  
  14. import android.graphics.PixelFormat;  
  15.   
  16. /* 引用Camera类 */  
  17. import android.hardware.Camera;  
  18.   
  19. /* 引用PictureCallback为取得拍照后的事件 */  
  20. import android.hardware.Camera.PictureCallback;  
  21. import android.hardware.Camera.ShutterCallback;  
  22. import android.os.Bundle;  
  23. import android.util.DisplayMetrics;  
  24. import android.util.Log;  
  25. import android.view.SurfaceHolder;  
  26. import android.view.SurfaceView;  
  27. import android.view.View;  
  28. import android.view.Window;  
  29. import android.widget.Button;  
  30. import android.widget.ImageView;  
  31. import android.widget.TextView;  
  32. import android.widget.Toast;  
  33.   
  34. /* 使Activity实现SurfaceHolder.Callback */  
  35. public class EX07_15 extends Activity implements SurfaceHolder.Callback  
  36. {  
  37.   /* 建立私有Camera对象 */  
  38.   private Camera mCamera01;  
  39.   private Button mButton01, mButton02, mButton03;  
  40.     
  41.   /* 当成review照下来的相片之用 */  
  42.   private ImageView mImageView01;  
  43.   private TextView mTextView01;  
  44.   private static String TAG = "HIPPO_DEBUG";  
  45.   private SurfaceView mSurfaceView01;  
  46.   private SurfaceHolder mSurfaceHolder01;  
  47.   //private int intScreenX, intScreenY;  
  48.     
  49.   /* 默认相机预览模式为false */  
  50.   private boolean bIfPreview = false;  
  51.     
  52.   /* 将照下来的图片保存在此 */  
  53.   private String strCaptureFilePath = "/sdcard/camera_snap.jpg";  
  54.     
  55.   /** Called when the activity is first created. */  
  56.   @Override  
  57.   public void onCreate(Bundle savedInstanceState)  
  58.   {  
  59.     super.onCreate(savedInstanceState);  
  60.       
  61.     /* 使应用程序全屏运行,不使用title bar */  
  62.     requestWindowFeature(Window.FEATURE_NO_TITLE);  
  63.     setContentView(R.layout.main);  
  64.       
  65.     /* 判断存储卡是否存在 */  
  66.     if(!checkSDCard())  
  67.     {  
  68.       /* 提醒User未安装存储卡 */  
  69.       mMakeTextToast  
  70.       (  
  71.         getResources().getText(R.string.str_err_nosd).toString(),  
  72.         true  
  73.       );  
  74.     }  
  75.       
  76.     /* 取得屏幕解析像素 */  
  77.     DisplayMetrics dm = new DisplayMetrics();  
  78.     getWindowManager().getDefaultDisplay().getMetrics(dm);  
  79.     //intScreenX = dm.widthPixels;  
  80.     //intScreenY = dm.heightPixels;  
  81.     //Log.i(TAG, Integer.toString(intScreenX));  
  82.       
  83.     /* 延含学习 */  
  84.     //import android.content.pm.ActivityInfo;  
  85.     //this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
  86.       
  87.     mTextView01 = (TextView) findViewById(R.id.myTextView1);  
  88.     mImageView01 = (ImageView) findViewById(R.id.myImageView1);  
  89.       
  90.     /* 以SurfaceView作为相机Preview之用 */  
  91.     mSurfaceView01 = (SurfaceView) findViewById(R.id.mSurfaceView1);  
  92.       
  93.     /* 绑定SurfaceView,取得SurfaceHolder对象 */  
  94.     mSurfaceHolder01 = mSurfaceView01.getHolder();  
  95.       
  96.     /* Activity必须实现SurfaceHolder.Callback */  
  97.     mSurfaceHolder01.addCallback(EX07_15.this);  
  98.       
  99.     /* 额外的设置预览大小设定,在此不使用 */  
  100.     //mSurfaceHolder01.setFixedSize(320, 240);  
  101.       
  102.     /* 
  103.      * 以SURFACE_TYPE_PUSH_BUFFERS(3) 
  104.      * 作为SurfaceHolder显示类型  
  105.      * */  
  106.     mSurfaceHolder01.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
  107.       
  108.     mButton01 = (Button)findViewById(R.id.myButton1);  
  109.     mButton02 = (Button)findViewById(R.id.myButton2);  
  110.     mButton03 = (Button)findViewById(R.id.myButton3);  
  111.       
  112.     /* 开启相机及Preview */  
  113.     mButton01.setOnClickListener(new Button.OnClickListener()  
  114.     {  
  115.       @Override  
  116.       public void onClick(View v)  
  117.       {  
  118.         // TODO Auto-generated method stub  
  119.           
  120.         /* 自定义初始化开启相机函数 */  
  121.         initCamera();  
  122.       }  
  123.     });  
  124.       
  125.     /* 停止Preview及相机 */  
  126.     mButton02.setOnClickListener(new Button.OnClickListener()  
  127.     {  
  128.       @Override  
  129.       public void onClick(View arg0)  
  130.       {  
  131.         // TODO Auto-generated method stub  
  132.           
  133.         /* 自定义重置相机,并关闭相机预览函数 */  
  134.         resetCamera();  
  135.       }  
  136.     });  
  137.       
  138.     /* 拍照 */  
  139.     mButton03.setOnClickListener(new Button.OnClickListener()  
  140.     {  
  141.       @Override  
  142.       public void onClick(View arg0)  
  143.       {  
  144.         // TODO Auto-generated method stub  
  145.           
  146.         /* 当存储卡存在才允许拍照,保存临时图像文件 */  
  147.         if(checkSDCard())  
  148.         {  
  149.           /* 自定义拍照函数 */  
  150.           takePicture();  
  151.         }  
  152.         else   
  153.         {  
  154.           /* 存储卡不存在的显示提示 */  
  155.           mTextView01.setText  
  156.           (  
  157.             getResources().getText(R.string.str_err_nosd).toString()  
  158.           );  
  159.         }  
  160.       }  
  161.     });  
  162.   }  
  163.     
  164.   /* 告订初始相机函数 */  
  165.   private void initCamera()  
  166.   {  
  167.     if(!bIfPreview)  
  168.     {  
  169.       /* 若相机非?预览模式,则开启相机 */  
  170.       try  
  171.       {  
  172.         /*  
  173.          * The Heap 是应用程序?手机里执行所配置的空间 
  174.          * 当超过预设的16 MB(每几个应用程序)时就会导致 "Out of memory"的错误 
  175.          * 目前看来是Cupcake(AVD 1.5)?会发生  
  176.          * Connect E from ICameraClient 0x.... 
  177.          * new client (0x...) sttempting to connect - rejected 
  178.          * */  
  179.         mCamera01 = Camera.open();  
  180.       }  
  181.       catch(Exception e)  
  182.       {  
  183.         Log.e(TAG, e.getMessage());  
  184.       }  
  185.     }  
  186.       
  187.     if (mCamera01 != null && !bIfPreview)  
  188.     {  
  189.       try  
  190.       {  
  191.         Log.i(TAG, "inside the camera");  
  192.         mCamera01.setPreviewDisplay(mSurfaceHolder01);  
  193.         /* 建立Camera.Parameters对象 */  
  194.         Camera.Parameters parameters = mCamera01.getParameters();  
  195.         /* 设定相片格式为JPEG */  
  196.         parameters.setPictureFormat(PixelFormat.JPEG);  
  197.         //parameters.setPreviewSize(w, h);  
  198.         List<Camera.Size> s=parameters.getSupportedPreviewSizes();  
  199.           
  200.         try  
  201.         {  
  202.           if(s!=null)  
  203.           {  
  204.             for(int i=0;i<s.size();i++)  
  205.             {  
  206.               Log.i(TAG,""+(((Camera.Size)s.get(i)).height)+"/"+(((Camera.Size)s.get(i)).width));  
  207.             }  
  208.           }  
  209.           parameters.setPreviewSize(320240);  
  210.           //parameters.setPreviewSize(176, 144);  
  211.           
  212.           /* ?2.0模拟器中,设置不支持的PreviewSize将造成Exception */  
  213.           s=parameters.getSupportedPictureSizes();  
  214.           try  
  215.           {  
  216.             if(s!=null)  
  217.             {  
  218.               for(int i=0;i<s.size();i++)  
  219.               {  
  220.                 Log.i(TAG,""+(((Camera.Size)s.get(i)).height)+"/"+(((Camera.Size)s.get(i)).width));  
  221.               }  
  222.             }  
  223.             /* ?2.0模拟器中,设置不支持的PictureSize将造成Exception */  
  224.             parameters.setPictureSize(512384);  
  225.             //parameters.setPictureSize(213, 350);  
  226.             /* 将Camera.Parameters设定予Camera */  
  227.             mCamera01.setParameters(parameters);  
  228.             /* setPreviewDisplay唯几的参数为SurfaceHolder */  
  229.             mCamera01.setPreviewDisplay(mSurfaceHolder01);  
  230.             /* 立即运行Preview */  
  231.             mCamera01.startPreview();  
  232.               
  233.             bIfPreview = true;  
  234.             Log.i(TAG, "startPreview");  
  235.           }  
  236.           catch (Exception e)  
  237.           {  
  238.             Log.i(TAG, e.toString());  
  239.             e.printStackTrace();  
  240.           }     
  241.         }  
  242.         catch (Exception e)  
  243.         {  
  244.           Toast.makeText  
  245.           (  
  246.             EX07_15.this,  
  247.             "initCamera error.",  
  248.             Toast.LENGTH_LONG  
  249.           ).show();  
  250.           e.printStackTrace();  
  251.         }  
  252.       }  
  253.       catch (IOException e)  
  254.       {  
  255.         // TODO Auto-generated catch block  
  256.         mCamera01.release();  
  257.         mCamera01 = null;  
  258.         Log.i(TAG, e.toString());  
  259.         e.printStackTrace();  
  260.       }  
  261.     }  
  262.   }  
  263.     
  264.   /* 拍照撷取影像 */   
  265.   private void takePicture()   
  266.   {  
  267.     if (mCamera01 != null && bIfPreview)   
  268.     {  
  269.       /* 调用takePicture()方法拍照 */  
  270.       mCamera01.takePicture(shutterCallback, rawCallback, jpegCallback);  
  271.     }  
  272.   }  
  273.     
  274.   /* 相机重置 */  
  275.   private void resetCamera()  
  276.   {  
  277.     if (mCamera01 != null && bIfPreview)  
  278.     {  
  279.       mCamera01.stopPreview();  
  280.         
  281.       /* 延含学习,释放Camera对象 */  
  282.       //mCamera01.release();  
  283.       //mCamera01 = null;  
  284.         
  285.       Log.i(TAG, "stopPreview");  
  286.       bIfPreview = false;  
  287.     }  
  288.   }  
  289.      
  290.   private ShutterCallback shutterCallback = new ShutterCallback()   
  291.   {   
  292.     public void onShutter()   
  293.     {   
  294.       // Shutter has closed   
  295.     }   
  296.   };   
  297.      
  298.   private PictureCallback rawCallback = new PictureCallback()   
  299.   {   
  300.     public void onPictureTaken(byte[] _data, Camera _camera)   
  301.     {   
  302.       // TODO Handle RAW image data   
  303.     }   
  304.   };   
  305.   
  306.   private PictureCallback jpegCallback = new PictureCallback()   
  307.   {  
  308.     public void onPictureTaken(byte[] _data, Camera _camera)  
  309.     {  
  310.       // TODO Handle JPEG image data  
  311.         
  312.       /* onPictureTaken传入的第几个参数即为相片的byte */  
  313.       Bitmap bm = BitmapFactory.decodeByteArray(_data, 0, _data.length);   
  314.         
  315.       /* 创建文件 */  
  316.       File myCaptureFile = new File(strCaptureFilePath);  
  317.       try  
  318.       {  
  319.         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));  
  320.         /* 采用压缩转档方法 */  
  321.         bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);  
  322.           
  323.         /* 调用flush()方法,更新BufferStream */  
  324.         bos.flush();  
  325.           
  326.         /* 结束OutputStream */  
  327.         bos.close();  
  328.           
  329.         /* 将拍照下来且保存完毕的图文件,显示出来 */   
  330.         mImageView01.setImageBitmap(bm);  
  331.           
  332.         /* 显示完图片,立即重置相机,并关闭预览 */  
  333.         resetCamera();  
  334.           
  335.         /* 再重新启动相机继续预览 */  
  336.         initCamera();  
  337.       }  
  338.       catch (Exception e)  
  339.       {  
  340.         Log.e(TAG, e.getMessage());  
  341.         Log.e(TAG, e.toString());  
  342.       }  
  343.     }  
  344.   };  
  345.     
  346.   /* 自定义文件函数 */  
  347.   private void delFile(String strFileName)  
  348.   {  
  349.     try  
  350.     {  
  351.       File myFile = new File(strFileName);  
  352.       if(myFile.exists())  
  353.       {  
  354.         myFile.delete();  
  355.       }  
  356.     }  
  357.     catch (Exception e)  
  358.     {  
  359.       Log.e(TAG, e.toString());  
  360.       e.printStackTrace();  
  361.     }  
  362.   }  
  363.     
  364.   public void mMakeTextToast(String str, boolean isLong)  
  365.   {  
  366.     if(isLong==true)  
  367.     {  
  368.       Toast.makeText(EX07_15.this, str, Toast.LENGTH_LONG).show();  
  369.     }  
  370.     else  
  371.     {  
  372.       Toast.makeText(EX07_15.this, str, Toast.LENGTH_SHORT).show();  
  373.     }  
  374.   }  
  375.     
  376.   private boolean checkSDCard()  
  377.   {  
  378.     /* 判断存储卡是否存在 */  
  379.     if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))  
  380.     {  
  381.       return true;  
  382.     }  
  383.     else  
  384.     {  
  385.       return false;  
  386.     }  
  387.   }  
  388.     
  389.   @Override  
  390.   public void surfaceChanged(SurfaceHolder surfaceholder, int format, int w, int h)  
  391.   {  
  392.     // TODO Auto-generated method stub  
  393.     Log.i(TAG, "Surface Changed1");  
  394.   }  
  395.     
  396.   @Override  
  397.   public void surfaceCreated(SurfaceHolder surfaceholder)  
  398.   {  
  399.     // TODO Auto-generated method stub  
  400.     Log.i(TAG, "Surface Changed2");  
  401.   }  
  402.     
  403.   @Override  
  404.   public void surfaceDestroyed(SurfaceHolder surfaceholder)  
  405.   {  
  406.     // TODO Auto-generated method stub  
  407.     /* 当Surface不存在,就需要删除图片 */  
  408.     try  
  409.     {  
  410.       delFile(strCaptureFilePath);  
  411.       mCamera01.stopPreview();  
  412.       mCamera01.release();  
  413.       mCamera01 = null;  
  414.       Log.i(TAG, "Surface Destroyed");  
  415.     }  
  416.     catch(Exception e)  
  417.     {  
  418.       e.printStackTrace();  
  419.     }  
  420.   }  
  421.   
  422.   @Override  
  423.   protected void onPause()  
  424.   {  
  425.     // TODO Auto-generated method stub  
  426.     try  
  427.     {  
  428.       resetCamera();  
  429.       mCamera01.release();  
  430.     }  
  431.     catch(Exception e)  
  432.     {  
  433.       e.printStackTrace();  
  434.     }  
  435.     super.onPause();  
  436.   }  
  437.     


转自:http://blog.csdn.net/dlutbrucezhang/article/details/8571715

你可能感兴趣的:(android,Surface,相机预览)