Android自定義相機,切換前後攝像頭,照相機拍照

Android自定義相機,簡單實現切換前後攝像頭,照相機拍照 

Ctrl +C  Ctrl+V 可以直接 run 起來,註釋比較詳細;源碼下載


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tomorrow_p.camera_p">

    <!-- 調用攝像頭權限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <!-- 掛載sd卡 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- sd卡讀寫權限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.tomorrow_p.camera_p.PreviewImageActivity"></activity>
    </application>

</manifest>


android6.0及以上需要 動態申請相機權限:

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
        }

package com.tomorrow_p.camera_p;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends Activity {
    private static final String TAG = "ansen";
    private View mRelativeLayout;
    private Camera mCamera;
    private Camera.Parameters mParameters = null;
    private int cameraPosition = 1;//0代表前置攝像頭,1代表後置攝像頭
    private SurfaceHolder mHolder;

    Bundle bundle = null; // 聲明一個Bundle對象,用來存儲數據

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        requestWindowFeature(Window.FEATURE_NO_TITLE);//沒有標題
//        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//設置全屏
//        this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照過程屏幕一直處於高亮

        setContentView(R.layout.activity_main);

        mRelativeLayout = findViewById(R.id.relativeLayout);
        SurfaceView surfaceView = (SurfaceView) 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的句柄添加一個回調函數
    }

    public void takePhoto(View v) {
        if (mCamera != null) {
            // 獲取到拍照的圖片數據後回調PictureCallback,PictureCallback可以對相片進行保存或傳入網絡
            mCamera.takePicture(null, null, new MyPictureCallback());
        }
    }

    public void change(View v) {
        //切換前後攝像頭
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();//得到攝像頭的個數

        for (int i = 0; i < cameraCount; i++) {
            Camera.getCameraInfo(i, cameraInfo);//得到每一個攝像頭的信息
            if (cameraPosition == 1) {
                //現在是後置,變更爲前置
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表攝像頭的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK後置
                    mCamera.stopPreview();//停掉原來攝像頭的預覽
                    mCamera.release();//釋放資源
                    mCamera = null;//取消原來攝像頭
                    mCamera = Camera.open(i);//打開當前選中的攝像頭
                    try {
                        mCamera.setPreviewDisplay(mHolder);//通過surfaceview顯示取景畫面
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mCamera.startPreview();//開始預覽
                    cameraPosition = 0;
                    break;
                }
            } else {
                //現在是前置, 變更爲後置
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表攝像頭的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK後置
                    mCamera.stopPreview();//停掉原來攝像頭的預覽
                    mCamera.release();//釋放資源
                    mCamera = null;//取消原來攝像頭
                    mCamera = Camera.open(i);//打開當前選中的攝像頭
                    try {
                        mCamera.setPreviewDisplay(mHolder);//通過surfaceview顯示取景畫面
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    mCamera.startPreview();//開始預覽
                    cameraPosition = 1;
                    break;
                }
            }

        }
    }

    /**
     * 圖片被點擊觸發的時間
     *
     * @param v
     */
    public void imageClick(View v) {
        if (bundle == null) {
            Toast.makeText(getApplicationContext(), "沒有數據", Toast.LENGTH_SHORT).show();
        } else {
            Intent intent = new Intent(this, PreviewImageActivity.class);
            intent.putExtras(bundle);
            startActivity(intent);
        }
    }

    private final class MyPictureCallback implements Camera.PictureCallback {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                bundle = new Bundle();
                bundle.putByteArray("bytes", data); //將圖片字節數據保存在bundle當中,實現數據交換
                saveToSDCard(data); // 保存圖片到sd卡中
                Toast.makeText(getApplicationContext(), "success", Toast.LENGTH_SHORT).show();
//                camera.startPreview(); // 拍完照後,重新開始預覽

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void saveToSDCard(byte[] data) throws IOException {
        Log.d(TAG, "saveToSDCard");
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化時間
        String filename = format.format(date) + ".jpg";
        File fileFolder = new File(Environment.getExternalStorageDirectory() + "/ansen/");// Environment.getRootDirectory()
        if (!fileFolder.exists()) {
            fileFolder.mkdir();
        }
        File jpgFile = new File(fileFolder, filename);
        FileOutputStream outputStream = new FileOutputStream(jpgFile); // 文件輸出流
        outputStream.write(data);
        outputStream.close();
        mCamera.startPreview(); // 拍完照後,重新開始預覽
        if (false) {
            Bitmap b = byteToBitmap(data);
            // 獲取手機屏幕的寬高
            WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
            int windowWidth = windowManager.getDefaultDisplay().getWidth();
            int windowHight = windowManager.getDefaultDisplay().getHeight();
            Bitmap bitmap = Bitmap.createBitmap(b, 0, 0, windowWidth, windowHight);
            // 圖片壓縮
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            outputStream.flush();

        }
    }

    /**
     * 把圖片byte流轉換成bitmap
     */
    private Bitmap byteToBitmap(byte[] data) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length, options);
        int i = 0;
        while (true) {
            if ((options.outWidth >> i <= 1000)
                    && (options.outHeight >> i <= 1000)) {
                options.inSampleSize = (int) Math.pow(2.0D, i);
                options.inJustDecodeBounds = false;
                b = BitmapFactory.decodeByteArray(data, 0, data.length, options);
                break;
            }
            i += 1;
        }
        return b;
    }

    /**
     * 顯示surfaceView 數據的接口
     */
    private class SurfaceCallback implements SurfaceHolder.Callback {

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                   int height) {
            Log.d(TAG, "surfaceChanged");
            mParameters = mCamera.getParameters(); // 獲取各項參數
            mParameters.setPictureFormat(PixelFormat.JPEG); // 設置圖片格式
            mParameters.setPreviewSize(width, height); // 設置預覽大小
            mParameters.setPreviewFrameRate(5);  //設置每秒顯示4幀
            mParameters.setPictureSize(width, height); // 設置保存的圖片尺寸
            mParameters.setJpegQuality(80); // 設置照片質量
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            Log.d(TAG, "surfaceCreated");
            mHolder = holder;// SurfaceHolder是系統提供用來設置surfaceView的對象
            try {
                mCamera = Camera.open(); // 打開攝像頭
                mCamera.setPreviewDisplay(holder); //通過surfaceview顯示取景畫面
                mCamera.setDisplayOrientation(getPreviewDegree(MainActivity.this));// 設置相機的方向
                mCamera.startPreview(); // 開始預覽
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "surfaceCreated >>  Exception: " + e.getMessage());
            }

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            Log.d(TAG, "surfaceDestroyed");
            if (mCamera != null) {
                mCamera.release(); // 釋放照相機
                mCamera = null;
            }
        }
    }

    /**
     * 點擊手機屏幕是,顯示兩個按鈕
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mRelativeLayout.setVisibility(ViewGroup.VISIBLE); // 設置視圖可見
                break;
        }
        return true;
    }


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_CAMERA:
                if (mCamera != null && event.getRepeatCount() == 0) {
                    // 獲取到拍照的圖片數據後回調PictureCallback,PictureCallback可以對相片進行保存或傳入網絡
                    mCamera.takePicture(null, null, new MyPictureCallback());
                }
        }
        return super.onKeyDown(keyCode, event);
    }

    // 用於根據手機方向獲得相機預覽畫面旋轉的角度
    public static int getPreviewDegree(Activity activity) {
        int degree = 0;
        // 獲得手機的方向
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        Log.d(TAG, "rotation : " + rotation);
        // 根據手機的方向計算相機預覽畫面應該選擇的角度
        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;
    }
}


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tomorrow_p.camera_p.MainActivity">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:id="@+id/relativeLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:visibility="gone">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="change"
            android:text="切換" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="takePhoto"
            android:text="拍照" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="imageClick"
            android:text="預覽" />

    </LinearLayout>
</FrameLayout>

package com.tomorrow_p.camera_p;

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 PreviewImageActivity extends Activity {
    private ImageView ivPic = null; // 顯示圖片控件

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.preview_image);
        ivPic = (ImageView) findViewById(R.id.imageView);
        setImageBitmap(getImageFormBundle());
    }

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

    public byte[] getImageFormBundle() {
        Intent intent = getIntent();
        Bundle data = intent.getExtras();
        byte[] bytes = data.getByteArray("bytes");
        return bytes;
    }

    /**
     * 將字節數組的圖形數據轉換爲Bitmap
     */
    private Bitmap byte2Bitmap() {
        byte[] data = getImageFormBundle();
        // 將byte數組轉換成Bitmap對象
        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        return bitmap;
    }
}




發佈了78 篇原創文章 · 獲贊 157 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章