Android Camera——拍照

 Android Camera——拍照

         Camera定义在package android.hardware内,具体用法SDK里叙述的可清楚了。架构解析什么的网上也有很多,没什么必要讲了(你认为我不知道我会说吗)。

         这篇呢,就整理了下Camera的拍照,其他还木有==
 
一、系统相机
         1)调用方式
         系统相机的入口Action:MediaStore.ACTION_IMAGE_CAPTURE。只需以startActivityForResult(…)启动该Activity即可。

  1. // 调用系统相机 
  2. Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
  3. startActivityForResult(intent, 1); 
         2)处理方式
         在onActivityResult(…)中,处理返回信息。

  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
  2.     if (1 == requestCode) { // 系统相机返回处理 
  3.         if (resultCode == Activity.RESULT_OK) { 
  4.             Bitmap cameraBitmap = (Bitmap) data.getExtras().get("data"); 
  5.             …… // 处理图像 
  6.         } 
  7.         takeBtn1.setClickable(true); 
  8.     }  
  9.     super.onActivityResult(requestCode, resultCode, data); 
 
二、自定义相机
         1)照相预览
         继承SufaceView写自己的预览界面,继而放到你的照相Activity的布局里。这里面有个相机拍照监听接口,用于在Activity里再处理这些操作。

  1. public class CameraPreview extends SurfaceView implements 
  2.         SurfaceHolder.Callback { 
  3.  
  4.     /** LOG标识 */ 
  5.     // private static final String TAG = "CameraPreview"; 
  6.  
  7.     /** 分辨率 */ 
  8.     public static final int WIDTH = 1024
  9.     public static final int HEIGHT = 768
  10.  
  11.     /** 监听接口 */ 
  12.     private OnCameraStatusListener listener; 
  13.  
  14.     private SurfaceHolder holder; 
  15.     private Camera camera; 
  16.  
  17.     // 创建一个PictureCallback对象,并实现其中的onPictureTaken方法 
  18.     private PictureCallback pictureCallback = new PictureCallback() { 
  19.  
  20.         // 该方法用于处理拍摄后的照片数据 
  21.         @Override 
  22.         public void onPictureTaken(byte[] data, Camera camera) { 
  23.  
  24.             // 停止照片拍摄 
  25.             camera.stopPreview(); 
  26.             camera = null
  27.  
  28.             // 调用结束事件 
  29.             if (null != listener) { 
  30.                 listener.onCameraStopped(data); 
  31.             } 
  32.         } 
  33.     }; 
  34.  
  35.     // Preview类的构造方法 
  36.     public CameraPreview(Context context, AttributeSet attrs) { 
  37.         super(context, attrs); 
  38.         // 获得SurfaceHolder对象 
  39.         holder = getHolder(); 
  40.         // 指定用于捕捉拍照事件的SurfaceHolder.Callback对象 
  41.         holder.addCallback(this); 
  42.         // 设置SurfaceHolder对象的类型 
  43.         holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
  44.     } 
  45.  
  46.     // 在surface创建时激发 
  47.     public void surfaceCreated(SurfaceHolder holder) { 
  48.         // Log.e(TAG, "==surfaceCreated=="); 
  49.         // 获得Camera对象 
  50.         camera = Camera.open(); 
  51.         try { 
  52.             // 设置用于显示拍照摄像的SurfaceHolder对象 
  53.             camera.setPreviewDisplay(holder); 
  54.         } catch (IOException e) { 
  55.             e.printStackTrace(); 
  56.             // 释放手机摄像头 
  57.             camera.release(); 
  58.             camera = null
  59.         } 
  60.     } 
  61.  
  62.     // 在surface销毁时激发 
  63.     public void surfaceDestroyed(SurfaceHolder holder) { 
  64.         // Log.e(TAG, "==surfaceDestroyed=="); 
  65.         // 释放手机摄像头 
  66.         camera.release(); 
  67.     } 
  68.  
  69.     // 在surface的大小发生改变时激发 
  70.     public void surfaceChanged(final SurfaceHolder holder, int format, int w, 
  71.             int h) { 
  72.         // Log.e(TAG, "==surfaceChanged=="); 
  73.         try { 
  74.             // 获取照相机参数 
  75.             Camera.Parameters parameters = camera.getParameters(); 
  76.             // 设置照片格式 
  77.             parameters.setPictureFormat(PixelFormat.JPEG); 
  78.             // 设置预浏尺寸 
  79.             parameters.setPreviewSize(WIDTH, HEIGHT); 
  80.             // 设置照片分辨率 
  81.             parameters.setPictureSize(WIDTH, HEIGHT); 
  82.             // 设置照相机参数 
  83.             camera.setParameters(parameters); 
  84.             // 开始拍照 
  85.             camera.startPreview(); 
  86.         } catch (Exception e) { 
  87.             e.printStackTrace(); 
  88.         } 
  89.     } 
  90.  
  91.     // 停止拍照,并将拍摄的照片传入PictureCallback接口的onPictureTaken方法 
  92.     public void takePicture() { 
  93.         // Log.e(TAG, "==takePicture=="); 
  94.         if (camera != null) { 
  95.             // 自动对焦 
  96.             camera.autoFocus(new AutoFocusCallback() { 
  97.                 @Override 
  98.                 public void onAutoFocus(boolean success, Camera camera) { 
  99.                     if (null != listener) { 
  100.                         listener.onAutoFocus(success); 
  101.                     } 
  102.                     // 自动对焦成功后才拍摄 
  103.                     if (success) { 
  104.                         camera.takePicture(nullnull, pictureCallback); 
  105.                     } 
  106.                 } 
  107.             }); 
  108.         } 
  109.     } 
  110.  
  111.     // 设置监听事件 
  112.     public void setOnCameraStatusListener(OnCameraStatusListener listener) { 
  113.         this.listener = listener; 
  114.     } 
  115.  
  116.     /** 
  117.      * 相机拍照监听接口 
  118.      */ 
  119.     public interface OnCameraStatusListener { 
  120.  
  121.         // 相机拍照结束事件 
  122.         void onCameraStopped(byte[] data); 
  123.  
  124.         // 拍摄时自动对焦事件 
  125.         void onAutoFocus(boolean success); 
  126.     } 
  127.  
 
         2)照相活动
         就是我们自己做的照相Activity了。完成后调用自己的相机,也就是跳转入这个Activity。这里面,照片以自定义路径的形式存入的媒体库。

  1. public class CameraActivity extends Activity implements 
  2.         CameraPreview.OnCameraStatusListener { 
  3.  
  4.     public static final Uri IMAGE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 
  5.     public static final String PATH = Environment.getExternalStorageDirectory() 
  6.             .toString() + "/AndroidMedia/"
  7.  
  8.     private CameraPreview mCameraPreview; 
  9.     private ImageView focusView; 
  10.     private boolean isTaking = false// 拍照中 
  11.  
  12.     @Override 
  13.     public void onCreate(Bundle savedInstanceState) { 
  14.         super.onCreate(savedInstanceState); 
  15.         // 设置横屏 
  16.         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 
  17.         // 设置全屏 
  18.         requestWindowFeature(Window.FEATURE_NO_TITLE); 
  19.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
  20.                 WindowManager.LayoutParams.FLAG_FULLSCREEN); 
  21.         // 设置布局视图 
  22.         setContentView(R.layout.camera); 
  23.         // 照相预览界面 
  24.         mCameraPreview = (CameraPreview) findViewById(R.id.preview); 
  25.         mCameraPreview.setOnCameraStatusListener(this); 
  26.         // 焦点图片 
  27.         focusView = (ImageView) findViewById(R.id.focusView); 
  28.     } 
  29.  
  30.     /** 
  31.      * 触屏事件 
  32.      */ 
  33.     @Override 
  34.     public boolean onTouchEvent(MotionEvent event) { 
  35.         if (event.getAction() == MotionEvent.ACTION_DOWN && !isTaking) { 
  36.             isTaking = true
  37.             mCameraPreview.takePicture(); 
  38.         } 
  39.         return super.onTouchEvent(event); 
  40.     } 
  41.  
  42.     /** 
  43.      * 存储图像并将信息添加入媒体数据库 
  44.      */ 
  45.     private Uri insertImage(ContentResolver cr, String name, long dateTaken, 
  46.             String directory, String filename, Bitmap source, byte[] jpegData) { 
  47.  
  48.         OutputStream outputStream = null
  49.         String filePath = directory + filename; 
  50.         try { 
  51.             File dir = new File(directory); 
  52.             if (!dir.exists()) { 
  53.                 dir.mkdirs(); 
  54.             } 
  55.             File file = new File(directory, filename); 
  56.             if (file.createNewFile()) { 
  57.                 outputStream = new FileOutputStream(file); 
  58.                 if (source != null) { 
  59.                     source.compress(CompressFormat.JPEG, 75, outputStream); 
  60.                 } else { 
  61.                     outputStream.write(jpegData); 
  62.                 } 
  63.             } 
  64.         } catch (FileNotFoundException e) { 
  65.             e.printStackTrace(); 
  66.             return null
  67.         } catch (IOException e) { 
  68.             e.printStackTrace(); 
  69.             return null
  70.         } finally { 
  71.             if (outputStream != null) { 
  72.                 try { 
  73.                     outputStream.close(); 
  74.                 } catch (Throwable t) { 
  75.                 } 
  76.             } 
  77.         } 
  78.         ContentValues values = new ContentValues(7); 
  79.         values.put(MediaStore.Images.Media.TITLE, name); 
  80.         values.put(MediaStore.Images.Media.DISPLAY_NAME, filename); 
  81.         values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken); 
  82.         values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); 
  83.         values.put(MediaStore.Images.Media.DATA, filePath); 
  84.         return cr.insert(IMAGE_URI, values); 
  85.     } 
  86.  
  87.     /** 
  88.      * 相机拍照结束事件 
  89.      */ 
  90.     @Override 
  91.     public void onCameraStopped(byte[] data) { 
  92.         Log.e("onCameraStopped""==onCameraStopped=="); 
  93.         // 创建图像 
  94.         Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 
  95.         // 系统时间 
  96.         long dateTaken = System.currentTimeMillis(); 
  97.         // 图像名称 
  98.         String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken) 
  99.                 .toString() + ".jpg"
  100.         // 存储图像(PATH目录) 
  101.         Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH, 
  102.                 filename, bitmap, data); 
  103.         // 返回结果 
  104.         Intent intent = getIntent(); 
  105.         intent.putExtra("uriStr", uri.toString()); 
  106.         intent.putExtra("dateTaken", dateTaken); 
  107.         // intent.putExtra("filePath", PATH + filename); 
  108.         // intent.putExtra("orientation", orientation); // 拍摄方向 
  109.         setResult(20, intent); 
  110.         // 关闭当前Activity 
  111.         finish(); 
  112.     } 
  113.  
  114.     /** 
  115.      * 拍摄时自动对焦事件 
  116.      */ 
  117.     @Override 
  118.     public void onAutoFocus(boolean success) { 
  119.         // 改变对焦状态图像 
  120.         if (success) { 
  121.             focusView.setImageResource(R.drawable.focus2); 
  122.         } else { 
  123.             focusView.setImageResource(R.drawable.focus1); 
  124.             Toast.makeText(this"焦距不准,请重拍!", Toast.LENGTH_SHORT).show(); 
  125.             isTaking = false
  126.         } 
  127.     } 
  128.  
 
3)相机调用&处理

  1. // 调用自定义相机 
  2. Intent intent = new Intent(this, CameraActivity.class); 
  3. startActivityForResult(intent, 2); 


  1. protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
  2.     if (2 == requestCode) { // 自定义相机返回处理 
  3.         // 拍照成功后,响应码是20 
  4.         if (resultCode == 20) { 
  5.             Bundle bundle = data.getExtras(); 
  6.             // 获得照片uri 
  7.             Uri uri = Uri.parse(bundle.getString("uriStr")); 
  8.             // 获得拍照时间 
  9.             long dateTaken = bundle.getLong("dateTaken"); 
  10.             try { 
  11.                 // 从媒体数据库获取该照片 
  12.                 Bitmap cameraBitmap = MediaStore.Images.Media.getBitmap( 
  13.                         getContentResolver(), uri); 
  14.                 previewBitmap(cameraBitmap); // 预览图像 
  15.                 // 从媒体数据库删除该照片(按拍照时间) 
  16.                 getContentResolver().delete( 
  17.                         CameraActivity.IMAGE_URI, 
  18.                         MediaStore.Images.Media.DATE_TAKEN + "=" 
  19.                                 + String.valueOf(dateTaken), null); 
  20.             } catch (Exception e) { 
  21.                 e.printStackTrace(); 
  22.             } 
  23.         } 
  24.         takeBtn2.setClickable(true); 
  25.     } 
  26.     super.onActivityResult(requestCode, resultCode, data); 
 
