简体   繁体   English

如何在我的MainActivity中包含CameraPreview类?

[英]How to include the CameraPreview class within my MainActivity?

I'm trying to build a custom Camera in Android without using the Camera API (Using intents). 我正在尝试在不使用Camera API(使用意图)的情况下在Android中构建自定义Camera。 I have a MainActivity.java, I need to call the CameraPreview class in order to see the camera preview, but I don't know how to call that class. 我有一个MainActivity.java,我需要调用CameraPreview类以查看摄像机预览,但是我不知道如何调用该类。 I tried 我试过了

public CameraPreview cameraPreview;

and later 然后

mPreview = new CameraPreview(this, mCamera);

    if(hasCamera) {
        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
}

It doesn't work. 没用 Please advice! 请指教! My code compiles but I don't see the preview on screen. 我的代码可以编译,但是在屏幕上看不到预览。

    public class MainActivity extends AppCompatActivity implements SensorEventListener{


    public CameraPreview cameraPreview;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        hasCamera = checkCameraHardware(this);

        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
            // Success! There's a accelerometer.
            mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            Toast.makeText(MainActivity.this, "Accelerometer Found!", Toast.LENGTH_SHORT).show();
        }
        else {
            // Failure! No accelerometer.
            Toast.makeText(MainActivity.this, "Accelerometer Not Found!", Toast.LENGTH_SHORT).show();
        }

        if(hasCamera) {
            // Create an instance of Camera
            mCamera = getCameraInstance();

            // Create our Preview view and set it as the content of our activity.
            mPreview = new CameraPreview(this, mCamera);
            FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
            preview.addView(mPreview);


           /* Button captureButton = (Button) findViewById(R.id.button_capture);
            captureButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            // get an image from the camera
                            mCamera.takePicture(null, null, mPicture);
                        }
                    }
            ); */

        }

        //Accelerometer Handling
        mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_NORMAL);


        if(safeToTakePicture) {

           // cameraPreview.surfaceCreated(mHolder);
            mCamera.takePicture(null, null, mPicture);
            safeToTakePicture = false;
        }



    }

         @Override
         public void onSensorChanged(SensorEvent event) {
        long currentTime = System.currentTimeMillis();
        // update every 1 sec
        if((currentTime - prevTime) > 10000){

            mDiff = currentTime - prevTime;

            mX = event.values[0];
            mY = event.values[1];
            mZ = event.values[2];

            float speed = Math.abs(mX + mY + mZ - mPrev_x - mPrev_y - mPrev_z) / mDiff;

            if (speed > SHAKE_THRESHOLD) {
                Log.d("sensor", "Shake detected with speed: " + speed);
                mCamera.takePicture(null, null, mPicture);
                safeToTakePicture = false;
                Toast.makeText(this, "Shake detected with speed: " + speed, Toast.LENGTH_SHORT).show();
            }


        }
            mPrev_x = mX;
            mPrev_y = mY;
            mPrev_z = mZ;
            prevTime = currentTime;

        //        mCamera.takePicture(null, null, mPicture);
        //        Toast.makeText(MainActivity.this, "SensorChanged Event! x:" + event.values[0], Toast.LENGTH_SHORT).show();
    }
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
            // this device has a camera
            return true;
        } else {
            // no camera on this device
            return false;
        }
    }
    /** A safe way to get an instance of the Camera object. */
    //Deprecated Camera instance, but ok
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }


    // receive data in a JPEG format,
    // you must implement an Camera.PictureCallback interface to receive the image data and write it to a file
    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {

            File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
            if (pictureFile == null){
                Log.d(TAG, "Error creating media file, check storage permissions: " /* +
                        e.getMessage()*/);
                return;
            }

            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();
            } catch (FileNotFoundException e) {
                Log.d(TAG, "File not found: " + e.getMessage());
            } catch (IOException e) {
                Log.d(TAG, "Error accessing file: " + e.getMessage());
            }
        }
    };
    private static File getOutputMediaFile(int type){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "MyCameraApp");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("MyCameraApp", "failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } else {
            return null;
        }

        return mediaFile;
    }



    public final void onAccuracyChanged(Sensor sensor, int accuracy){
        //mCamera.takePicture(null, null, mPicture);
        //Toast.makeText(this, "Taking a picture now", Toast.LENGTH_SHORT).show();
    }


    @Override
    protected void onPause() {
        super.onPause();
        letGo();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        letGo();
    }


    private void letGo(){
        if(mCamera != null){
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);
            mCamera.release();
            mCamera = null;
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        if (mCamera != null) {
            // Call stopPreview() to stop updating the preview surface.
            mCamera.stopPreview();
        }
    }

    /**
     * When this function returns, mCamera will be null.
     */
    private void stopPreviewAndFreeCamera() {

        if (mCamera != null) {
            // Call stopPreview() to stop updating the preview surface.
            mCamera.stopPreview();

            // Important: Call release() to release the camera for use by other
            // applications. Applications should release the camera immediately
            // during onPause() and re-open() it during onResume()).
            mCamera.release();

            mCamera = null;
        }
    }
}

