Android調用系統相機、本身定義相機、處理大圖片

Android調用系統相機和本身定義相機實例


本博文主要是介紹了android上使用相機進行拍照並顯示的兩種方式,並且由於涉及到要把拍到
的照片顯示出來,該樣例也會涉及到Android載入大圖片時候的處理(避免OOM),還有簡要提一下有些人SurfaceView出現黑屏的緣由。




Android應用拍照的兩種方式,如下爲兩種形式的Demo展現出來的效果。java

   


知識點:android

1、調用系統自帶的相機應用

2、本身定義咱們本身的拍照界面算法


3、關於計算機解析圖片原理(怎樣正確載入圖片到Android應用中)緩存



所需權限:


<uses-permission android:name="android.permission.CAMERA" />
 
<uses-feature android:name="android.hardware.camera" />
 
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


1、調用系統照相機程序拍照(方式一)


1.定義所需要的權限
2.咱們需要定義調用系統相機App的Intent,固然是經過設定IntentFilter中的Action來打開我們想要的activity了。


MediaStore.ACTION_IMAGE_CAPTURE - 這個Action將打開拍照的系統相機。返回一個Image


MediaStore.ACTION_VIDEO_CAPTURE - 這個Action將打開錄像的系統相機。返回一個Video


在MediaStore.ACTION_IMAGE_CAPTURE中,咱們可以看到這段話:


【The caller may pass an extra EXTRA_OUTPUT to control where this image will be 


written. If the EXTRA_OUTPUT is not present, then a small sized image is returned 


as a Bitmap object in the extra field. This is useful for applications that only 


need a small image. If the EXTRA_OUTPUT is present, then the full-sized image will 


be written to the Uri value of EXTRA_OUTPUT.】



3.API規定咱們傳入拍照獲得圖片的存儲位置的Uri。不然Bimmap將以一個壓縮後的形式返回到
咱們當前Activity.


intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name

則會把拍照的圖片存儲到咱們傳入的Uri相應的File裏面。app



4.咱們調用startActivityForResult(intent)來啓動這樣一個系統相機app以後。而後在當前應
用Activity的onActivityResult()中接受到返回拍照成功或者失敗的消息,作相應處理。


5.「壓縮處理」(Android應用中載入大圖片),並顯示到ImageView中。


2、本身定義照相機


1.檢查相機是否存在,並獲取相機Camera。ide

2.建立一個相機圖像預覽類:extends SurfaceView 並 implements SurfaceHolder (我定義:MySurfaceView)
3.把這個預覽類放入一個本身定義佈局layout裏面,並且可以在layout里加入本身的其它button
4.設置相應的拍照button而後聽事件
5.捕獲照片和保存圖片

6.釋放掉咱們使用的相機Camera,否則以後其它應用將沒法使用它。佈局


3、計算機解析圖片的方式和Android中大圖片Bitmap的壓縮顯示處理
this

這個問題有點老生長談了,平時咱們經常遇到一些圖片資源。咱們把它載入到內存發現拋出內存不夠用的異常,即OOM。固然載入圖片時出現的OOM狀況有很是多種,比方單張圖片沒有作壓縮,致使圖片佔用內存過大而發生內存溢出。也有多張圖片一次性載入進來。致使的內存溢出。spa

一般單張大圖。咱們載入進來每每會通過一個圖片的壓縮處理的過程。而假設多張圖片載入,咱們可能就需要一些緩存機制。再加上一些算法來保證程序不出現OOM。.net

咱們這裏想要講的知識點跟單張大圖比較有關係

首先,咱們知道一個圖片,它是由很是多像素點來表示的,而像素點的個數僅僅跟圖片的分辨率有關。而跟圖片所佔的內存空間大小無關。比方咱們的桌面壁紙:1280 * 768 的分辨率,那麼它就有 1280 * 768 = 983040個像素點,這意味着什麼呢?咱們知道咱們要表示一個像素點的顏色。最經常咱們需要RGB三種顏色來表示,而R:0~255,至關於兩個FF的位置,就是8位。這種話RGB合起來,一個像素點的表示就需要24位(這就是咱們平衡聽到的24位圖),而加上透明度的8位,就是平時說的32位圖。那麼一張圖片,它載入到內存中的話,它會佔用多大的空間呢?

計算方法:(像素點 * 一個像素所佔用的byte數) / 1024 / 1024 (MB) 

1280 * 768 的分辨率。32位圖爲例:所佔內存大小: ((1280 * 768 * (32 / 8)) / 1024)/1024=3.75(MB)

說了這麼多,那麼咱們再來講下Android系統的規定吧。Android系統嚴格規定了每個應用所能分配的最大的內存爲多少,咱們知道有一個VM值(在咱們建立模擬器的時候),這個VM值裏面即是咱們所說的堆空間(Heap Size),當你的應用佔用的空間已經超出咱們定義的堆空間大小,那麼很差意思,OOM

這種話,咱們明確了圖片的大小佔領原理,還有儘可能不要超出這個堆空間,那麼OK,現在問題變得簡單了。假設咱們有一種方式可以在圖片載入進來以前,知道圖片的大小。而後改變它的長、寬,這種話,分辨率便變小了,這樣出來的乘積也就變小了。比方:咱們的屏幕僅僅有320 * 240。 這時候你載入大分辨的圖片進來最多也僅僅能顯示成這樣,因此咱們常採用的是對圖片進行壓縮處理。這裏有個概念叫壓縮比:

長:1024 / 320 = 3.2  約等於 3

寬:768 / 240 = 3.2 

那這樣咱們假設把圖片壓縮成這樣大小的,最後的圖片載入進來的大小即是

((320 * 240 * (32 / 8)) / 1024)/1024=0.29(MB)


但願我這樣講完,你們都能聽懂了。我這裏先把照相機實例中出現的關於假設處理這塊圖片的代碼先粘出來

//-----------------------Android大圖的處理方式---------------------------
	private void setPicToImageView(ImageView imageView, File imageFile){
		int imageViewWidth = imageView.getWidth();
		int imageViewHeight = imageView.getHeight();
		BitmapFactory.Options opts = new Options();
		
		//設置這個,僅僅獲得Bitmap的屬性信息放入opts,而不把Bitmap載入到內存中
		opts.inJustDecodeBounds = true;	
		BitmapFactory.decodeFile(imageFile.getPath(), opts);
		
		int bitmapWidth = opts.outWidth;
		int bitmapHeight = opts.outHeight;
		//取最大的比例。保證整個圖片的長或者寬必然在該屏幕中可以顯示得下
		int scale = Math.max(imageViewWidth / bitmapWidth, imageViewHeight / bitmapHeight);
		
		//縮放的比例
		opts.inSampleSize = scale;
		//內存不足時可被回收
		opts.inPurgeable = true;
		//設置爲false,表示不只Bitmap的屬性,也要載入bitmap
		opts.inJustDecodeBounds = false;
		
		Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath(), opts);
		imageView.setImageBitmap(bitmap);
	}


關於堆空間:

堆(HEAP)是VM中佔用內存最多的部分。通常是動態分配的。堆的大小不是一成不變的,一般有一個分配機制來控制它的大小。

比方初始的HEAP是4M大,當4M的空間被佔用超過75%的時候,又一次分配堆爲8M大。當8M被佔用超過75%,分配堆爲16M大。倒過來,當16M的堆利用不足30%的時候,縮減它的大小爲8M大。

又一次設置堆的大小,尤爲是壓縮,一般會涉及到內存的拷貝,因此變動堆的大小對效率有不良影響。


廢話少說如下就看代碼咯~~爲了你們看起來方便點,代碼的結構可能不是很是規範!


源代碼下載地址:http://download.csdn.net/detail/u011133213/7844683


代碼部分: 

1、用系統的相機


button點擊以後開啓系統相機Activity

findViewById(R.id.system_camera_btn).setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Intent intent = new Intent();
				intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
				imageFileUri = getOutFileUri(TYPE_FILE_IMAGE);//獲得一個File Uri
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
				startActivityForResult(intent, SYSTEM_CAMERA_REQUESTCODE);
			}
		});


