1. 程式人生 > >Android開發筆記(五十六)攝像頭拍照

Android開發筆記(五十六)攝像頭拍照

相機Camera

Camera是直接操作攝像頭硬體的工具類。常用的方法如下:

getNumberOfCameras : 獲取本機的攝像頭數目
open : 開啟攝像頭,預設開啟後置攝像頭。如果有多個攝像頭,那麼open(0)表示開啟後置攝像頭,open(1)表示開啟前置攝像頭。
以上兩個方法是靜態方法。

getParameters : 獲取攝像頭的拍照引數。
setParameters : 設定攝像頭的拍照引數。
--setPreviewSize : 設定預覽介面的尺寸。
--setPictureSize : 設定儲存圖片的尺寸。
--setPictureFormat : 設定圖片格式。一般使用ImageFormat.JPEG表示jpg格式,
--setFocusMode : 設定對焦模式。一般使用FOCUS_MODE_AUTO表示自動對焦。
以上是Camera.Parameters的引數設定方法。

setPreviewDisplay : 設定預覽介面,引數為SurfaceHolder型別。
startPreview : 開始預覽。該方法在setPreviewDisplay之後呼叫。
stopPreview : 停止預覽
unlock : 錄影時需要對攝像頭解鎖,這樣攝像頭才能持續錄影。該方法在startPreview之後呼叫。
lock : 錄影完畢對攝像頭加鎖。該方法在stopPreview之後呼叫。
setDisplayOrientation : 設定預覽的角度。因為Android的0角度都在三點鐘位置,而手機畫面都是六點鐘的垂直位置,所以從三點鐘到六點鐘需要旋轉90度。
autoFocus : 設定對焦事件,引數為AutoFocusCallback型別。比如說在對焦成功時顯示一個圖片提示使用者可以拍照了。
takePicture : 拍照。第一個引數ShutterCallback用來控制按下快門時的事件,我們可在此播放拍照聲音,預設就是咔嚓一聲;後面的幾個回撥介面PictureCallback分別對應原始影象、縮放和壓縮影象和JPG影象,影象資料可以在介面中的onPictureTaken方法中獲得,通常我們只關心最後一個的JPG影象資料,所以前面的介面引數可以直接傳null。
release : 釋放攝像頭。每次退出拍照都要釋放,因為攝像頭不能重複開啟,要麼就是把Camera物件做成單例模式。


預覽檢視SurfaceView/預覽持有者SurfaceHolder

SurfaceView是Android中的一種特殊檢視,它擁有獨立的繪圖表面,即它不與其宿主頁面共享同一個繪圖表面。由於擁有獨立的繪圖表面,因此SurfaceView的介面就可以在一個獨立的執行緒中進行繪製,我們稱之為渲染執行緒。因為它不佔用主執行緒資源,所以一方面可以實現複雜而高效的UI,另一方面也會及時響應使用者輸入。鑑於SurfaceView具備如上特性,故而它可用於拍照以及錄影的預覽介面,也可用於遊戲的畫面。


不過SurfaceView自身主要完成繪圖功能,其他功能設定以及事件處理還有待於SurfaceHolder來操作。SurfaceView的getHolder方法把二者關聯了起來,可獲取預覽介面當前幹活的操縱者。SurfaceHolder應與SurfaceView配合使用,下面是SurfaceHolder的常用方法:
addCallback : 添加回調介面
removeCallback : 移除回撥介面
isCreating : 判斷預覽介面是否有效
setFormat : 設定預覽格式。PixelFormat.TRANSPARENT表示透明,PixelFormat.TRANSLUCENT表示半透明,PixelFormat.OPAQUE表示不透明。
setFixedSize : 設定預覽介面的尺寸
setSizeFromLayout : 設定預覽介面的尺寸為佈局檔案中的配置
getSurfaceFrame : 獲取預覽介面的尺寸
getSurface : 獲取預覽檢視的物件。主要用於播放視訊。


拍照的相關事件

下面是幾個拍照用到的回撥事件介面:

