Android: RelativeLayout margin works but gets overridden

前提是你 提交于 2019-12-25 01:46:49

问题


Problem

Using RelativeLayout.LayoutParams and marginTop before setLayoutParams( params ).
Works on all devices for half a second but some cases it bumps back to top. The view stays centered in the holding view on my Moto X 2014 running Lollipop but not on my Nexus 4 running Lollipop.


Opening activity

  1. Opens activity
  2. The margin is fine and the SurfaceView is centered
  3. ~200ms delay
  4. The margin resets and its back to top (top of SurfaceView at the top of holder)

Closing activity

  1. Back pressed
  2. ~200ms delay
  3. The margin sets in, putting my view to the right position
  4. Activity closes

Code (Edited)

RelativeLayout holder = ( RelativeLayout ) findViewById( R.id.holder );
RelativeLayout.LayoutParams params = ( RelativeLayout.LayoutParams ) holder.getLayoutParams();
CustomCamera view = ( CustomCamera ) findViewById( R.id.surface ); // Extends SurfaceView
params.topMargin = margin;
view.setLayoutParams( params );

Example

I need the margin to work like this every time on every device. On some devices the red (SurfaceView) is aligned with top of screen ignoring the margin and gravity.


回答1:


To make your life easier, here is my simple camera implementation which can help you out. Note that this implementation relays on the old android.hardware.Camera API. Starting API level 21 there is a new way of working with the camera.

Anyway here is your basic xml file for your activity:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

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

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

    <Button
        android:id="@+id/take_picture_button"
        android:layout_width="wrap_content"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="16dp"
        android:text="Take picture" />
</RelativeLayout>

Your camera activity:

package com.your.package;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.Size;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;

import java.io.IOException;

/**
 * @author Mike Reman
 *
 */
public class MainActivity extends Activity {

    // Constants
    private static final float PREVIEW_SIZE_FACTOR = 1.3f;
    private static final String STATE_SELECTED_CHECKBOX = "CameraType";

    private Camera mCamera;
    private SurfaceHolder mSurfaceHolder = null;

    // Data
//private boolean mHasTwoCameras = (Camera.getNumberOfCameras() > 1);
    private boolean mIsInPreview;
    private boolean mIsUsingFFC = false;
    private boolean mIsLandscape;
    protected AutoFocusCallback autoFocusCallback = new AutoFocusCallback() {

        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            try {
                camera.takePicture(shutterCallback, null, jpegCallback);
            } catch (RuntimeException e) {
                e.printStackTrace();
            }
        }
    };

    private ShutterCallback shutterCallback = new ShutterCallback() {

        @Override
        public void onShutter() {
            AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
            mgr.playSoundEffect(AudioManager.FLAG_PLAY_SOUND);
        }
    };

    private PictureCallback jpegCallback = new PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            //Tadaaa, you got your picture taken
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        mIsLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;

        if (savedInstanceState != null) {
            mIsUsingFFC = savedInstanceState.getBoolean(STATE_SELECTED_CHECKBOX, false);
        }
        setContentView(R.layout.activity_main);
        initializeCameraAndViews();
    }


    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putBoolean(STATE_SELECTED_CHECKBOX, mIsUsingFFC);
    }

    /**
     * Initialize the views used by the activity, including the SurfaceView
     * displaying the 'camera'. There is a SurfaceHolder object which is
     * initialized by the SurfaceView's SurfaceHolder
     */
    private void initializeCameraAndViews() {
        FrameLayout frame = (FrameLayout) findViewById(R.id.root_container);
        SurfaceView surfaceView = (SurfaceView) frame.findViewById(R.id.camera_surfaceview);

        if (mSurfaceHolder == null) {
            mSurfaceHolder = surfaceView.getHolder();
        }

        mSurfaceHolder.addCallback(surfaceHolderCallback(mIsUsingFFC));
        findViewById(R.id.take_picture_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mCamera.autoFocus(autoFocusCallback);
                } catch (RuntimeException e) {
                    try {
                        mCamera.takePicture(shutterCallback, null, jpegCallback);
                    } catch (RuntimeException ex) {
                        // Failed to take the picture
                        ex.printStackTrace();
                    }
                }
            }
        });
    }


    private SurfaceHolder.Callback surfaceHolderCallback(final boolean isUsingFFC) {
        return new SurfaceHolder.Callback() {
            private AsyncTask<Void, Void, Void> initCameraAsyncTask;
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (isUsingFFC) {
                    try {
                        mCamera = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
                        CameraUtils.setCameraDisplayOrientation(MainActivity.this, CameraInfo.CAMERA_FACING_FRONT, mCamera);
                    } catch (RuntimeException e) {
                        // Open camera failed
                    }
                } else {
                    try {
                        mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
                        CameraUtils.setCameraDisplayOrientation(MainActivity.this, CameraInfo.CAMERA_FACING_BACK, mCamera);
                    } catch (RuntimeException e) {
                        // Open camera failed
                    }
                }

                try {
                    if (mCamera != null) {
                        mCamera.setPreviewDisplay(holder);
                    } else {
                        // Most probably the device has no camera...yeah it is possible :)
                    }
                } catch (IOException exception) {
                    mCamera.release();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, final int width, final int height) {
                initCameraAsyncTask = new AsyncTask<Void, Void, Void>() {

                    @Override
                    protected Void doInBackground(Void... params) {
                        if (mCamera != null) {
                            try {
                                Camera.Parameters parameters = mCamera.getParameters();
                                Size size = getOptimalSize(mCamera);
                                parameters.setPreviewSize(size.width, size.height);
                                mCamera.setParameters(parameters);
                            } catch (RuntimeException e) {
                                e.printStackTrace();
                            }

                            mCamera.startPreview();
                        }
                        return null;
                    }

                    @Override
                    protected void onPostExecute(Void result) {
                        super.onPostExecute(result);
                        mIsInPreview = true;

                        // Set the initial FlashMode to OFF
                        if (mCamera != null) {
                            Camera.Parameters parameters = mCamera.getParameters();
                            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                            mCamera.setParameters(parameters);
                        }
                    }
                };
                initCameraAsyncTask.execute();
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if (mCamera != null) {
                    mCamera.stopPreview();
                    mIsInPreview = false;
                    initCameraAsyncTask.cancel(true);
                    mCamera.release();
                }
            }
        };
    }


    /**
     * Open the camera by creating a new instance of the Camera object depending
     * on the given parameter. Use this method if you want to switch between Front facing camera(FFC) and back facing cameras
     * <p>
     * If the parameter value is true, a new Camera object will be created using
     * the Front Facing Camera. Otherwise the newly created Camera object will
     * use the Back Facing Camera
     * </p>
     *
     * @param isWithFFC
     *            - the parameter to be the deciding factor on which camera is
     *            used
     */
    private void openCamera(boolean isWithFFC) {
        if (mIsInPreview) {
            mCamera.stopPreview();
            mIsInPreview = false;
        }
        mCamera.release();

        int currentCameraId;
        if (isWithFFC) {
            currentCameraId = CameraInfo.CAMERA_FACING_FRONT;
        } else {
            currentCameraId = CameraInfo.CAMERA_FACING_BACK;
        }
        mCamera = Camera.open(currentCameraId);

        CameraUtils.setCameraDisplayOrientation(MainActivity.this, currentCameraId, mCamera);

        try {
            mCamera.setPreviewDisplay(mSurfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Camera.Parameters parameters = mCamera.getParameters();
                    Size size = getOptimalSize(mCamera);
                    parameters.setPreviewSize(size.width, size.height);
                    mCamera.setParameters(parameters);
                } catch (RuntimeException e) {
                    e.printStackTrace();
                }
                mCamera.startPreview();
                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                super.onPostExecute(result);
                mIsInPreview = true;
            }

        }.execute();
    }

    /**
     * Gets an optimal size for the created Camera.
     *
     * @param camera
     *            - the built Camera object
     * @return the optimal Size for the Camera object
     */
    private Size getOptimalSize(Camera camera) {
        Size result = null;
        final Camera.Parameters parameters = camera.getParameters();

        int width = ScreenUtils.getScreenWidth(this);
        int height = ScreenUtils.getScreenHeight(this);

        for (final Size size : parameters.getSupportedPreviewSizes()) {
            if (size.width <= width * PREVIEW_SIZE_FACTOR && size.height <= height * PREVIEW_SIZE_FACTOR) {

                if (mIsLandscape) {
                    size.width = width;
                    size.height = height;
                } else {
                    size.height = width; // switching the values because the default camera is basically always in landscape mode and our camera isn't.
                    size.width = height;
                }
                if (result == null) {
                    result = size;
                } else {
                    final int resultArea = result.width * result.height;
                    final int newArea = size.width * size.height;

                    if (newArea > resultArea) {
                        result = size;
                    }
                }
            }
        }
        if (result == null) {
            result = parameters.getSupportedPreviewSizes().get(0);
        }
        return result;
    }
}

