问题
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
- Opens activity
- The margin is fine and the SurfaceView is centered
- ~200ms delay
- The margin resets and its back to top (top of SurfaceView at the top of holder)
Closing activity
- Back pressed
- ~200ms delay
- The margin sets in, putting my view to the right position
- 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