預覽變化事件 
監聽器類名 : SurfaceHolder.Callback
設定監聽器的方法 : 
Camera.setPreviewDisplay : 新增預覽持有者SurfaceHolder。該方法用於關聯Camera和SurfaceHolder
SurfaceHolder.addCallback : 添加回調介面Callback。該方法用於關聯SurfaceView和SurfaceHolder,它與Camera.setPreviewDisplay最終聯合完成SurfaceView與Camera的關聯,即攝像頭的畫面展示在預覽介面上。
SurfaceHolder.removeCallback : 移除回撥介面Callback
監聽器需要重寫的方法 : 
surfaceCreated : 預覽建立。不管是拍照還是錄影,通常在該方法中設定拍照預覽Camera.setPreviewDisplay。
surfaceChanged : 預覽變化
surfaceDestroyed : 預覽結束。注意SurfaceView的渲染執行緒只在surfaceCreated和surfaceDestroyed之間有效,所以如果在別處操作SurfaceView畫面,得判斷當前預覽介面是否有效,也就是呼叫SurfaceHolder.isCreating方法來判斷。

自動對焦事件 
監聽器類名 : Camera.AutoFocusCallback
設定監聽器的方法 : Camera.autoFocus
監聽器需要重寫的方法 : onAutoFocus

快門按下事件 
監聽器類名 : Camera.ShutterCallback
設定監聽器的方法 : Camera.takePicture
監聽器需要重寫的方法 : onShutter

拍照事件 
監聽器類名 : Camera.PictureCallback
設定監聽器的方法 : Camera.takePicture
監聽器需要重寫的方法 : onPictureTaken

變焦事件 
監聽器類名 : Camera.OnZoomChangeListener
設定監聽器的方法 : Camera.setZoomChangeListener
監聽器需要重寫的方法 : onZoomChange


掃描二維碼

這個功能最有名的應用就是微信裡的“掃一掃”了,通過攝像頭拍照從二維碼中獲取相關資訊,然後再進行相應操作(比如說新增好友、下載檔案、訪問頁面等等)。Android中的二維碼掃描可用Google的zxing開源庫,再結合zxing的使用框架MipcaActivityCapture。
下面是zxing+MipcaActivityCapture框架的程式碼整合例子:
1、給工程加入zxing3.2.1.jar;
2、把MipcaActivityCapture原始碼(com.app.zxing)加入到工程;
3、編寫MipcaActivityCapture的佈局檔案activity_capture.xml,主要是加入SurfaceView和com.app.zxing.view.ViewfinderView兩個檢視,前一個檢視是預覽介面,後一個是掃碼介面;
4、如果需要調整掃描介面的UI,則修改ViewfinderView的onDraw方法,可加入新的元素或者調整尺寸。
5、對掃碼結果的處理見MipcaActivityCapture的handleDecode方法,視情況做相應處理,如新增好友、下載檔案、訪問頁面等等。


程式碼示例

下面是相機檢視CameraView的程式碼示例:
import com.example.exmcamera.util.BitmapUtil;
import com.example.exmcamera.util.MetricsUtil;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
	private static final String TAG = "CameraView";
	private Context mContext;
	private Camera mCamera;
    private Bitmap mBitmap = null;
	private SurfaceHolder mHolder = null;
	private boolean isPreviewing = false;
	private Point mCameraSize;
	private int mCameraType = CAMERA_BEHIND;
	public static int CAMERA_BEHIND = 0;
	public static int CAMERA_FRONT = 1;

	public CameraView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		mHolder = getHolder();
		mHolder.setFormat(PixelFormat.TRANSPARENT);//translucent半透明 transparent透明
		//mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		mHolder.addCallback(this);
	}

	public CameraView(Context context) {
		this(context, null);
	}

	public int getCameraType() {
		return mCameraType;
	}
	
	public void setCameraType(int CameraType) {
		mCameraType = CameraType;
	}

	public Bitmap getPhoto() {
		if (mBitmap != null) {
			Log.d(TAG, "mBitmap.size="+(mBitmap.getByteCount()/1024)+"K");
		} else {
			Log.d(TAG, "mBitmap is null.");
		}
		return mBitmap;
	}

	public void doTakePicture() {
		if(isPreviewing && (mCamera != null)) {
			mCamera.takePicture(mShutterCallback, null, mPictureCallback);
		}
	}

	//快門按下的回撥,在這裡我們可以設定類似播放“咔嚓”聲之類的操作。預設的就是咔嚓。
	ShutterCallback mShutterCallback = new ShutterCallback() {
		public void onShutter() {
			Log.d(TAG, "myShutterCallback:onShutter...");
		}
	};
	
	PictureCallback mPictureCallback = new PictureCallback() {
		public void onPictureTaken(byte[] data, Camera camera) {
			Log.d(TAG, "mPictureCallback:onPictureTaken...");
			Bitmap b = null;
			if(null != data) {
				b = BitmapFactory.decodeByteArray(data, 0, data.length);//data是位元組資料,將其解析成點陣圖
				mCamera.stopPreview();
				isPreviewing = false;
			}
			if (mCameraType == CameraView.CAMERA_BEHIND) {
				mBitmap = BitmapUtil.getRotateBitmap(b, 90);
			} else {
				mBitmap = BitmapUtil.getRotateBitmap(b, -90);
			}
			Log.d(TAG, "mBitmap.size="+(mBitmap.getByteCount()/1024)+"K");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//再次進入預覽
			mCamera.startPreview();
			isPreviewing = true;
		}
	};

	@Override
	public void surfaceCreated(SurfaceHolder mHolder) {
		// 當預覽檢視建立的時候開啟相機
		mCamera = Camera.open(mCameraType);
		try {
			// 設定預覽
			mCamera.setPreviewDisplay(mHolder);
			mCameraSize = MetricsUtil.getCameraSize(mCamera.getParameters(), MetricsUtil.getSize(mContext));
			Log.d(TAG, "width="+mCameraSize.x+", height="+mCameraSize.y);
		    Camera.Parameters parameters = mCamera.getParameters();
			// 設定預覽大小
		    parameters.setPreviewSize(mCameraSize.x, mCameraSize.y);
			// 設定圖片儲存時的解析度大小
			parameters.setPictureSize(mCameraSize.x, mCameraSize.y);
			// 設定格式
			parameters.setPictureFormat(ImageFormat.JPEG);
			// 設定自動對焦。前置攝像頭似乎無法自動對焦
			if (mCameraType == CameraView.CAMERA_BEHIND) {
				parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
			}
		    mCamera.setParameters(parameters);
		} catch (Exception e) {
			Log.d(TAG, "setPreviewDisplay error: "+e.getMessage());
			// 釋放相機資源並置空
			mCamera.release();
			mCamera = null;
		}
		return;
	}

	@Override
	public void surfaceChanged(SurfaceHolder mHolder, int format, int width, int height) {
		Log.d(TAG, "surfaceChanged");
		mCamera.setDisplayOrientation(90);
		// 開始預覽
		mCamera.startPreview();
		isPreviewing = true;
		mCamera.autoFocus(null);
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder mHolder) {
		Log.d(TAG, "surfaceDestroyed");
		mCamera.stopPreview();
		mCamera.release();
		mCamera = null;
	}

}


下面是拍照頁面CameraActivity的程式碼示例:
import java.text.SimpleDateFormat;
import java.util.Date;

import com.example.exmcamera.R;
import com.example.exmcamera.util.BitmapUtil;
import com.example.exmcamera.util.MetricsUtil;
import com.example.exmcamera.widget.CameraView;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageButton;

public class CameraActivity extends Activity implements OnClickListener {
	private static final String TAG = "CameraActivity";
	private CameraView cameraView;
	private ImageButton shutterBtn;
	private int mCameraType;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.item_camera);
		mCameraType = getIntent().getIntExtra("type", CameraView.CAMERA_BEHIND);
		cameraView = (CameraView)findViewById(R.id.camera_view);
		cameraView.setCameraType(mCameraType);
		shutterBtn = (ImageButton)findViewById(R.id.btn_shutter);
		shutterBtn.setOnClickListener(this);
		initViewParams();
	}

	private void initViewParams(){
		LayoutParams paramsLayout = cameraView.getLayoutParams();
		Point size = MetricsUtil.getSize(this);
		paramsLayout.width = size.x;
		paramsLayout.height = size.y;
		cameraView.setLayoutParams(paramsLayout);

		LayoutParams paramsButton = shutterBtn.getLayoutParams();
		paramsButton.width = MetricsUtil.dip2px(this, 60);
		paramsButton.height = MetricsUtil.dip2px(this, 60);
		shutterBtn.setLayoutParams(paramsButton);
	}

	@Override
	public void onBackPressed() {
        Intent intent = new Intent();
		Bundle bundle = new Bundle();
		Bitmap bitmap = cameraView.getPhoto();
		if (bitmap == null) {
			bundle.putString("is_null", "yes");
		} else {
			bundle.putString("is_null", "no");
			String path = String.format("%s%s.jpg", BitmapUtil.getCachePath(this),getNowDateTime());
			BitmapUtil.saveFile(cameraView.getPhoto(), path);
			bundle.putString("path", path);
		}
		intent.putExtras(bundle);
        setResult(Activity.RESULT_OK, intent);
        finish();
	}

	private String getNowDateTime() {
		SimpleDateFormat s_format = new SimpleDateFormat("yyyyMMddHHmmss");
		Date d_date = new Date();
		String s_date = "";
		s_date = s_format.format(d_date);
		return s_date;
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_shutter) {
			cameraView.doTakePicture();
		}
	}

}







點此檢視Android開發筆記的完整目錄

相關推薦

Android開發筆記攝像頭拍照

相機Camera Camera是直接操作攝像頭硬體的工具類。常用的方法如下: getNumberOfCameras : 獲取本機的攝像頭數目 open : 開啟攝像頭,預設開啟後置攝像頭。如果有多個攝像頭,那麼open(0)表示開啟後置攝像頭,open(1)表示開啟前置攝像

Java開發筆記方法的輸出引數

前面介紹了方法的輸入引數,與輸入引數相對應的則為輸出引數,輸出引數也被稱作方法的返回值,意思是經過方法的處理最終得到的運算數值。這個返回值可能是整型數,也可能是雙精度數,也可能是陣列等其它型別,甚至允許不返回任何引數。與輸入引數類似,輸出引數也需要定義資料型別,它的返回值型別在方法名稱前面定義,具體位置參見方

Java開發筆記方法的輸出參數

多個 精度 類型 運行出錯 stat 值類型 html static 其它 前面介紹了方法的輸入參數,與輸入參數相對應的則為輸出參數,輸出參數也被稱作方法的返回值,意思是經過方法的處理最終得到的運算數值。這個返回值可能是整型數,也可能是雙精度數,也可能是數組等其它類型,甚至

Java開發筆記字串的常用方法

不管是給字串賦值,還是對字串格式化,都屬於往字串填充內容,一旦內容填充完畢,則需開展進一步的處理。譬如一段Word文字,常見的加工操作就有查詢、替換、追加、擷取等等,按照字串的處理結果異同,可將這些操作方法歸為三大類,分別說明如下。一、判斷字串是否具備某種特徵該類方法主要用來判斷字串是否滿足某種條件,返回tr

Java開發筆記類的構造方法

前面介紹瞭如何定義一個簡單的類,以及它的成員屬性和成員方法,從示例程式碼可以看到,不管是OrangeSimple還是OrangeMember,都要先利用關鍵字new建立一個例項,然後才能通過例項名稱訪問成員屬性和成員方法。不知道大家有沒有注意到,new後面的類名跟著一副圓括號,就像下面程式碼這樣:

Android開發筆記圖片快取演算法

ImageCache 由於手機流量有限,又要加快app的執行效率,因此好的app都有做圖片快取。圖片快取說起來簡單,做起來就用到很多知識點,可算是集Android技術之大全了。只要理解圖片快取的演算法,並加以實踐把它做好,我覺得差不多可以懂半個Android的開發。快取策略

Java開發筆記對象的類型檢查

可靠 all 取值 得到 tar int 獲取 介紹 instance 前面介紹了類的多態性,來自於雞類的實例chicken,既能用來表達公雞實例,也能用來表達母雞實例。可是這導致了一個問題,假如在call方法內部需要手工判斷輸入參數屬於公雞實例還是母雞實例,那該如何是好?

Java開發筆記內部類和嵌套類

pro get print 資源 sys 文件 stat 解決 運用 通常情況下,一個Java代碼文件只定義一個類,即使兩個類是父類與子類的關系,也要把它們拆成兩個代碼文件分別定義。可是有些事物相互之間密切聯系,又不同於父子類的繼承關系,比如一棵樹會開很多花朵,這些花兒作為

Android開發筆記Runnable介面實現多執行緒

Runnable概述 Runnable介面可宣告一連串的事務,常用於多執行緒處理。但是實現Runnable介面並不意味著開啟了一個新執行緒,只是定義了接下來要做的事情,至於說這些事情要在主執行緒處理,還是在分執行緒處理,那得看我們在哪裡執行Runnable例項。如果在Han

Java開發筆記因抽象方法而產生的抽象類

用法 得到 之前 public http ref 操作 使用 brush 前面介紹了類的常見用法,令人感嘆面向對象的強大,幾乎日常生活中的所有事物,都可以抽象成Java的基類及其子類。然而抽象操作也有副作用,就是某個抽象而來的行為可能是不確定的,比如半夜雞叫,如果是公雞則必

Java開發筆記簡單接口及其實現

multi 舉例 動物 三種 behavior 不出 ant wim run方法 前面介紹了抽象方法及抽象類的用法,看似解決了不確定行為的方法定義,既然叫喚動作允許聲明為抽象方法,那麽飛翔、遊泳也能聲明為抽象方法,並且雞類涵蓋的物種不夠多,最好把這些行為動作擴展到鳥類這個群

Java開發筆記Java8之後的擴展接口

com 完整 不遠 urn 遊泳 java8 調用接口 基本 中國 前面介紹了接口的基本用法,有心的朋友可能註意到這麽一句話“在Java8以前,接口內部的所有方法都必須是抽象方法”,如此說來,在Java8之後,接口的內部方法也可能不是抽象方法了嗎?之所以Java8對接口的定

Android開發筆記使用Properties讀寫屬性值

Properties概述 Java中的配置檔案常為.properties檔案,而Properties類便是讀寫此類檔案的工具。屬性檔案有兩種格式,一種是文字格式,其內容是“鍵=值”的形式,文字註釋資訊可以用"#"來註釋。另一種是XML格式,鍵值對遵循XML規範,Androi

Android開發筆記手機資料庫Realm

Realm應用背景 Android自帶的SQLite資料庫,在多數場合能夠滿足我們的需求,但隨著app廣泛使用,SQLite也暴露了幾個不足之處: 1、開發者編碼比較麻煩,而且還要求開發者具備SQL語法知識; 2、SQLite預設沒有加密功能,手機一旦丟失容易導致資料庫被破解; 3、SQLite底層採用ja

Pro Android學習筆記:ActionBar3:搜尋條

ActionBar中的搜尋條 我們同樣可以在Action Bar中嵌入搜尋條。在小例子中,我們在action bar中嵌入一個搜尋框的widget(稱為search view)。當我們輸入搜尋內容,能夠在指定的activity中開啟(稱為searchable activitiy),本例不做實質的搜尋,只是

OpenCV開發筆記:紅胖子8分鐘帶你深入瞭解Haar、LBP特徵以及級聯分類器識別過程圖文並茂+淺顯易懂+程式原始碼

若該文為原創文章,未經允許不得轉載原博主部落格地址:https://blog.csdn.net/qq21497936原博主部落格導航:https://blog.csdn.net/qq21497936/article/details/102478062本文章部落格地址:https://blog.csdn.net

Android開發系列:Notification的功能與使用方法

font _id when ice extends 開發 content androi mark 關於消息的提示有兩種:一種是Toast,一種就是Notification。前者維持的時間比較短暫,後者維持的時間比較長。 並且我們尋常手機的應用比方網易、貼吧等等都有非常多

Linux學習筆記grep

grepgrepgrep [-cinvABC] ‘word‘ filename -c 行數-i 不區分大小寫-n 顯示行號-v 取反-r 遍歷所有子目錄-A 後面跟數字,過濾出符合要求的行以及下面n行-B 同上,過濾出符合要求的行以及上面n行-C 同上,同時過濾出符合要求的行以及上下各n行 mkdir /tm

Android開發實戰:淺談android:clipChildren屬性

.cn viewpage port 部分 lap ole 有一個 默認 版本 原文:Android開發實戰(二十一):淺談android:clipChildren屬性實現功能: 1、APP主界面底部模塊欄 2、ViewPager一屏多個界面顯示 3、........

Linux學習總結監控zabbix部署 下篇

zabbix 監控 zabbix 應用舉例 一 添加自定義監控項目 我們舉一個實例:監控web服務器80端口的並發連接數,並設置圖形。1 . 寫一個可以抓取數據的腳本,在客戶端zabbix-agent 上創建腳本 vim /usr/local/sbin/estab.sh #!/bin/bash net