Your needed utils classes. You can add these to your own CameraActivity if you won't use them elsewhere:

CameraUtils:

public final class CameraUtils {

            /**
             * Sets the orientation for the Camera object as the default orientation is
             * in landscape mode.
             * @param activity - the Activity where the orientation is applied to the Camera object
             * @param cameraId - the id of the used Camera(using the Front Facing Camera or the Back Facing Camera)
             * @param camera - the Camera object on which the orientation changes will be applied
             */
            public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
                Camera.CameraInfo info = new Camera.CameraInfo();
                Camera.getCameraInfo(cameraId, info);
                int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();

                int degrees = 0;
                switch (rotation) {
                    case Surface.ROTATION_0:
                        degrees = 0;
                        break;
                    case Surface.ROTATION_90:
                        degrees = 90;
                        break;
                    case Surface.ROTATION_180:
                        degrees = 180;
                        break;
                    case Surface.ROTATION_270:
                        degrees = 270;
                        break;
                }

                int result;
                if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    result = (info.orientation + degrees) % 360;
                    result = (360 - result) % 360; // compensate the mirror(basically turn the image upside down)
                } else { // back-facing
                    result = (info.orientation - degrees + 360) % 360;
                }
                camera.setDisplayOrientation(result);
            }
}

ScreenUtils:

public final class ScreenUtils {
            /**
             * Calculates the screen's width and returns this value.
             * @param activity - the activity where the method is called from
             * @return - the screen's width
             */
            public static int getScreenWidth(Activity activity) {
                Display display = activity.getWindowManager().getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);
                return size.x;
            }

            /**
             * Calculates the screen's height and returns this value.
             * @param activity - the activity where the method is called from
             * @return - the screen's height
             */
            public static int getScreenHeight(Activity activity) {
                Display display = activity.getWindowManager().getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);
                return size.y;
            }
} 

And last but not least, don't forget about you permissions in the manifest file:

<uses-permission android:name="android.permission.CAMERA" />

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

I hope this answer helps you to get your camera on the go!

Cheers, Mike



来源:https://stackoverflow.com/questions/28304276/android-relativelayout-margin-works-but-gets-overridden

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!