简体   繁体   中英

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). 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. 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

    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):

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. Have a nice day ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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