三、我加的相机蒙版^^
         这个最重要了,就是为了这个才把这还未完工的工程放上来的。
         布局文件里面我们在加一个自定义的MaskSurfaceView,注意放在相机预览的前面,并要设置成透明(包括重刷图层的时候也要注意透明度)。

  1.  <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:layout_width="fill_parent" 
  4.     android:layout_height="fill_parent" 
  5.     android:gravity="center" 
  6.     android:orientation="horizontal" > 
  7.  
  8.     <RelativeLayout 
  9.         android:layout_width="fill_parent" 
  10.         android:layout_height="fill_parent" 
  11.         android:layout_weight="1" > 
  12.  
  13.         <org.join.meida.camera.takephoto.MaskSurfaceView 
  14.             android:layout_width="fill_parent" 
  15.             android:layout_height="fill_parent" /> 
  16.  
  17.         <org.join.meida.camera.takephoto.CameraPreview 
  18.             android:id="@+id/preview" 
  19.             android:layout_width="fill_parent" 
  20.             android:layout_height="fill_parent" /> 
  21.  
  22.         <ImageView 
  23.             android:id="@+id/focusView" 
  24.             android:layout_width="wrap_content" 
  25.             android:layout_height="wrap_content" 
  26.             android:layout_centerInParent="true" 
  27.             android:src="@drawable/focus1" /> 
  28.     </RelativeLayout> 
  29.  
  30. </LinearLayout> 
 
         蒙版来啦,注意到心了没有?(3d爱心是网上别人实现的)

  1. public class MaskSurfaceView extends SurfaceView implements 
  2.         SurfaceHolder.Callback, Runnable { 
  3.  
  4.     // 定义SurfaceHolder对象 
  5.     private SurfaceHolder mSurfaceHolder; 
  6.     // 循环标记 
  7.     private boolean loop = true
  8.     // 循环间隔 
  9.     private static final long TIME = 300
  10.     // 计数器 
  11.     private int mCount; 
  12.     // 绘制方式 
  13.     private int mode; 
  14.  
  15.     public MaskSurfaceView(Context context, AttributeSet attrs) { 
  16.         super(context, attrs); 
  17.         mSurfaceHolder = getHolder(); // 获取SurfaceHolder 
  18.         mSurfaceHolder.addCallback(this); // 添加回调 
  19.         mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT); // 设置透明 
  20.     } 
  21.  
  22.     // 在surface创建时激发 
  23.     @Override 
  24.     public void surfaceCreated(SurfaceHolder holder) { 
  25.         mode = new Random().nextInt(2); // 随机一个[0-2)数 
  26.         new Thread(this).start(); // 开始绘制 
  27.     } 
  28.  
  29.     // 在surface的大小发生改变时激发 
  30.     @Override 
  31.     public void surfaceChanged(SurfaceHolder holder, int format, int width, 
  32.             int height) { 
  33.     } 
  34.  
  35.     // 在surface销毁时激发 
  36.     @Override 
  37.     public void surfaceDestroyed(SurfaceHolder holder) { 
  38.         loop = false
  39.     } 
  40.  
  41.     @Override 
  42.     public void run() { 
  43.         while (loop) { 
  44.             try { 
  45.                 Thread.sleep(TIME); 
  46.             } catch (InterruptedException e) { 
  47.                 e.printStackTrace(); 
  48.             } 
  49.             synchronized (mSurfaceHolder) { 
  50.                 drawMask(); 
  51.             } 
  52.         } 
  53.     } 
  54.  
  55.     /** 
  56.      * 绘制蒙版 
  57.      */ 
  58.     private void drawMask() { 
  59.         // 锁定画布,得到canvas 
  60.         Canvas mCanvas = mSurfaceHolder.lockCanvas(); 
  61.  
  62.         // 避免surface销毁后,线程唤醒仍进入绘制 
  63.         if (mSurfaceHolder == null || mCanvas == null
  64.             return
  65.  
  66.         int w = mCanvas.getWidth(); 
  67.         int h = mCanvas.getHeight(); 
  68.  
  69.         /* 创建一个画笔 */ 
  70.         Paint mPaint = new Paint(); 
  71.         mPaint.setAntiAlias(true); // 设置抗锯齿 
  72.         mPaint.setColor(0x00000000); // 设置透明黑色 
  73.         mCanvas.drawRect(00, w, h, mPaint); // 重绘背景 
  74.  
  75.         setPaintColor(mPaint); // 循环设置画笔颜色 
  76.         mPaint.setStyle(Paint.Style.STROKE); // 描边 
  77.  
  78.         if (0 == mode) { 
  79.             drawHeart2D(mCanvas, mPaint, w / 2, h / 2, h / 2); // 画一个2d爱心 
  80.         } else { 
  81.             drawHeart3D(mCanvas, mPaint); // 画一个3d爱心 
  82.         } 
  83.  
  84.         // 绘制后解锁,绘制后必须解锁才能显示 
  85.         mSurfaceHolder.unlockCanvasAndPost(mCanvas); 
  86.     } 
  87.  
  88.     /** 画一个2d爱心(半圆+sin曲线) */ 
  89.     private void drawHeart2D(Canvas mCanvas, Paint mPaint, int centerX, 
  90.             int centerY, float height) { 
  91.  
  92.         float r = height / 4
  93.         /* 心两半圆结点处 */ 
  94.         float topX = centerX; 
  95.         float topY = centerY - r; 
  96.  
  97.         /* 左上半圆 */ 
  98.         RectF leftOval = new RectF(topX - 2 * r, topY - r, topX, topY + r); 
  99.         mCanvas.drawArc(leftOval, 180f, 180f, false, mPaint); 
  100.         /* 右上半圆 */ 
  101.         RectF rightOval = new RectF(topX, topY - r, topX + 2 * r, topY + r); 
  102.         mCanvas.drawArc(rightOval, 180f, 180f, false, mPaint); 
  103.  
  104.         /* 下半两sin曲线 */ 
  105.         float base = 3 * r; 
  106.         double argu = Math.PI / 2 / base; 
  107.         float y = base, value; 
  108.         while (y >= 0) { 
  109.             value = (float) (2 * r * Math.sin(argu * (base - y))); 
  110.             mCanvas.drawPoint(topX - value, topY + y, mPaint); 
  111.             mCanvas.drawPoint(topX + value, topY + y, mPaint); 
  112.             y -= 1
  113.         } 
  114.  
  115.         // 1)心形函数:x²+(y-³√x²)²=1 
  116.         // >> x^2+(y-(x^2)^(1/3))^2=1 
  117.         // 
  118.         // 2)心形的各种画法: 
  119.         // >> http://woshao.com/article/1a855728bda511e0b40e000c29fa3b3a/ 
  120.         // 
  121.         // 3)笛卡尔情书的秘密——心形函数的绘制 
  122.         // >> http://www.cssass.com/blog/index.php/2010/808.html 
  123.     } 
  124.  
  125.     /** 画一个3d爱心 */ 
  126.     private void drawHeart3D(Canvas mCanvas, Paint mPaint) { 
  127.  
  128.         int w = mCanvas.getWidth(); 
  129.         int h = mCanvas.getHeight(); 
  130.  
  131.         /* 画一个3d爱心 */ 
  132.         int i, j; 
  133.         double x, y, r; 
  134.         for (i = 0; i <= 90; i++) { 
  135.             for (j = 0; j <= 90; j++) { 
  136.                 r = Math.PI / 45 * i * (1 - Math.sin(Math.PI / 45 * j)) * 20
  137.                 x = r * Math.cos(Math.PI / 45 * j) * Math.sin(Math.PI / 45 * i) 
  138.                         + w / 2
  139.                 y = -r * Math.sin(Math.PI / 45 * j) + h / 4
  140.                 mCanvas.drawPoint((float) x, (float) y, mPaint); 
  141.             } 
  142.         } 
  143.     } 
  144.  
  145.     /** 循环设置画笔颜色 */ 
  146.     private void setPaintColor(Paint mPaint) { 
  147.         mCount = mCount < 100 ? mCount + 1 : 0
  148.         switch (mCount % 6) { 
  149.         case 0
  150.             mPaint.setColor(Color.BLUE); 
  151.             break
  152.         case 1
  153.             mPaint.setColor(Color.GREEN); 
  154.             break
  155.         case 2
  156.             mPaint.setColor(Color.RED); 
  157.             break
  158.         case 3
  159.             mPaint.setColor(Color.YELLOW); 
  160.             break
  161.         case 4
  162.             mPaint.setColor(Color.argb(255255181216)); 
  163.             break
  164.         case 5
  165.             mPaint.setColor(Color.argb(2550255255)); 
  166.             break
  167.         default
  168.             mPaint.setColor(Color.WHITE); 
  169.             break
  170.         } 
  171.     } 
  172.  
 
四、后记
         今天是几号来着?T^T。
 

 

附件:http://down.51cto.com/data/2359820


     本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/779942,如需转载请自行联系原作者






上一篇:Android UI 优化——使用HierarchyViewer工具


下一篇:WPF界面设计技巧(10)-样式的继承