现在的位置: 首页 > 移动开发 > 正文

android学习笔记—手机拍照功能的实现,及原理

2019年09月20日 移动开发 ⁄ 共 12482字 ⁄ 字号 评论关闭
45_拍照
-------------------------------------------------------------------
在main.xml布局文件添加用于显示取景画面的SurfaceView 控件:
<SurfaceView android:layout_width="fill_parent" android:layout_height="240dip" android:id="@+id/surfaceView" />
SurfaceView surfaceView = (SurfaceView)this.findViewById(R.id.surfaceView);
surfaceView.getHolder().setFixedSize(176, 144); //设置分辨率
/*下面设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到用户面前*/
surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Camera camera = Camera.open();
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(display.getWidth(), display.getHeight());//设置预览照片的大小
parameters.setPreviewFrameRate(3);//每秒3帧
parameters.setPictureFormat(PixelFormat.JPEG);//设置照片的输出格式
parameters.set("jpeg-quality", 85);//照片质量
parameters.setPictureSize(display.getWidth(), display.getHeight());//设置照片的大小
camera.setParameters(parameters);
camera.setPreviewDisplay(surfaceView.getHolder());//通过SurfaceView显示取景画面
camera.startPreview();//开始预览
camera.autoFocus(null);//自动对焦
camera.takePicture(null, null, null, jpegCallback);//拍照片
camera.stopPreview();//停止预览
camera.release();//释放摄像头
-------------------------------------------------------------------------
<uses-permission android:name="android.permission.CAMERA"/>

package cn.itcast.picture;

import java.io.File;
import java.io.FileOutputStream;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;

public class TakePictureActivity extends Activity {
 private static final String TAG = "TakePictureActivity";
    private SurfaceView surfaceView;
    private Camera camera;
    private boolean preview;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Window window = getWindow();
     requestWindowFeature(Window.FEATURE_NO_TITLE);//没有标题
     window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏
     window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//高亮

        setContentView(R.layout.main);
       
        surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
        surfaceView.getHolder().addCallback(new SufaceListener());
        /*下面设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到用户面前*/
        surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceView.getHolder().setFixedSize(176, 144); //设置分辨率
    }
   
    private final class SufaceListener implements SurfaceHolder.Callback{
  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  }
  @Override
  public void surfaceCreated(SurfaceHolder holder) {
   try {
    camera = Camera.open();//打开摄像头
    Camera.Parameters parameters = camera.getParameters();
    WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    parameters.setPreviewSize(display.getWidth(), display.getHeight());//设置预览照片的大小
    parameters.setPreviewFrameRate(3);//每秒3帧
    parameters.setPictureFormat(PixelFormat.JPEG);//设置照片的输出格式
    parameters.set("jpeg-quality", 85);//照片质量
    parameters.setPictureSize(display.getWidth(), display.getHeight());//设置照片的大小
    camera.setParameters(parameters);
    camera.setPreviewDisplay(surfaceView.getHolder());//通过SurfaceView显示取景画面
    camera.startPreview();
    preview = true;
   } catch (Exception e) {
    Log.e(TAG, e.toString());
   }
  }
  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
   if(camera!=null){
    if(preview) camera.stopPreview();
    camera.release();
    camera = null;
   }
  }     
    }

 @Override
 public boolean onKeyDown(int keyCode, KeyEvent event) {
  if(camera!=null && event.getRepeatCount()==0){
   switch (keyCode) {
   case KeyEvent.KEYCODE_SEARCH:
    camera.autoFocus(null);//自动对焦
    break;
   case KeyEvent.KEYCODE_DPAD_CENTER:   
   case KeyEvent.KEYCODE_CAMERA:
    //拍照
    camera.takePicture(null, null, new PictureCallbackListener());
    break;
   }
  }
  return true;
 }

 private final class PictureCallbackListener implements Camera.PictureCallback{
  @Override
  public void onPictureTaken(byte[] data, Camera camera) {
   try {
    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
    File file = new File(Environment.getExternalStorageDirectory(), "itcast.jpg");
    FileOutputStream outStream = new FileOutputStream(file);
    bitmap.compress(CompressFormat.JPEG, 100, outStream);
    outStream.close();
    //重新浏览
    camera.stopPreview();
    camera.startPreview();
    preview = true;
   } catch (Exception e) {
    Log.e(TAG, e.toString());
   }
  }  
 }
}
-------------------------------------------------------------------------------------------------
1.拍照应用的界面:
---------------------
  a.横向,全屏
  b.--------------------------

    拍照区域

    ---------------------------
  c.右下角:  对焦   拍照
