1. 程式人生 > >Android自定義照相機實現(拍照、儲存到SD卡,利用Bundle在Acitivity交換資料)

Android自定義照相機實現(拍照、儲存到SD卡,利用Bundle在Acitivity交換資料)

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;
	}
}


這是小巫那個創新專案的一小部分,已經完美實現簡單的照相機功能了,儲存圖片不會像有些網友提供的程式碼給定一個特定的檔名,不能儲存多張圖片,還特定把一些方法封裝了一下,有需要的朋友好好看看吧。