camera preview within viewpager

2019-04-02 10:01发布

问题:

I have created a viewpager with a fragment page adapter and have two fragments in it. In one of my fragments I am displaying a camera live preview in a frame layout, but the problem is, that whenever I swipe from the first page to the camera preview page it is delayed. So I get black bars between the first fragment and the camera preview fragment when I’m swiping to the right, because the frame layout with the camera do not follow the swipe correctly. But this only happens when I’m displaying the camera live preview. If I were to remove the preview and just set the background color to green it wouldn’t be any delay. Camera Fragment:

public class CameraFragment extends Fragment {

    private Context mContext;
    private Camera mCamera;
    private CameraPreview mCameraPreview;
    private FrameLayout frameLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.camera_layout, container, false);
        mContext = getActivity();
        frameLayout = (FrameLayout) v.findViewById(R.id.camera_preview);
        new Thread() {
            public void run() {
                Looper.prepare();
                mCamera = getCameraInstance();
                mCameraPreview = new CameraPreview(mContext, mCamera, frameLayout);
                try {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            frameLayout.addView(mCameraPreview);
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
        return v;
    }

    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = getFrontFacingCamera();
        } catch (Exception e) {
            // cannot get camera or does not exist
        }
        return camera;
    }

    private Camera getFrontFacingCamera() {
        int cameraCount = 0;
        Camera cam = null;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();
        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                try {
                    cam = Camera.open(camIdx);
                } catch (RuntimeException e) {
                    Log.e("CameraFragment", "Camera failed to open: " + e.getLocalizedMessage());
                }
            }
        }
        return cam;
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        releaseCamera();
    }

    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
    }
}

Camera preview class:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private String TAG = CameraPreview.class.getSimpleName();
    private Context mContext;
    private FrameLayout frameLayout;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;
    boolean done = false;

    public CameraPreview(Context context, Camera camera, FrameLayout frameLayout) {
        super(context);
        mContext = context;
        mCamera = camera;
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for (Camera.Size str : mSupportedPreviewSizes)
            Log.e(TAG, str.width + "/" + str.height);
        this.frameLayout = frameLayout;
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mHolder.getSurface() == null) {
            return;
        }
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @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, true);
            if (mPreviewSize == null) {
                mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height, false);
            }
        }

        float ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
        if ((int) (width * ratio) >= height) {
            setMeasuredDimension(width, (int) (width * ratio));
        } else {
            setMeasuredDimension(width, height);
        }
        done = true;
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h, boolean minSize) {
        if (sizes == null) {
            return null;
        }
        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;
        double frameRatio = (double) h / w;
        double testRatio;
        int width, height;
        for (Camera.Size size : sizes) {
            width = size.height;
            height = size.width;
            if (width > height) {
                int t_width = width;
                width = height;
                height = t_width;
            }
            if (height < h / 2 && minSize) continue;
            testRatio = (double) height / width;
            if (Math.abs(frameRatio - testRatio) < minDiff) {
                if (optimalSize == null) optimalSize = sizes.get(0);
                // else if (frameRatio - testRatio <= 0 && optimalSize.height > height) continue;
                optimalSize.width = width;
                optimalSize.height = height;
                minDiff = Math.abs(frameRatio - testRatio);
                Log.d("", "" + frameRatio + ", " + testRatio);
            } else if (Math.abs(frameRatio - testRatio) == minDiff) {
                if (optimalSize.height > height) continue;
                if (optimalSize == null) optimalSize = sizes.get(0);
                optimalSize.width = width;
                optimalSize.height = height;
                minDiff = Math.abs(frameRatio - testRatio);
                Log.d("", "" + frameRatio + ", " + testRatio);
            }
        }
        Log.d("", "" + optimalSize.width + ", " + optimalSize.height);
        return optimalSize;
    }
}

It's kinda hard to explain but I hope you understand what is happening. Thanks!