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" />
則會把拍照的圖片存儲到咱們傳入的Uri相應的File裏面。app
1.檢查相機是否存在,並獲取相機Camera。ide
2.建立一個相機圖像預覽類:extends SurfaceView 並 implements SurfaceHolder (我定義:MySurfaceView)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; }
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; } }