-------------------------------------------------
1.要求当用户触摸屏幕的时候显示:对焦   拍照
2.这两个按钮是显示在之上的.
----------------------------------------------------
3.所以这时候用的是帧布局.一层在一层之上.
4.也用到了相对布局
---------------------------------------------
下面是拍照的所有源码:
------------------------------
新建android项目:
1./takepicture/src/com/credream/camer/TakepictureActivity.java

package com.credream.camer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;


public class TakepictureActivity extends Activity {
 
   private View layout;//布局

    private Camera camera;//摄像头
  
  
    @Override
    
public void onCreate(Bundle savedInstanceState) {
    
    super.onCreate(savedInstanceState);
        
requestWindowFeature(Window.FEATURE_NO_TITLE);//设置activity没有标题栏
  
      getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        		WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置activity全屏显示
        setContentView(R.layout.main);
        //获取这个相对布局
        layout = this.findViewById(R.id.buttonlayout);
 //这里可以使用surfaceView来显示视频画面。
        SurfaceView surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
     //直接将画面显示到屏幕上,不再使用缓冲区
   surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
     //设置画面的分辨率
   surfaceView.getHolder().setFixedSize(176, 144);
   //保持屏幕的高亮,不要锁定屏幕
     surfaceView.getHolder().setKeepScreenOn(true);
       //当surfaceView被创建的时候就会调用
 surfaceView.getHolder().addCallback(new SurfaceCallback());
   
 }
    
   
 public void takepicture(View v){
    	
if(camera!=null){//拍照前判断,照相机对象不允许为null
    		
switch (v.getId()) {//当id为takepicture时候,
    	
	case R.id.takepicture:
    	
		camera.takePicture(null, null, new MyPictureCallback());
    	//camera.startPreview();//拍完照调用这个方法继续预览,这样是不可以的
		//camera.takePicture这个方法内部采用了异步保存照片,当camera.takePicture方法执行完后,摄像头可能还没有处理完照片
    	//所以直接在camera.takePicture方法后面调用camera.startPreview();方法是不对的
		//这个方法开始拍照,这三个参数是:快门按下去后
		/*
		 * null,第一个当快门按下去之后,会调用这个对象中的回调方法
		 * null,第二个参数是当摄像头拍完后,照片数据分为照片的原始数据,和照片经过压缩后的数据,第二个参数就是指照片的原始数据
		 * 第三个参数就是照片经过压缩后的数据
		 * new MyPictureCallback()这个方法就是得到摄像头拍摄完,并压缩后的数据
		 * 
		 * */
		break;
		//摄像头拍照的时候是不可以预览的,因为摄像头在某一个时刻只能做一件事情,当拍完照后就没有预览画面了。

    	
	case R.id.autofocus:
    
			camera.autoFocus(null);
			//这里的输入参数是:得到对焦成功之后的事件。,不想得到这个事件可以设为null
   
 			break;
    		
}
    	}
    }
    
  
  private final class MyPictureCallback implements PictureCallback{
	
	
public void onPictureTaken(byte[] data, Camera camera) {
	//byte[] data, Camera camera这个是压缩后的数据和摄像头
		try {
		//保存照片文件	Environment.getExternalStorageDirectory()这个是sd卡的根目录
	File jpgFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis()+".jpg");
			//文件输出流	
FileOutputStream outStream = new FileOutputStream(jpgFile);
	//把照片写到sd卡的根目录
			outStream.write(data);
	
			outStream.close();
		//截止到这里摄像头已经把照片处理完了,这时候在调用camera.startPreview();进入画面预览界面,才可以
		camera.startPreview();//写完数据后继续
			
} catch (Exception e) {
				e.printStackTrace();
		
	}
		}
    	
    }
    
   
 private final class SurfaceCallback implements Callback{
//当surfaceView被创建的时候执行这个方法
		public void surfaceCreated(SurfaceHolder holder) {

			try{
			
	camera = Camera.open();//打开摄像头,Camera这里Camera类,有两个这里用硬件中的那个import android.hardware.Camera;
				Camera.Parameters parameters = camera.getParameters();//不同厂商的摄像头,他们的参数是不同的。
				//注意这里需要真实的手机进行测试,因为这样才可以得到摄像头的各项参数
				//Log.i("TakepictureActivity", parameters.flatten());
				//可以通过Log.i("TakepictureActivity", parameters.flatten());来查看该摄像头的各项参数
				//parameters.flatten()这个方法用来查看,摄像头可以设置的各项参数
				//这些参数根据摄像头不同时不一样的
				parameters.setPreviewSize(800, 480);//摄像头捕获的照片大小
				parameters.setPreviewFrameRate(5);//每秒中摄像头捕获的画面
				parameters.setPictureSize(1024,768);//设置照片大小
				parameters.setJpegQuality(80);//设置照片的质量,100是最好的
				camera.setParameters(parameters);//设置完摄像头的参数后把参数再给摄像头
				camera.setPreviewDisplay(holder);//把摄像头捕获的数据给surfaceView
				camera.startPreview();//开始预览
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
		}
//当应用不在前台的时候调用这个方法
		public void surfaceDestroyed(SurfaceHolder holder) {
			if(camera!=null){
				camera.release();//释放摄像头
				camera = null;
			}
		}
    	
    }

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		//判断如果当前的事件是,触摸按下的事件的话。
		if(event.getAction() == MotionEvent.ACTION_DOWN){
			layout.setVisibility(ViewGroup.VISIBLE);//这时候就显示按钮。相对布局,
			//return true的时候就是把这个事件消费掉。不在往下传递。
			return true;
		}
		return super.onTouchEvent(event);
	}
    
    
}
-------------------------------------------------------------------

2./takepicture/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.credream.camer"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />
 <!-- android:screenOrientation="landscape"这句的作用是控制屏幕为横屏。 
 全屏,没有标题栏,和状态栏。
 -->
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".TakepictureActivity"
             android:screenOrientation="landscape" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    
      <!--这个是访问摄像头的权限 --> 
<uses-permission android:name="android.permission.CAMERA"/>
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

        

</manifest>
--------------------------------------------------------------------------------------

3./takepicture/res/layout/main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<!-- 这里用了帧布局 -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <!-- 拍照用到了SurfaceView控件 -->
	<SurfaceView  
	    android:layout_width="fill_parent" 
	    android:layout_height="fill_parent" 
	    android:id="@+id/surfaceView"
	    />
    <!-- 用到了相对布局 -->
    <RelativeLayout
   		android:layout_width="fill_parent" 
	    android:layout_height="fill_parent" 
	    android:visibility="gone"
	    android:id="@+id/buttonlayout"
    >
    <!-- android:onClick="takepicture"指定点击后执行的方法,
    android:layout_alignParentRight="true"指定在相对布局(也就是button按钮的父元素的右边)
    android:layout_alignParentBottom="true"指定在相对布局(也就是button按钮的父元素的下边)
    android:layout_marginRight="5dp"指的是右边距离相对布局(也就是button按钮的父元素为5个像素)
    
     -->
    <!-- 拍照按钮 -->
    	<Button
    	android:layout_width="wrap_content" 
	    android:layout_height="wrap_content" 
	    android:layout_alignParentRight="true"
	    android:layout_alignParentBottom="true"
	    android:layout_marginRight="5dp"
	    android:text="@string/takepicture"
	    android:onClick="takepicture"
	    android:id="@+id/takepicture"
    	/>
    	 <!-- android:layout_toLeftOf="@id/takepicture"指的是这个按钮在id为takepicture的组件的左边。
    	  android:layout_alignTop="@id/takepicture"指的是这个按钮和id为takepicture的组件的顶部对其。
    	  android:layout_marginRight="20dp"指的是这个按钮距离相对布局右边20个像素
    	  -->
    	<Button
    	android:layout_width="wrap_content" 
	    android:layout_height="wrap_content" 
	    android:layout_toLeftOf="@id/takepicture"
	    android:layout_alignTop="@id/takepicture"
	    android:layout_marginRight="20dp"
	    android:text="@string/autofocus"
	    android:onClick="takepicture"
	    android:id="@+id/autofocus"
    	/>    
    </RelativeLayout>
</FrameLayout>

------------------------------------------------------------------------------------------------------------------------

4. /takepicture/res/values/strings.xml

 

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="hello">Hello World, TakepictureActivity!</string>
   <string name="app_name">拍照</string>
    <string name="takepicture">拍照</string>
    <string name="autofocus">对焦</string>
</resources>

 

抱歉!评论已关闭.