Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發學習之以Camera方式實現相機功能(一)——快速實現相機

Android開發學習之以Camera方式實現相機功能(一)——快速實現相機

編輯:關於Android編程

今天無意當中發現在《Android開發學習之基於ZBar實現微信掃一掃》中的一部分代碼可以用來以硬件方式實現一個照相機的功能,在《Android開發學習之調用系統相機完成拍照的實現》中我們是以Intent方式調用系統內置的相機來完成拍照的,今天呢,就讓我們來以Camera類為核心來開發自己的相機應用吧。在Android的官方文檔中,以硬件方式定制相機應用的步驟如下:


1. 檢查和訪問Camera

創建代碼來檢查Camera和所申請訪問的存在性;

2. 創建一個預覽類

繼承SurfaceView來創建一個Camera的預覽類,並實現SurfaceHolder接口。這個類用來預覽來自Camera的實施圖像。

3. 構建一個預覽布局

一旦有了Camera預覽類,就可以把這個預覽類和你想要的用戶界面控制結合在一起來創建一個視圖布局。

4. 針對采集建立監聽

把監聽器與響應用戶動作(如按下按鈕)的界面控制連接到一起來啟動圖像或視頻的采集。

5. 采集和保存文件

針對真正采集的圖片或視頻,以及輸出的保存來編寫代碼。

6. 釋放Camera

使用Camera之後,你的應用程序必須釋放Camera,以便其他應用程序能夠使用。

Camera硬件是一個必須要認真管理的共享資源,因此你的應用程序在使用它時,不能跟其他應用程序發生沖突。下文將討論如何檢查Camera硬件、如何申請對Camera的訪問,如何采集圖片和視頻,以及在應用使用完成後如何釋放Camera。

警告:在應用程序使用完Camera時,要記住通過調用Camera.release()方法來釋放Camera對象。如果你的應用程序沒有正確的釋放Camera,所有的後續的視圖對Camera的訪問,包括你自己的應用程序,都會失敗,並可能到你的或其他的應用程序關閉。

下面我們就來按照上面的步驟來一步步的制作一個屬於自己的相機應用吧!

第一步:檢查和訪問相機

/** 官方建議的安全地訪問攝像頭的方法  **/
	public static Camera getCameraInstance(){ 
	    Camera c = null; 
	    try { 
	        c = Camera.open();
	    } 
	    catch (Exception e){
	    Log.d("TAG", "Error is "+e.getMessage());
	    } 
	    return c;
	}
	
	/** 檢查設備是否支持攝像頭  **/
	private boolean CheckCameraHardware(Context mContext)
	{
		if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA))
		{ 
	        // 攝像頭存在 
	        return true; 
	    } else { 
	        // 攝像頭不存在 
	        return false; 
	    } 
	}
	

第二步:創建一個預覽類。這裡我們使用的是官方API中提供的一個基本的預覽類:

package com.android.OpenCamera;

import java.io.IOException;

import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/** 一個基本的相機預覽界面類    **/
@SuppressLint("ViewConstructor")
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

	/** Camera **/
	private Camera mCamera;
	/** SurfaceHolder **/
	private SurfaceHolder mHolder;
	
	/** CamreaPreview構造函數   **/
	@SuppressWarnings("deprecation")
	public CameraPreview(Context mContext,Camera mCamera) 
	{
		super(mContext);
		this.mCamera=mCamera;
        // 安裝一個SurfaceHolder.Callback,
        // 這樣創建和銷毀底層surface時能夠獲得通知。
        mHolder = getHolder(); 
        mHolder.addCallback(this); 
        // 已過期的設置,但版本低於3.0的Android還需要
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
	}

	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) 
	{
		
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) 
	{
	   try {
		mCamera.setPreviewDisplay(holder);
		mCamera.startPreview();
		mCamera.setDisplayOrientation(90);
	   } catch (IOException e) {
		Log.d("TAG", "Error is "+e.getMessage());
	   }	
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) 
	{
		// 如果預覽無法更改或旋轉,注意此處的事件
        // 確保在縮放或重排時停止預覽
        if (mHolder.getSurface() == null){ 
          // 預覽surface不存在
          return; 
        } 
        // 更改時停止預覽 
        try { 
            mCamera.stopPreview(); 
        } catch (Exception e){ 
          // 忽略:試圖停止不存在的預覽
        } 
        // 在此進行縮放、旋轉和重新組織格式
        // 以新的設置啟動預
        try { 
            mCamera.setPreviewDisplay(mHolder); 
            mCamera.setDisplayOrientation(90); 
            mCamera.startPreview(); 
        } catch (Exception e){ 
            Log.d("TAG", "Error is " + e.getMessage()); 
        } 
		
	}

}

第三步:構建預覽布局。這裡我們使用FrameLayout來加載第二步創建的預覽類,使用一個按鈕進行拍照並完成儲存,使用一個ImageView顯示拍照的縮略圖,當我們點擊這個縮略圖時時,系統將會調用相應的程序來打開這個圖片。


    <frameLayout
        android:id="@+id/PreviewView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.8" >
    </frameLayout>
    
        

第四步:針對采集監聽。其中PictureCallback接口用於處理拍照的結果,這裡我們做三件事情,第一,保存圖片;第二,顯示縮略圖;第三、保存當前圖片的Uri,以便於系統的訪問。ShutterCallback接口用於處理按下快門的事件,這裡我們使用MediaPlayer類來播放快門按下時的音效。

/** 拍照回調接口  **/
	private PictureCallback mPicureCallback=new PictureCallback()
	{
		@Override
		public void onPictureTaken(byte[] mData, Camera camera) 
		{
			File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE); 
	        if (mPictureFile == null){  
	            return; 
	        }
	        try { 
	        	/** 存儲照片  **/
	            FileOutputStream fos = new FileOutputStream(mPictureFile); 
	            fos.write(mData); 
	            fos.close(); 
	            /** 設置縮略圖  **/
	            Bitmap mBitmap=BitmapFactory.decodeByteArray(mData, 0, mData.length);
	            ThumbsView.setImageBitmap(mBitmap);
	            /** 獲取縮略圖Uri  **/
	            mUri=StorageHelper.getOutputUri(mPictureFile);
	            /**停止預覽**/
	            mCamera.stopPreview();
	            /**開始預覽**/
	            mCamera.startPreview();
	        } catch (FileNotFoundException e) { 
	        	e.printStackTrace();
	        } catch (IOException e) {
				e.printStackTrace();
			}
		}
	};
	
/** 快門回調接口  **/
    private ShutterCallback mShutterCallback=new ShutterCallback()
    {
		@Override
		public void onShutter() 
		{
			mPlayer=new MediaPlayer();
			mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter);
			try {
				mPlayer.prepare();
			} catch (IllegalStateException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			mPlayer.start();
		}
    };

第五步:采集與保存。采集與保存在第四步已經給出了,這裡給出一個用於保存文件的輔助類StorageHelper類:

package com.android.OpenCamera;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.annotation.SuppressLint;
import android.net.Uri;
import android.os.Environment;

@SuppressLint("SimpleDateFormat")
public final class StorageHelper 
{
   public static final int MEDIA_TYPE_IMAGE=0;
   public static final int MEDIA_TYPE_VIDEO=1;
   
   public static Uri  getOutputUri(File mFile)
   {
	   return Uri.fromFile(mFile);
   }
   
   
   public static File getOutputFile(int mType)
   {
	   File mMediaFileDir=new File(Environment.getExternalStorageDirectory(),"OpenCamera");
	   if(!mMediaFileDir.exists())
	   {
		   if(!mMediaFileDir.mkdir())
			{
			   return null;
			}
	   }
	   File mMediaFile=null;
	   /**  創建文件名   **/
	   String mFileName=new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
	   switch(mType)
	   {
	     case MEDIA_TYPE_IMAGE:
	    	 mMediaFile=new File(mMediaFileDir.getPath()+File.separator+"IMG_"+mFileName+".jpg");
	    	 break;
	     case MEDIA_TYPE_VIDEO:
	    	 mMediaFile=new File(mMediaFileDir.getPath()+File.separator+"VID_"+mFileName+".mp4");
	    	 break; 
	    	 default:
	    		 mMediaFile=null;
	   }
	   return mMediaFile;
   }
}

第六步:相機的釋放

if (mCamera != null){ 
            mCamera.release();
            mCamera = null; 
        } 

通過以上步驟,我們就完成了一個簡單的相機,最後給出主要的邏輯代碼:

package com.android.OpenCamera;


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

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

	
	/** 相機   **/
	private Camera mCamera;  
	/** 預覽界面  **/
	private CameraPreview mPreview;
	/** 縮略圖  **/
	ImageView ThumbsView;
	/** 當前縮略圖Uri **/
	private Uri mUri;
	/** MediaPlayer **/
	private MediaPlayer mPlayer;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		/** 隱藏標題欄  **/
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		/** 隱藏狀態欄  **/
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        /** 禁用鎖屏,因為再次喚醒屏幕程序就會終止,暫時不知道怎麼解決  **/
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		setContentView(R.layout.activity_main);
		
		/** 硬件檢查  **/
		if(CheckCameraHardware(this)==false)
		{
			Toast.makeText(this, "很抱歉,您的設備可能不支持攝像頭功能!", Toast.LENGTH_SHORT).show();
			return;
		}
		
		/** 獲取相機  **/
		mCamera=getCameraInstance();
		/** 獲取預覽界面   **/
        mPreview = new CameraPreview(this, mCamera); 
        FrameLayout mFrameLayout = (FrameLayout)findViewById(R.id.PreviewView); 
        mFrameLayout.addView(mPreview); 
        mCamera.startPreview();
        /** 拍照按鈕  **/
        Button BtnCapture = (Button) findViewById(R.id.BtnCapture);
        BtnCapture.setOnClickListener(new OnClickListener()
        {
			@Override
			public void onClick(View v) 
			{
				/** 使用takePicture()方法完成拍照  **/
				mCamera.autoFocus(new AutoFocusCallback()
				{
					/** 自動聚焦聚焦後完成拍照  **/
					@Override
					public void onAutoFocus(boolean isSuccess, Camera camera) 
					{
						if(isSuccess&&camera!=null)
						{
							mCamera.takePicture(mShutterCallback, null, mPicureCallback);
						}
					}
					
				});
			}
        });
        
        /** 相機縮略圖  **/
        
        ThumbsView = (ImageView)findViewById(R.id.ThumbsView);
        ThumbsView.setOnClickListener(new OnClickListener()
        {
			@Override
			public void onClick(View v) 
			{
				/** 使用Uri訪問當前縮略圖  **/
				Intent intent = new Intent(Intent.ACTION_VIEW); 
			    intent.setDataAndType(mUri, "image/*");
			    startActivity(intent);
			}
        });
	}
	/** 快門回調接口  **/
    private ShutterCallback mShutterCallback=new ShutterCallback()
    {
		@Override
		public void onShutter() 
		{
			mPlayer=new MediaPlayer();
			mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter);
			try {
				mPlayer.prepare();
			} catch (IllegalStateException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			mPlayer.start();
		}
    };
    
	/** 拍照回調接口  **/
	private PictureCallback mPicureCallback=new PictureCallback()
	{
		@Override
		public void onPictureTaken(byte[] mData, Camera camera) 
		{
			File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE); 
	        if (mPictureFile == null){  
	            return; 
	        }
	        try { 
	        	/** 存儲照片  **/
	            FileOutputStream fos = new FileOutputStream(mPictureFile); 
	            fos.write(mData); 
	            fos.close(); 
	            /** 設置縮略圖  **/
	            Bitmap mBitmap=BitmapFactory.decodeByteArray(mData, 0, mData.length);
	            ThumbsView.setImageBitmap(mBitmap);
	            /** 獲取縮略圖Uri  **/
	            mUri=StorageHelper.getOutputUri(mPictureFile);
	            /**停止預覽**/
	            mCamera.stopPreview();
	            /**開始預覽**/
	            mCamera.startPreview();
	        } catch (FileNotFoundException e) { 
	        	e.printStackTrace();
	        } catch (IOException e) {
				e.printStackTrace();
			}
		}
	};
	
	
	/** 官方建議的安全地訪問攝像頭的方法  **/
	public static Camera getCameraInstance(){ 
	    Camera c = null; 
	    try { 
	        c = Camera.open();
	    } 
	    catch (Exception e){
	    Log.d("TAG", "Error is "+e.getMessage());
	    } 
	    return c;
	}
	
	/** 檢查設備是否支持攝像頭  **/
	private boolean CheckCameraHardware(Context mContext)
	{
		if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA))
		{ 
	        // 攝像頭存在 
	        return true; 
	    } else { 
	        // 攝像頭不存在 
	        return false; 
	    } 
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		if (mCamera != null){ 
            mCamera.release();
            mCamera = null; 
        } 
	}

	@Override
	protected void onResume() 
	{
		super.onResume();
		if(mCamera == null)
        {
            mCamera = getCameraInstance();
            mCamera.startPreview(); 	
        }
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

這裡一直有一個困惑我的問題,就是當手機鎖屏以後再次喚醒屏幕,程序會立即終止。因為找不到原因,所以目前只能用代碼控制設備禁止鎖屏來避免這個問題的發生。這個問題和《Android開發學習之基於ZBar實現微信掃一掃》是一樣的,留給大家去思考了。記得給程序加入以下權限:


        
        

晚上看到Camera360開放了SDK的消息。主推拍照和濾鏡API,其實開源的濾鏡有很多啦,結合我們今天的例子,我們完全可以做出一個不錯的相機應用來,作為長期受益於開源社區的回報,我決定在整合濾鏡以後把全部的程序開源,希望大家支持我哦。唉,明天要考試了,我竟然還能如此充滿激情的編程,其實這就是編程的魅力啦,在編程的世界裡,我可以完全按照自己的意願去做自己喜歡的,而這就夠了,當老師和同學們都覺得我在抱怨專業課的時候,其實我只是想讓自己的內心感到快樂而已。好了,晚安,各位!


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved