2

I am trying to set the camera preview in my fragment and add some xml elements like buttons and signs over the preview. however while using addview the preview of the camera appears different based on devices ratio and camera. sometime it's on top and sometimes it's at bottom pushing the capture button out of the layout.

Note: I've been trying to set the framelayout containing the preview to alignparenttop or centercrop. however the after adding the preview usign addView programatically it keeps showing unaligned while pushing elements off the layout view in certain devices. meanwhile in some devices it displays the layout aligned to top and elements located over the framlayout.

enter image description here

here is my fragment code

public class KycCameraFragment extends Fragment {

public static final String TAG = "KycCameraFragment";

// Native camera.
private Camera mCamera;

// View to display the camera output.
private CameraPreview mPreview;

// Reference to the containing view.
private View mCameraView;

public KycCameraFragment() {
    // Required empty public constructor
}

@Override

public void onStart() {
    super.onStart();
    // register the event to listen.
    GlobalBus.getBus().register(this);
}



@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_kyc_camera, container, false);


    // Create our Preview view and set it as the content of our activity.
    boolean opened = safeCameraOpenInView(view);

    if(opened == false){
        Log.d("CameraGuide","Error, Camera failed to open");
        return view;
    }

    // Trap the capture button_round.
    Button captureButton = (Button) view.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);
                }
            }
    );

    return view;
}

@Subscribe(sticky = true)
public void getMessage(Events.FragmentIdTypeMessage fragmentIdTypeMessage) {
    TextView _tvEKYCCamera = (TextView) getView().findViewById(R.id.tvEKYCCamera);
    _tvEKYCCamera.setText("Message from Fragment ID type" + " " + fragmentIdTypeMessage.getMessage());
    Toast.makeText(getContext(),
            "Message Received: " + fragmentIdTypeMessage.getMessage(),
            Toast.LENGTH_SHORT).show();
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    // unregister the registered event.
    releaseCameraAndPreview();
    GlobalBus.getBus().unregister(this);
}

/**
 * Recommended "safe" way to open the camera.
 * @param view
 * @return
 */
private boolean safeCameraOpenInView(View view) {
    boolean qOpened = false;
    releaseCameraAndPreview();
    mCamera = getCameraInstance();
    mCameraView = view;
    qOpened = (mCamera != null);

    if(qOpened == true){
        mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera,view);
        FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview);
        preview.addView(mPreview);
        mPreview.startCameraPreview();
    }
    return qOpened;
}

/**
 * Safe method for getting a camera instance.
 * @return
 */
public static Camera getCameraInstance(){
    Camera c = null;
    try {
        c = Camera.open(); // attempt to get a Camera instance
    }
    catch (Exception e){
        e.printStackTrace();
    }
    return c; // returns null if camera is unavailable
}

/**
 * Clear any existing preview / camera.
 */
private void releaseCameraAndPreview() {

    if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
    if(mPreview != null){
        mPreview.destroyDrawingCache();
        mPreview.mCamera = null;
    }
}

/**
 * Surface on which the camera projects it's capture results. This is derived both from Google's docs and the
 * excellent StackOverflow answer provided below.
 *
 * Reference / Credit: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
 */
class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    // SurfaceHolder
    private SurfaceHolder mHolder;

    // Our Camera.
    private Camera mCamera;

    // Parent Context.
    private Context mContext;

    // Camera Sizing (For rotation, orientation changes)
    private Camera.Size mPreviewSize;

    // List of supported preview sizes
    private List<Camera.Size> mSupportedPreviewSizes;

    // Flash modes supported by this camera
    private List<String> mSupportedFlashModes;

    // View holding this camera.
    private View mCameraView;

    public CameraPreview(Context context, Camera camera, View cameraView) {
        super(context);

        // Capture the context
        mCameraView = cameraView;
        mContext = context;
        setCamera(camera);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setKeepScreenOn(true);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    /**
     * Begin the preview of the camera input.
     */
    public void startCameraPreview()
    {
        try{
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }


    /**
     * Extract supported preview and flash modes from the camera.
     * @param camera
     */
    private void setCamera(Camera camera)
    {
        // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
        mCamera = camera;
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();

        // Set the camera to Auto Flash mode.
        if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)){
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
            mCamera.setParameters(parameters);
        }

        requestLayout();
    }

    /**
     * The Surface has been created, now tell the camera where to draw the preview.
     * @param holder
     */
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mCamera.setPreviewDisplay(holder);
            } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Dispose of the camera preview.
     * @param holder
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null){
            mCamera.stopPreview();
        }
    }

    /**
     * React to surface changed events
     * @param holder
     * @param format
     * @param w
     * @param h
     */
    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 {
            Camera.Parameters parameters = mCamera.getParameters();

            // Set the auto-focus mode to "continuous"
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

            // Preview size must exist.
            if(mPreviewSize != null) {
                Camera.Size previewSize = mPreviewSize;
                parameters.setPreviewSize(previewSize.width, previewSize.height);
            }

            mCamera.setParameters(parameters);
            mCamera.startPreview();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * Calculate the measurements of the layout
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

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

    /**
     * Update the layout based on rotation and orientation changes.
     * @param changed
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom)
    {
        // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
        if (changed) {
            final int width = right - left;
            final int height = bottom - top;

            int previewWidth = width;
            int previewHeight = height;

            if (mPreviewSize != null){
                Display display = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

                switch (display.getRotation())
                {
                    case Surface.ROTATION_0:
                        previewWidth = mPreviewSize.height;
                        previewHeight = mPreviewSize.width;
                        mCamera.setDisplayOrientation(90);
                        Log.d(TAG,"0 degree camera orientation got hit");
                        break;
                    case Surface.ROTATION_90:
                        previewWidth = mPreviewSize.width;
                        previewHeight = mPreviewSize.height;
                        //added to fix rotation issue
                        Log.d(TAG,"90 degree camera orientation got hit");

                        break;
                    case Surface.ROTATION_180:
                        previewWidth = mPreviewSize.height;
                        previewHeight = mPreviewSize.width;
                        //added to fix rotation issue
                        mCamera.setDisplayOrientation(0);
                        Log.d(TAG,"180 degree camera orientation got hit");
                        break;
                    case Surface.ROTATION_270:
                        previewWidth = mPreviewSize.width;
                        previewHeight = mPreviewSize.height;
                        mCamera.setDisplayOrientation(180);
                        Log.d(TAG,"270 degree camera orientation got hit");
                        break;
                }
            }

            final int scaledChildHeight = previewHeight * width / previewWidth;
            mCameraView.layout(0, height - scaledChildHeight, width, height);
        }


    }

    /**
     *
     * @param sizes
     * @param width
     * @param height
     * @return
     */
    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height)
    {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) height / width;

        if (sizes == null)
            return null;

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

        int targetHeight = height;

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

/**
 * Picture Callback for handling a picture capture and saving it out to a file.
 */
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

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

        File pictureFile = getOutputMediaFile();
        if (pictureFile == null){
            Toast.makeText(getActivity(), "Image retrieval failed.", Toast.LENGTH_SHORT)
                    .show();
            return;
        }

        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();

            // Restart the camera preview.
            safeCameraOpenInView(mCameraView);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
};

/**
 * Used to return the camera File output.
 * @return
 */
private File getOutputMediaFile(){

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "MM");

    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("CameraGuide", "Required media storage does not exist");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator +
            "IMG_"+ timeStamp + ".jpg");

    Toast.makeText(getActivity(),"Your picture has been saved!", Toast.LENGTH_LONG).show();
 //        DialogHelper.showDialog( "Success!","Your picture has been saved!",getActivity());

    return mediaFile;
}
}

my fragment XML:

<RelativeLayout 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"
android:background="@color/black"
>

<!-- TODO: Update blank fragment layout -->
<TextView
    android:id="@+id/tvEKYCCamera"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="@string/hello_blank_fragment" />

<FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:background="@color/black"
    />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"
    android:orientation="vertical"
    android:gravity="center"
    >
    <View
        android:id="@+id/myRectangleView"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@drawable/rectangle"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Place ID Within Here"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:gravity="center">
    <Button
        android:id="@+id/button_capture"
        android:text=""
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_margin="10dp"
        android:background="@drawable/camera_button"
        />
</LinearLayout>

Poorya
  • 1,291
  • 6
  • 27
  • 57
  • it may be really tricky to a view to a RelativeLayout at runtime, and camera SurfaceView is especially tricky. Try to use FrameLayout as the parent. – Alex Cohn May 27 '17 at 17:05

0 Answers0