CameraPreview CameraPreview

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private static final String TAG = MainActivity.class.getSimpleName();
    SurfaceView mSurfaceView;
    private Camera mCamera;
    private boolean safeToTakePicture = false;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;
        mSurfaceView = new SurfaceView(context);


        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.


        if (mHolder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
            safeToTakePicture = true;

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

Ok guys, sharing my stable variant, Here is improved Custom camera preview class (make attention! preview size set to 800x600 and image size around 5mpx for better performance, this can be removed, look in code): 好的,大家分享我的稳定版本,这是改进的自定义相机预览类(请注意!预览尺寸设置为800x600,图像尺寸约为5mpx,以获得更好的性能,可以将其删除,在代码中查看):

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private Context mContext;
    private Camera.PreviewCallback previewCallback;
    private Camera.AutoFocusCallback autoFocusCallback;

    private static final String TAG = "CameraPreview";
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public CameraPreview(Context context, Camera camera,
                         Camera.PreviewCallback previewCb,
                         Camera.AutoFocusCallback autoFocusCb) {
        super(context);
        mContext = context;
        mCamera = camera;
        previewCallback = previewCb;
        autoFocusCallback = autoFocusCb;
// supported preview sizes
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
//        for(Camera.Size str: mSupportedPreviewSizes)
//            Log.e(TAG, str.width + "/" + str.height);
//        for(Camera.Size str: mCamera.getParameters().getSupportedPictureSizes())
//            Log.e(TAG, str.width + "/" + str.height);





    // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
//        // deprecated setting, but required on Android versions prior to 3.0
//        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        /*
         * Set camera to continuous focus if supported, otherwise use
         * software auto-focus. Only works for API level >=9.
         */
        /*
        Camera.Parameters parameters = camera.getParameters();
        for (String f : parameters.getSupportedFocusModes()) {
            if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
                mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                autoFocusCallback = null;
                break;
            }
        }
        */

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            Log.d("DBG", "Error setting camera preview: " + e.getMessage());
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
 /*
         * If your preview can change or rotate, take care of those events here.
         * Make sure to stop the preview before resizing or reformatting it.
         */
        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        try {
            // Hard code camera surface rotation 90 degs to match Activity view in portrait
//            Camera.Parameters parameters = mCamera.getParameters();
//            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
//            mCamera.setParameters(parameters);
            Camera.Parameters params = mCamera.getParameters();
            for(Camera.Size str: params.getSupportedPictureSizes()) {
                if (str.width >= 2400 && str.width <= 2600 && str.height >= 1800 && str.height <= 2000) {
                    params.setPictureSize(str.width, str.height);
                    Log.e("Picture size: ", str.width + "/" + str.height);
                }

            }
            for(Camera.Size str: params.getSupportedPreviewSizes())
                if(str.width == 800 && str.height == 600){
                    params.setPreviewSize(str.width, str.height);
                    Log.e("Preview size: ", str.width + "/" + str.height);
                }
            mCamera.setParameters(params);
            mCamera.setDisplayOrientation(90);

            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(previewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(autoFocusCallback);
            if(!(SettingsService.settings.autofocusOn))
                mCamera.cancelAutoFocus();
        } catch (Exception e) {
            Log.d("DBG", "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        this.getHolder().removeCallback(this);
        try {
            mCamera.stopPreview();
            mCamera.release();
        }catch(Exception e){

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }

        float ratio;
        if(mPreviewSize.height >= mPreviewSize.width)
            ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
        else
            ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

        // One of these methods should be used, second method squishes preview slightly
        setMeasuredDimension(width, (int) (width * ratio));
//        setMeasuredDimension((int) (width * ratio), height);
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.height / size.width;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;

            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }

        return optimalSize;
    }
}

And here is usage in activity or fragment: 这是活动或片段中的用法:

    public class ScanFragment extends Fragment {

        private View mFragmentView;
        private static Camera mCamera;
        private CameraPreview mPreview;
        private Handler autoFocusHandler;
        private Context mContext;
        public boolean previewing = true;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    mContext = (MainActivity)getActivity();
    // Inflate the layout for this fragment

    mFragmentView = inflater.inflate(R.layout.fragment_scan, container, false);
}


public void cameraPreviewStop(){
        previewing = false;
        if(mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
        }
    }
    public void cameraPreviewStart(){
        try {
            if (mCamera != null) {
                mCamera.setPreviewCallback(previewCb);
                mCamera.startPreview();
                previewing = true;
                mCamera.autoFocus(autoFocusCB);
            }else{
                initControls();
                cameraPreviewStart();
            }
        }catch (Exception e){
            releaseCamera();
            initControls();
            cameraPreviewStart();
        }

    }

    public void initControls() {
        getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        autoFocusHandler = new Handler();
        try{
            mCamera = getCameraInstance();
        }catch(Exception e){

        }

        // Instance barcode scanner
        initPreviewCamera();
    }

    public void initPreviewCamera(){
        autoFocusCB = new Camera.AutoFocusCallback() {
            public void onAutoFocus(boolean success, Camera camera) {
                autoFocusHandler.postDelayed(doAutoFocus, 1000);
            }
        };
        mPreview = new CameraPreview(mContext, mCamera, previewCb,
                autoFocusCB);
        FrameLayout preview = (FrameLayout) mFragmentView.findViewById(R.id.cameraPreview);
        preview.removeAllViews();
        preview.addView(mPreview);
    }


    /**
     * A safe way to get an instance of the Camera object.
     */
    public static Camera getCameraInstance() {
        Camera c = null;
        try {
            c = Camera.open();
        } catch (Exception e) {
        }
        return c;
    }

    public void releaseCamera() {
        if (mCamera != null) {
            previewing = false;
            try {
                mCamera.setPreviewCallback(null);
                mCamera.release();
            }catch (Exception e){

            }
            mCamera = null;
        }
    }

    private Runnable doAutoFocus = new Runnable() {
        public void run() {
            if (previewing)
                mCamera.autoFocus(autoFocusCB);
        }
    };

    Camera.PreviewCallback previewCb = new Camera.PreviewCallback() {
        public void onPreviewFrame(byte[] data, Camera camera) {
            Camera.Parameters parameters = camera.getParameters();
            Camera.Size size = parameters.getPreviewSize();

            Image imageFromCamera = new Image(size.width, size.height, "Y800");
            // Do something...

        }
    };

    // Mimic continuous auto-focusing
    Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
        public void onAutoFocus(boolean success, Camera camera) {
            autoFocusHandler.postDelayed(doAutoFocus, 1000);
        }
    };
}

If in activity then override some things: 如果处于活动状态,则覆盖一些内容:

@Override
protected void onResume() {
    super.onResume();
    if(previewing) {
        initControls();
        cameraPreviewStart();
    }
}

@Override
protected void onStop() {
    super.onStop();
    releaseCamera();
}

And the fragment view with camera fragment (you can add here your custom views and anything you want to customize, size of preview, absolutely anything you want): 以及带有相机片段的片段视图(您可以在此处添加您的自定义视图以及您要自定义的任何内容,预览的大小,绝对要的任何内容):

<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"
    xmlns:local="http://schemas.android.com/apk/res-auto">


            <FrameLayout
                android:id="@+id/cameraPreview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

</FrameLayout>

And final note, this is very important! 最后一点,这很重要! Especially for Android api 23+ devices, from android 6.0 you must use only one instance of camera if you got null on camera object then you are trying to open camera that is already opened. 特别是对于Android api 23+设备,从android 6.0开始,如果在camera对象上为null,则必须仅使用一个camera实例,然后尝试打开已打开的camera。 Have a nice day ;) 祝你今天愉快 ;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM