Camera API
Andorid 自5.0開始, 對Camera API 進行重大的改版, 稱為Camera API 2. 而5.0之前, 稱為Camera API 1. Google開始推動API 2的開發, 故凡是使用API 1的方法, 都會被畫上刪除線提示為過期.
盡使API 1被畫上刪除線, 但還是可以使用. 而使用API 2時, 相對的也增加了開發的難度, 不容易理解. 再加上API 1其實是學習照相機開發的基礎, 故本篇還是以 API 1 講解 相關原理
相機預覽元件
相機的預覽元件可以使用SurfaceView及TextureView, 此處以SurfaceView作說明
SurfaceView裏要顯示的資料可由多方來源提供, 比如由Ram, DMA, GPU等, 亦可由 Camera 提供數據. 要控制供應來源, 需先取得SurfaceView 的 SurfaceHoler, 再由 SurfaceHolder的 setType進行設定. SurfaceHolder是一個抽象介面, 用來控制SurfaceView的運作.
首先在layout中安置一個SurfaceView, 於onCreate中取得surfaceView物件後再由此物件取得surfaceHolder.
SurfaceView的建立需要花費一段相當長的時間(好幾百萬奈秒), 因此當建立完成, 或狀態改變時, 都會回調特定的方法, 這些方法全都集合於 SurfaceHoler.Callback類別中. 因此需要產生此一物件, 並由 holder.addCallback指定此回調物件
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN ); setContentView(R.layout.activity_main); this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); surfaceView=(SurfaceView)findViewById(R.id.cameraView); holder=surfaceView.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.addCallback(new CameraHolderCallback()); }
SurfaceHolder.Callback
實作SurfaceHolder.Callback介面需要實作三個方法 : surfaceCreated, surfaceChanged, surfaceDestroyed
SurfaceCreated是當SurfaceView建立完成時的回調方法, 可以在此處開啟相機, 設定參數等. 但在開啟相機前, 需先取得使用相機的權限, 所以可以在此要求 getPermission, 待真正取得權限, 再開啟相機, 設定相機與SurfaceView的連結, 並開啟預覽
SurfaceDestroyed是當SurfaceView銷毀時會被回調的方法, 因此可在此關閉相機的預覽並關閉相機
class CameraHolderCallback implements SurfaceHolder.Callback{ @Override public void surfaceCreated(SurfaceHolder holder) { getPermission(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(SurfaceHolder holder) { if(camera!=null){ camera.stopPreview(); camera.release(); camera=null; } } } private void getPermission(){ if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){ if(checkSelfPermission(Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String[]{ Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSION); } else startUpCamera(); } else{ startUpCamera(); } } //權限要求對畫框後的回調 @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode==REQUEST_PERMISSION) { Map<String, Integer> perms = new HashMap<String, Integer>(); // Initial perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED); perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED); perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED); // Fill with results for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); if (perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { startUpCamera(); } else { Toast.makeText(this, "No permission", Toast.LENGTH_LONG).show(); finish(); } } else{ super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void startUpCamera(){ camera=Camera.open(); try { camera.setPreviewDisplay(holder); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } }
預覽
上述程式碼中的startUpCamera() 是在取得權限後才執行, 此方法中實作了三項工作
開啟相機 : camera=Camera.open()
設定SurfaceView與相機的連結 : camera.setPreviewDisplay(holder)
開始預覽 : camera.startPreview
完整程式碼下載 : CameraTest