Android調用系統相機、自訂相機、處理大圖片,

來源:互聯網
上載者:User

Android調用系統相機、自訂相機、處理大圖片,
Android調用系統相機和自訂相機執行個體


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


Android應用拍照的兩種方式,下面為兩種形式的Demo展示出來的效果。

   


知識點:

一、調用系統內建的相機應用

二、自訂我們自己的拍照介面


三、關於電腦解析圖片原理(如何正確載入圖片到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.定義所需要的許可權
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裡面。



4.我們調用startActivityForResult(intent)來啟動這樣一個系統相機app之後,然後在當前應用Activity的onActivityResult()中接受到返回拍照成功或者失敗的訊息,做相應處理。


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


二、自訂照相機


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

2.建立一個相機映像預覽類:extends SurfaceView 並 implements SurfaceHolder (我定義:MySurfaceView)
3.把這個預覽類放入一個自訂布局layout裡面,並且可以在layout裡添加自己的其他按鈕
4.設定對應的拍照按鈕然後聽事件
5.捕獲照片和儲存圖片

6.釋放掉我們使用的相機Camera,不然之後其他應用將無法使用它。


三、電腦解析圖片的方式和Android中大圖片Bitmap的壓縮顯示處理

這個問題有點老生長談了,平時我們經常遇到一些圖片資源,我們把它載入到記憶體發現拋出記憶體不夠用的異常,即OOM,當然載入圖片時出現的OOM情況有很多種,比如單張圖片沒有做壓縮,導致圖片佔用記憶體過大而發生記憶體溢出,也有多張圖片一次性載入進來,導致的記憶體溢出。

通常單張大圖,我們載入進來往往會經過一個圖片的壓縮處理的過程,而如果多張圖片載入,我們可能就需要一些緩衝機制,再加上一些演算法來保證程式不出現OOM。

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

首先,我們知道一個圖片,它是由很多像素點來表示的,而像素點的個數只跟圖片的解析度有關,而跟圖片所佔的記憶體空間大小無關。比如我們的案頭壁紙: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的屬性,也要載入bitmapopts.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


代碼部分: 

一、用系統的相機


按鈕點擊之後開啟系統相機Activity

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


產生File檔案,並得到Uri

//-----------------------產生Uri---------------------------------------//得到輸出檔案的URIprivate 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;}



二、用自訂的相機


檢測相機裝置是否存在:

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

按鈕按下之後的判斷:

findViewById(R.id.myapp_camera_btn).setOnClickListener(new OnClickListener() {@Overridepublic 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}@Overridepublic void surfaceCreated(SurfaceHolder holder) {try{camera.setPreviewDisplay(surfaceHolder);camera.startPreview();}catch(Exception e){e.printStackTrace();}}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {//根本沒有可處理的SurfaceViewif (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();}}@Overridepublic 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() {@Overridepublic 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();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onClick(View v) {camera.startPreview();btn_camera_capture.setVisibility(View.VISIBLE);btn_camera_ok.setVisibility(View.INVISIBLE);btn_camera_cancel.setVisibility(View.INVISIBLE);}});}@Overrideprotected void onPause() {// TODO Auto-generated method stubsuper.onPause();camera.release();camera = null;}@Overrideprotected void onResume() {// TODO Auto-generated method stubsuper.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---------------------------------------//得到輸出檔案的URIprivate 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;}}






聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.