生成File文件,並獲得Uri

//-----------------------生成Uri---------------------------------------
	//獲得輸出文件的URI
	private Uri getOutFileUri(int fileType) {
		return Uri.fromFile(getOutFile(fileType));
	}
	
	//生成輸出文件
	private File getOutFile(int fileType) {
		
		String storageState = Environment.getExternalStorageState();
		if (Environment.MEDIA_REMOVED.equals(storageState)){
			Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
			return null;
		}
		
		File mediaStorageDir = new File (Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
				,"MyPictures");
		if (!mediaStorageDir.exists()){
			if (!mediaStorageDir.mkdirs()){
				Log.i("MyPictures", "建立圖片存儲路徑文件夾失敗");
				Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
				return null;
			}
		}
		
		File file = new File(getFilePath(mediaStorageDir,fileType));
		
		return file;
	}
	//生成輸出文件路徑
	private String getFilePath(File mediaStorageDir, int fileType){
		String timeStamp =new SimpleDateFormat("yyyyMMdd_HHmmss")
							.format(new Date());
		String filePath = mediaStorageDir.getPath() + File.separator;
		if (fileType == TYPE_FILE_IMAGE){
			filePath += ("IMG_" + timeStamp + ".jpg");
		}else if (fileType == TYPE_FILE_VEDIO){
			filePath += ("VIDEO_" + timeStamp + ".mp4");
		}else{
			return null;
		}
		return filePath;
	}
	



2、用本身定義的相機


檢測相機設備是否存在:

/*檢測相機是否存在*/
	private boolean checkCameraHardWare(Context context){
		PackageManager packageManager = context.getPackageManager();
		if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)){
			return true;
		}
		return false;
	}

button按下以後的推斷:

findViewById(R.id.myapp_camera_btn).setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if (checkCameraHardWare(getApplicationContext())){
					Intent intent = new Intent(getApplicationContext(), MyCameraActivity.class);
					startActivity(intent);
				}else{
					Toast.makeText(getApplicationContext(), "沒有相機存在", Toast.LENGTH_SHORT).show();
				}
			}
		});
		

本身定義的SurfaceView類:

package cn.panghu.camera;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

	
	private Camera camera = null;
	private SurfaceHolder surfaceHolder = null;
	
	public MySurfaceView(Context context, Camera camera) {
		super(context);
		this.camera = camera;
		surfaceHolder = getHolder();
		surfaceHolder.addCallback(this);
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}
	public MySurfaceView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		try{
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		//根本沒有可處理的SurfaceView
		if (surfaceHolder.getSurface() == null){
			return ;
		}
		
		//先中止Camera的預覽
		try{
			camera.stopPreview();
		}catch(Exception e){
			e.printStackTrace();
		}
		
		//這裏可以作一些咱們要作的變換。

//又一次開啓Camera的預覽功能 try{ camera.setPreviewDisplay(surfaceHolder); camera.startPreview(); }catch(Exception e){ e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }

本身定義相機Activity類:(爲了不當用戶按下Home鍵,以後再回到咱們App中,SurfaceView變黑屏。咱們需要將SurfaceView載入到FrameLayout中的代碼寫在onResume中)

package cn.panghu.camera;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

import com.example.camerademoapp.R;

public class MyCameraActivity extends Activity {
	private Button btn_camera_capture = null;
	private Button btn_camera_cancel = null;
	private Button btn_camera_ok = null;
	
	private Camera camera = null;
	private MySurfaceView mySurfaceView = null;
	
	private byte[] buffer = null;
	
	private final int TYPE_FILE_IMAGE = 1;
	private final int TYPE_FILE_VEDIO = 2;
	
	private PictureCallback pictureCallback = new PictureCallback() {
		
		@Override
		public void onPictureTaken(byte[] data, Camera camera) {
			if (data == null){
				Log.i("MyPicture", "picture taken data: null");
			}else{
				Log.i("MyPicture", "picture taken data: " + data.length);
			}
			
			buffer = new byte[data.length];
			buffer = data.clone();
		}
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.mycamera_layout);
		
		btn_camera_capture = (Button) findViewById(R.id.camera_capture);
		btn_camera_ok = (Button) findViewById(R.id.camera_ok);
		btn_camera_cancel = (Button) findViewById(R.id.camera_cancel);
		
		
		btn_camera_capture.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				
				camera.takePicture(null, null, pictureCallback);
				
				btn_camera_capture.setVisibility(View.INVISIBLE);
				btn_camera_ok.setVisibility(View.VISIBLE);
				btn_camera_cancel.setVisibility(View.VISIBLE);
			}
		});
		btn_camera_ok.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//保存圖片
				saveImageToFile();
				
				camera.startPreview();
				btn_camera_capture.setVisibility(View.VISIBLE);
				btn_camera_ok.setVisibility(View.INVISIBLE);
				btn_camera_cancel.setVisibility(View.INVISIBLE);
			}
		});
		btn_camera_cancel.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				camera.startPreview();
				btn_camera_capture.setVisibility(View.VISIBLE);
				btn_camera_ok.setVisibility(View.INVISIBLE);
				btn_camera_cancel.setVisibility(View.INVISIBLE);				
			}
		});
	}
	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		
		camera.release();
		camera = null;
	}
	
	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		if (camera == null){
			camera = getCameraInstance();
		}
		//必須放在onResume中,否則會出現Home鍵以後。再回到該APP。黑屏
		mySurfaceView = new MySurfaceView(getApplicationContext(), camera);
		FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
		preview.addView(mySurfaceView);
	}
	
	/*獲得一相機對象*/
	private Camera getCameraInstance(){
		Camera camera = null;
		try{
			camera = camera.open();
		}catch(Exception e){
			e.printStackTrace();
		}
		return camera;
	}
	
	
	//-----------------------保存圖片---------------------------------------
	private void saveImageToFile(){
		File file = getOutFile(TYPE_FILE_IMAGE);
		if (file == null){
			Toast.makeText(getApplicationContext(), "文件建立失敗,請檢查SD卡讀寫權限", Toast.LENGTH_SHORT).show();
			return ;
		}
		Log.i("MyPicture", "本身定義相機圖片路徑:" + file.getPath());
		Toast.makeText(getApplicationContext(), "圖片保存路徑:" + file.getPath(), Toast.LENGTH_SHORT).show();
		if (buffer == null){
			Log.i("MyPicture", "本身定義相機Buffer: null");
		}else{
			try{
				FileOutputStream fos = new FileOutputStream(file);
				fos.write(buffer);
				fos.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}
	
	//-----------------------生成Uri---------------------------------------
	//獲得輸出文件的URI
	private Uri getOutFileUri(int fileType) {
		return Uri.fromFile(getOutFile(fileType));
	}
	
	//生成輸出文件
	private File getOutFile(int fileType) {
		
		String storageState = Environment.getExternalStorageState();
		if (Environment.MEDIA_REMOVED.equals(storageState)){
			Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
			return null;
		}
		
		File mediaStorageDir = new File (Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
				,"MyPictures");
		if (!mediaStorageDir.exists()){
			if (!mediaStorageDir.mkdirs()){
				Log.i("MyPictures", "建立圖片存儲路徑文件夾失敗");
				Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
				return null;
			}
		}
		
		File file = new File(getFilePath(mediaStorageDir,fileType));
		
		return file;
	}
	//生成輸出文件路徑
	private String getFilePath(File mediaStorageDir, int fileType){
		String timeStamp =new SimpleDateFormat("yyyyMMdd_HHmmss")
							.format(new Date());
		String filePath = mediaStorageDir.getPath() + File.separator;
		if (fileType == TYPE_FILE_IMAGE){
			filePath += ("IMG_" + timeStamp + ".jpg");
		}else if (fileType == TYPE_FILE_VEDIO){
			filePath += ("VIDEO_" + timeStamp + ".mp4");
		}else{
			return null;
		}
		return filePath;
	}

}
相關文章
相關標籤/搜索