Android自定義照相機實現
其實開發一款照相機軟件並不太難,下面就是通過自定義的方式來實現手機照相的功能。
創建一個項目:FingerTakePicture
首先來搞一下界面:
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/FrameLayout1"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <!-- 顯示預覽圖形 -->
- <SurfaceView
- android:id="@+id/surfaceView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
- <!-- 相對佈局,放置兩個按鈕 -->
- <RelativeLayout
- android:id="@+id/buttonLayout"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- >
- <!-- 拍照按鈕 -->
- <Button
- android:id="@+id/takepicture"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:background="@drawable/btn_tabkepicture_selector"
- android:onClick="btnOnclick"
- />
- <ImageView
- android:id="@+id/scalePic"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:layout_marginLeft="5dp"
- android:background="@drawable/img_showpic_selector"
- android:onClick="imageClick"
- />
- </RelativeLayout>
- </FrameLayout>
界面效果(無法把預覽給截屏下來滴):
權限設置少不了:
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.wwj.finger"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="4"
- android:targetSdkVersion="15" />
- <uses-permission android:name="android.permission.CAMERA" />
- <!-- 在SDCard中創建與刪除文件權限 -->
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
- <!-- 往SDCard寫入數據權限 -->
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name=".MainActivity"
- android:label="@string/title_activity_main"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <activity
- android:name=".ShowPicActivity"
- android:label="@string/app_name"
- android:theme="@style/AppTheme"
- android:configChanges="orientation|keyboardHidden"
- ></activity>
- </application>
- </manifest>
主Activity:
- package com.wwj.finger;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import android.app.Activity;
- import android.content.Intent;
- import android.graphics.PixelFormat;
- import android.hardware.Camera;
- import android.hardware.Camera.PictureCallback;
- import android.os.Bundle;
- import android.os.Environment;
- import android.view.KeyEvent;
- import android.view.MotionEvent;
- import android.view.Surface;
- import android.view.SurfaceHolder;
- import android.view.SurfaceHolder.Callback;
- import android.view.SurfaceView;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.Toast;
- /**
- * Android手指拍照
- *
- * @author wwj
- * @date 2013/4/29
- */
- public class MainActivity extends Activity {
- private View layout;
- private Camera camera;
- private Camera.Parameters parameters = null;
- Bundle bundle = null; // 聲明一個Bundle對象,用來存儲數據
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 顯示界面
- setContentView(R.layout.activity_main);
- layout = this.findViewById(R.id.buttonLayout);
- SurfaceView surfaceView = (SurfaceView) this
- .findViewById(R.id.surfaceView);
- surfaceView.getHolder()
- .setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- surfaceView.getHolder().setFixedSize(176, 144); //設置Surface分辨率
- surfaceView.getHolder().setKeepScreenOn(true);// 屏幕常亮
- surfaceView.getHolder().addCallback(new SurfaceCallback());//爲SurfaceView的句柄添加一個回調函數
- }
- /**
- * 按鈕被點擊觸發的事件
- *
- * @param v
- */
- public void btnOnclick(View v) {
- if (camera != null) {
- switch (v.getId()) {
- case R.id.takepicture:
- // 拍照
- camera.takePicture(null, null, new MyPictureCallback());
- break;
- }
- }
- }
- /**
- * 圖片被點擊觸發的時間
- *
- * @param v
- */
- public void imageClick(View v) {
- if (v.getId() == R.id.scalePic) {
- if (bundle == null) {
- Toast.makeText(getApplicationContext(), R.string.takephoto,
- Toast.LENGTH_SHORT).show();
- } else {
- Intent intent = new Intent(this, ShowPicActivity.class);
- intent.putExtras(bundle);
- startActivity(intent);
- }
- }
- }
- private final class MyPictureCallback implements PictureCallback {
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- try {
- bundle = new Bundle();
- bundle.putByteArray("bytes", data); //將圖片字節數據保存在bundle當中,實現數據交換
- saveToSDCard(data); // 保存圖片到sd卡中
- Toast.makeText(getApplicationContext(), R.string.success,
- Toast.LENGTH_SHORT).show();
- camera.startPreview(); // 拍完照後,重新開始預覽
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 將拍下來的照片存放在SD卡中
- * @param data
- * @throws IOException
- */
- public static void saveToSDCard(byte[] data) throws IOException {
- Date date = new Date();
- SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化時間
- String filename = format.format(date) + ".jpg";
- File fileFolder = new File(Environment.getExternalStorageDirectory()
- + "/finger/");
- if (!fileFolder.exists()) { // 如果目錄不存在,則創建一個名爲"finger"的目錄
- fileFolder.mkdir();
- }
- File jpgFile = new File(fileFolder, filename);
- FileOutputStream outputStream = new FileOutputStream(jpgFile); // 文件輸出流
- outputStream.write(data); // 寫入sd卡中
- outputStream.close(); // 關閉輸出流
- }
- private final class SurfaceCallback implements Callback {
- // 拍照狀態變化時調用該方法
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
- parameters = camera.getParameters(); // 獲取各項參數
- parameters.setPictureFormat(PixelFormat.JPEG); // 設置圖片格式
- parameters.setPreviewSize(width, height); // 設置預覽大小
- parameters.setPreviewFrameRate(5); //設置每秒顯示4幀
- parameters.setPictureSize(width, height); // 設置保存的圖片尺寸
- parameters.setJpegQuality(80); // 設置照片質量
- }
- // 開始拍照時調用該方法
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- try {
- camera = Camera.open(); // 打開攝像頭
- camera.setPreviewDisplay(holder); // 設置用於顯示拍照影像的SurfaceHolder對象
- camera.setDisplayOrientation(getPreviewDegree(MainActivity.this));
- camera.startPreview(); // 開始預覽
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- // 停止拍照時調用該方法
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- if (camera != null) {
- camera.release(); // 釋放照相機
- camera = null;
- }
- }
- }
- /**
- * 點擊手機屏幕是,顯示兩個按鈕
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- layout.setVisibility(ViewGroup.VISIBLE); // 設置視圖可見
- break;
- }
- return true;
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CAMERA: // 按下拍照按鈕
- if (camera != null && event.getRepeatCount() == 0) {
- // 拍照
- //注:調用takePicture()方法進行拍照是傳入了一個PictureCallback對象——當程序獲取了拍照所得的圖片數據之後
- //,PictureCallback對象將會被回調,該對象可以負責對相片進行保存或傳入網絡
- camera.takePicture(null, null, new MyPictureCallback());
- }
- }
- return super.onKeyDown(keyCode, event);
- }
- // 提供一個靜態方法,用於根據手機方向獲得相機預覽畫面旋轉的角度
- public static int getPreviewDegree(Activity activity) {
- // 獲得手機的方向
- int rotation = activity.getWindowManager().getDefaultDisplay()
- .getRotation();
- int degree = 0;
- // 根據手機的方向計算相機預覽畫面應該選擇的角度
- switch (rotation) {
- case Surface.ROTATION_0:
- degree = 90;
- break;
- case Surface.ROTATION_90:
- degree = 0;
- break;
- case Surface.ROTATION_180:
- degree = 270;
- break;
- case Surface.ROTATION_270:
- degree = 180;
- break;
- }
- return degree;
- }
- }
用來顯示圖片的Activity:
- package com.wwj.finger;
- import android.app.Activity;
- import android.content.Intent;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Matrix;
- import android.os.Bundle;
- import android.widget.ImageView;
- public class ShowPicActivity extends Activity {
- private ImageView ivPic = null; // 顯示圖片控件
- /**
- * Activity在創建的時候回調的函數 主要用來初始化一些變量
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.showpic);
- ivPic = (ImageView) findViewById(R.id.ivPic);
- setImageBitmap(getImageFormBundle());
- }
- /**
- * 將MainActivity傳過來的圖片顯示在界面當中
- *
- * @param bytes
- */
- public void setImageBitmap(byte[] bytes) {
- Bitmap cameraBitmap = byte2Bitmap();
- // 根據拍攝的方向旋轉圖像(縱向拍攝時要需要將圖像選擇90度)
- Matrix matrix = new Matrix();
- matrix.setRotate(MainActivity.getPreviewDegree(this));
- cameraBitmap = Bitmap
- .createBitmap(cameraBitmap, 0, 0, cameraBitmap.getWidth(),
- cameraBitmap.getHeight(), matrix, true);
- ivPic.setImageBitmap(cameraBitmap);
- }
- /**
- * 從Bundle對象中獲取數據
- *
- * @return
- */
- public byte[] getImageFormBundle() {
- Intent intent = getIntent();
- Bundle data = intent.getExtras();
- byte[] bytes = data.getByteArray("bytes");
- return bytes;
- }
- /**
- * 將字節數組的圖形數據轉換爲Bitmap
- *
- * @return
- */
- private Bitmap byte2Bitmap() {
- byte[] data = getImageFormBundle();
- // 將byte數組轉換成Bitmap對象
- Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
- return bitmap;
- }
- }
這部分已經完美實現簡單的照相機功能了,保存圖片不會像有些網友提供的代碼給定一個特定的文件名,不能保存多張圖片,還特定把一些方法封裝了一下,有需要的朋友好好看看吧。