Android自定義照相機實現(拍照、儲存到SD卡,利用Bundle在Acitivity交換資料)
阿新 • • 發佈:2019-01-29
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;
}
}
這是小巫那個創新專案的一小部分,已經完美實現簡單的照相機功能了,儲存圖片不會像有些網友提供的程式碼給定一個特定的檔名,不能儲存多張圖片,還特定把一些方法封裝了一下,有需要的朋友好好看看吧。