編輯:關於Android編程
今天無意當中發現在《Android開發學習之基於ZBar實現微信掃一掃》中的一部分代碼可以用來以硬件方式實現一個照相機的功能,在《Android開發學習之調用系統相機完成拍照的實現》中我們是以Intent方式調用系統內置的相機來完成拍照的,今天呢,就讓我們來以Camera類為核心來開發自己的相機應用吧。在Android的官方文檔中,以硬件方式定制相機應用的步驟如下:
1. 檢查和訪問Camera
創建代碼來檢查Camera和所申請訪問的存在性;
2. 創建一個預覽類
繼承SurfaceView來創建一個Camera的預覽類,並實現SurfaceHolder接口。這個類用來預覽來自Camera的實施圖像。
3. 構建一個預覽布局
一旦有了Camera預覽類,就可以把這個預覽類和你想要的用戶界面控制結合在一起來創建一個視圖布局。
4. 針對采集建立監聽
把監聽器與響應用戶動作(如按下按鈕)的界面控制連接到一起來啟動圖像或視頻的采集。
5. 采集和保存文件
針對真正采集的圖片或視頻,以及輸出的保存來編寫代碼。
6. 釋放Camera
使用Camera之後,你的應用程序必須釋放Camera,以便其他應用程序能夠使用。
Camera硬件是一個必須要認真管理的共享資源,因此你的應用程序在使用它時,不能跟其他應用程序發生沖突。下文將討論如何檢查Camera硬件、如何申請對Camera的訪問,如何采集圖片和視頻,以及在應用使用完成後如何釋放Camera。
警告:在應用程序使用完Camera時,要記住通過調用Camera.release()方法來釋放Camera對象。如果你的應用程序沒有正確的釋放Camera,所有的後續的視圖對Camera的訪問,包括你自己的應用程序,都會失敗,並可能到你的或其他的應用程序關閉。
下面我們就來按照上面的步驟來一步步的制作一個屬於自己的相機應用吧!
第一步:檢查和訪問相機
/** 官方建議的安全地訪問攝像頭的方法 **/ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); } catch (Exception e){ Log.d("TAG", "Error is "+e.getMessage()); } return c; } /** 檢查設備是否支持攝像頭 **/ private boolean CheckCameraHardware(Context mContext) { if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // 攝像頭存在 return true; } else { // 攝像頭不存在 return false; } }
package com.android.OpenCamera; import java.io.IOException; import android.annotation.SuppressLint; import android.content.Context; import android.hardware.Camera; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; /** 一個基本的相機預覽界面類 **/ @SuppressLint("ViewConstructor") public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { /** Camera **/ private Camera mCamera; /** SurfaceHolder **/ private SurfaceHolder mHolder; /** CamreaPreview構造函數 **/ @SuppressWarnings("deprecation") public CameraPreview(Context mContext,Camera mCamera) { super(mContext); this.mCamera=mCamera; // 安裝一個SurfaceHolder.Callback, // 這樣創建和銷毀底層surface時能夠獲得通知。 mHolder = getHolder(); mHolder.addCallback(this); // 已過期的設置,但版本低於3.0的Android還需要 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } @Override public void surfaceCreated(SurfaceHolder holder) { try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); mCamera.setDisplayOrientation(90); } catch (IOException e) { Log.d("TAG", "Error is "+e.getMessage()); } } @Override public void surfaceDestroyed(SurfaceHolder arg0) { // 如果預覽無法更改或旋轉,注意此處的事件 // 確保在縮放或重排時停止預覽 if (mHolder.getSurface() == null){ // 預覽surface不存在 return; } // 更改時停止預覽 try { mCamera.stopPreview(); } catch (Exception e){ // 忽略:試圖停止不存在的預覽 } // 在此進行縮放、旋轉和重新組織格式 // 以新的設置啟動預 try { mCamera.setPreviewDisplay(mHolder); mCamera.setDisplayOrientation(90); mCamera.startPreview(); } catch (Exception e){ Log.d("TAG", "Error is " + e.getMessage()); } } }
<frameLayout android:id="@+id/PreviewView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.8" > </frameLayout>
/** 拍照回調接口 **/ private PictureCallback mPicureCallback=new PictureCallback() { @Override public void onPictureTaken(byte[] mData, Camera camera) { File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE); if (mPictureFile == null){ return; } try { /** 存儲照片 **/ FileOutputStream fos = new FileOutputStream(mPictureFile); fos.write(mData); fos.close(); /** 設置縮略圖 **/ Bitmap mBitmap=BitmapFactory.decodeByteArray(mData, 0, mData.length); ThumbsView.setImageBitmap(mBitmap); /** 獲取縮略圖Uri **/ mUri=StorageHelper.getOutputUri(mPictureFile); /**停止預覽**/ mCamera.stopPreview(); /**開始預覽**/ mCamera.startPreview(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } };
/** 快門回調接口 **/ private ShutterCallback mShutterCallback=new ShutterCallback() { @Override public void onShutter() { mPlayer=new MediaPlayer(); mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter); try { mPlayer.prepare(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mPlayer.start(); } };
package com.android.OpenCamera; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import android.annotation.SuppressLint; import android.net.Uri; import android.os.Environment; @SuppressLint("SimpleDateFormat") public final class StorageHelper { public static final int MEDIA_TYPE_IMAGE=0; public static final int MEDIA_TYPE_VIDEO=1; public static Uri getOutputUri(File mFile) { return Uri.fromFile(mFile); } public static File getOutputFile(int mType) { File mMediaFileDir=new File(Environment.getExternalStorageDirectory(),"OpenCamera"); if(!mMediaFileDir.exists()) { if(!mMediaFileDir.mkdir()) { return null; } } File mMediaFile=null; /** 創建文件名 **/ String mFileName=new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); switch(mType) { case MEDIA_TYPE_IMAGE: mMediaFile=new File(mMediaFileDir.getPath()+File.separator+"IMG_"+mFileName+".jpg"); break; case MEDIA_TYPE_VIDEO: mMediaFile=new File(mMediaFileDir.getPath()+File.separator+"VID_"+mFileName+".mp4"); break; default: mMediaFile=null; } return mMediaFile; } }
if (mCamera != null){ mCamera.release(); mCamera = null; }
package com.android.OpenCamera; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { /** 相機 **/ private Camera mCamera; /** 預覽界面 **/ private CameraPreview mPreview; /** 縮略圖 **/ ImageView ThumbsView; /** 當前縮略圖Uri **/ private Uri mUri; /** MediaPlayer **/ private MediaPlayer mPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /** 隱藏標題欄 **/ requestWindowFeature(Window.FEATURE_NO_TITLE); /** 隱藏狀態欄 **/ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); /** 禁用鎖屏,因為再次喚醒屏幕程序就會終止,暫時不知道怎麼解決 **/ getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_main); /** 硬件檢查 **/ if(CheckCameraHardware(this)==false) { Toast.makeText(this, "很抱歉,您的設備可能不支持攝像頭功能!", Toast.LENGTH_SHORT).show(); return; } /** 獲取相機 **/ mCamera=getCameraInstance(); /** 獲取預覽界面 **/ mPreview = new CameraPreview(this, mCamera); FrameLayout mFrameLayout = (FrameLayout)findViewById(R.id.PreviewView); mFrameLayout.addView(mPreview); mCamera.startPreview(); /** 拍照按鈕 **/ Button BtnCapture = (Button) findViewById(R.id.BtnCapture); BtnCapture.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /** 使用takePicture()方法完成拍照 **/ mCamera.autoFocus(new AutoFocusCallback() { /** 自動聚焦聚焦後完成拍照 **/ @Override public void onAutoFocus(boolean isSuccess, Camera camera) { if(isSuccess&&camera!=null) { mCamera.takePicture(mShutterCallback, null, mPicureCallback); } } }); } }); /** 相機縮略圖 **/ ThumbsView = (ImageView)findViewById(R.id.ThumbsView); ThumbsView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /** 使用Uri訪問當前縮略圖 **/ Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(mUri, "image/*"); startActivity(intent); } }); } /** 快門回調接口 **/ private ShutterCallback mShutterCallback=new ShutterCallback() { @Override public void onShutter() { mPlayer=new MediaPlayer(); mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter); try { mPlayer.prepare(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mPlayer.start(); } }; /** 拍照回調接口 **/ private PictureCallback mPicureCallback=new PictureCallback() { @Override public void onPictureTaken(byte[] mData, Camera camera) { File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE); if (mPictureFile == null){ return; } try { /** 存儲照片 **/ FileOutputStream fos = new FileOutputStream(mPictureFile); fos.write(mData); fos.close(); /** 設置縮略圖 **/ Bitmap mBitmap=BitmapFactory.decodeByteArray(mData, 0, mData.length); ThumbsView.setImageBitmap(mBitmap); /** 獲取縮略圖Uri **/ mUri=StorageHelper.getOutputUri(mPictureFile); /**停止預覽**/ mCamera.stopPreview(); /**開始預覽**/ mCamera.startPreview(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }; /** 官方建議的安全地訪問攝像頭的方法 **/ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); } catch (Exception e){ Log.d("TAG", "Error is "+e.getMessage()); } return c; } /** 檢查設備是否支持攝像頭 **/ private boolean CheckCameraHardware(Context mContext) { if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // 攝像頭存在 return true; } else { // 攝像頭不存在 return false; } } @Override protected void onPause() { super.onPause(); if (mCamera != null){ mCamera.release(); mCamera = null; } } @Override protected void onResume() { super.onResume(); if(mCamera == null) { mCamera = getCameraInstance(); mCamera.startPreview(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
晚上看到Camera360開放了SDK的消息。主推拍照和濾鏡API,其實開源的濾鏡有很多啦,結合我們今天的例子,我們完全可以做出一個不錯的相機應用來,作為長期受益於開源社區的回報,我決定在整合濾鏡以後把全部的程序開源,希望大家支持我哦。唉,明天要考試了,我竟然還能如此充滿激情的編程,其實這就是編程的魅力啦,在編程的世界裡,我可以完全按照自己的意願去做自己喜歡的,而這就夠了,當老師和同學們都覺得我在抱怨專業課的時候,其實我只是想讓自己的內心感到快樂而已。好了,晚安,各位!
今天這篇稍微增強點代碼量,可能要多花上5分鐘喽。本篇完成一個稍微顯得絢麗的菜單項,模仿優酷選擇菜單。如果想對其中的任意一項實現點擊功能,自行加入即可。現在就一步一步做出這
Android開發記錄16-友盟第三方登錄、分享實現 本篇博客給大家分享一個筆者正在做的關於第三方登錄、分享的實例,這裡選用的是友盟社會化組件。 博客大綱如
前面關於tween動畫寫了2篇博客,如果那2篇博客都看懂了,估計Tween動畫理解使用起來問題就不大,之前也說了Tween動畫的缺點就是在交互上有bug,今天就講下屬性動
Android 左右側滑示例。可以自已設置側滑的效果,我在這裡實現是左右都可以。側滑出來的界面可以啟動activity,或者加載view。側滑效果是用了碎片規則來處理的。