Android камера предварительного просмотра выглядит странно
Я реализую приложение камеры, и когда я смотрю на предварительный просмотр (особенно с фронтальной камерой), изображение очень толстое. Похоже, что изображение растягивается по горизонтали. Я следую примеру SDK с оптимизированным размером камеры, но это не помогает. Как я могу отрегулировать настройку камеры, чтобы она была в режиме предварительного просмотра, как в другом приложении камеры?
Благодарю.
Мой код ниже.
public class CameraActivity extends Activity implements SurfaceHolder.Callback, Camera.ShutterCallback, Camera.PictureCallback {
Camera m_camera;
SurfaceView m_surfaceView;
int m_numOfCamera;
int m_defaultCameraId;
int m_currentCamera;
int m_surfaceWidth;
int m_surfaceHeight;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
getActionBar().setDisplayHomeAsUpEnabled(true);
m_surfaceView = (SurfaceView)findViewById(R.id.cameraPreview);
m_surfaceView.getHolder().addCallback(this);
m_camera = Camera.open();
m_numOfCamera = Camera.getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < m_numOfCamera; ++i) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
m_defaultCameraId = i;
m_currentCamera = m_defaultCameraId;
}
}
if (m_numOfCamera < 1) {
MenuItem switchCam = (MenuItem)findViewById(R.id.menu_switch_camera);
switchCam.setVisible(false);
}
}
@Override
public void onPause() {
super.onPause();
m_camera.stopPreview();
}
@Override
public void onDestroy() {
super.onDestroy();
m_camera.release();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_camera, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(final MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
}
else if (item.getItemId() == R.id.menu_switch_camera)
{
if (m_camera != null) {
m_camera.stopPreview();
m_camera.release();
m_camera = null;
}
m_camera = Camera.open((m_currentCamera + 1) % m_numOfCamera);
m_currentCamera = (m_currentCamera + 1) % m_numOfCamera;
Camera.Parameters params = m_camera.getParameters();
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
Camera.Size size = getOptimalPreviewSize(sizes, m_surfaceWidth, m_surfaceHeight);
params.setPreviewSize(size.width, size.height);
m_camera.setParameters(params);
setCameraDisplayOrientation(this, m_currentCamera, m_camera);
m_camera.startPreview();
try {
m_camera.setPreviewDisplay(m_surfaceView.getHolder());
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
return true;
}
public void onPictureTaken(byte[] arg0, Camera arg1) {
// TODO Auto-generated method stub
}
public void onShutter() {
// TODO Auto-generated method stub
}
public void surfaceChanged(SurfaceHolder arg0, int format, int w, int h) {
m_surfaceWidth = w;
m_surfaceHeight = h;
Camera.Parameters params = m_camera.getParameters();
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
Camera.Size selected = getOptimalPreviewSize(sizes, w, h);
params.setPreviewSize(selected.width, selected.height);
m_camera.setParameters(params);
setCameraDisplayOrientation(this, m_currentCamera, m_camera);
m_camera.startPreview();
}
private static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.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
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
public void surfaceCreated(SurfaceHolder arg0) {
try {
m_camera.setPreviewDisplay(m_surfaceView.getHolder());
} catch (Exception e) {
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null)
return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
4 ответа
Предварительный просмотр камеры всегда заполняет SurfaceView
показывая это. Если соотношение сторон m_surfaceView
не соответствует соотношению сторон камеры, предварительный просмотр будет растянут.
Вам нужно будет создать m_surfaceView
соответствие пропорций. Это означает, что вам нужно создать его из кода, а не из файла XML макета.
Существует пример проекта APIDemos, который вы найдете в примерах проектов Android. В проекте есть вещь, названнаяCameraPreview
, У этого есть хорошая демонстрация для настройки предварительного просмотра камеры в SurfaceView
, У него есть класс, который extends ViewGroup
и добавляет SurfaceView
как его ребенок из кода. onMeasure()
Метод был переопределен для определения высоты и ширины SurfaceView
, поэтому соотношение сторон сохраняется. Посмотрите на проект, и я надеюсь, что это будет ясно.
[Извините, я не смог опубликовать ссылку здесь - это должна быть ссылка, но я обнаружил, что она не работает. Но если вы установили образцы проектов с помощью Android SDK, вы можете найти проект в примерах. Откройте новый пример проекта Android, выберите APIDemos, а затем найдите класс с именем CameraPreview
, Должно быть в упаковке com.example.android.apis.graphics
, насколько я помню.]
Я изменил метод onLayout и не предварительный просмотр камеры не растягивается. В остальном все то же самое, что и APiDemo, который находится здесь sdk/sample/adroid-18. Идея в том, что у нас есть только некоторый поддерживаемый размер предварительного просмотра, но наш размер представления не всегда совпадает с размером предварительного просмотра. поэтому я взял больший размер предварительного просмотра, чем мой размер изображения. меня устраивает. Может помочь кому-то..
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight < height * previewWidth) {
final int scaledChildWidth = previewWidth * height
/ previewHeight;
left = (width - scaledChildWidth) / 2;
top = 0;
right = (width + scaledChildWidth) / 2;
bottom = height;
child.layout(left, top, right, bottom);
} else {
final int scaledChildHeight = previewHeight * width
/ previewWidth;
left = 0;
top = (height - scaledChildHeight) / 2;
right = width;
bottom = (height + scaledChildHeight) / 2;
child.layout(left, top, right, bottom);
}
}
}
Я уже решил проблему. Что может вызвать проблемы со странным предварительным просмотром камеры.
Строка состояния занимает пробелы - вы можете скрыть ее Скрытие строки состояния
Некоторые пробелы также принимают TitleBar - вы можете отключить это в манифесте
Android: тема ="@ андроид: стиль /Theme.NoTitleBar">
Изменена ориентация деятельности на альбомную ориентацию "потому что предварительный просмотр камеры поддерживает эту ориентацию" - вы можете проверить это в API demo Graphics-> CameraPreview
алгоритм, который сравнивает размер Display.getWidth () Camera.getParameters size (). getSupportedPreviewSizes (); если они одинаковы - это функция изменения SurfaceChanged Parametrs.setPreviewSize (x, y), которую вы получили при поиске в списке
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters setPrevOrientation = mCamera.getParameters();
if (mHolder.getSurface() == null)
return;
try {
mCamera.stopPreview();
Size sizeBefore = mCamera.getParameters().getPreviewSize();
setPrevOrientation.setRotation(setCameraDisplayOrientation(
(Activity) context, getCameraId(), mCamera));
// Orientacja Portrait np 640x480 Landscape 480x640
this.resolution.setPreviewSize(this.optimalPreviewSize.x,
this.optimalPreviewSize.y);
mCamera.setParameters(this.resolution);
Size sizeAfter = mCamera.getParameters().getPreviewSize();
} catch (RuntimeException e) {
L.d("Podgląd nie istnieje");
}
try {
mCamera.stopPreview();
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
L.d("błąd podgladu: " + e.getMessage());
}
}
private Point getOptimalPreviewResolution(Display displaySize) {
lSuportedPreviewSize = mCamera.getParameters()
.getSupportedPreviewSizes();
Point optimalPreviewSize = new Point();
int displayWidth = displaySize.getWidth();
int displayHeight = displaySize.getHeight();
int cameraHeight;
int cameraWidth;
List<Point> lOptimalPoint = new ArrayList<Point>();
for (int i = 0; i < lSuportedPreviewSize.size(); i++) {
cameraHeight = lSuportedPreviewSize.get(i).width;
cameraWidth = lSuportedPreviewSize.get(i).height;
if (displayHeight >= cameraHeight) {
lOptimalPoint.add(new Point(cameraHeight, cameraWidth));
}
}
// Sort ascending
Collections.sort(lOptimalPoint,
new ComapreSupportedPreviewByWidth());
// Last element is optimal
optimalPreviewSize = lOptimalPoint.get(lOptimalPoint.size() - 1);
// Return resolution - camera at landscape mode (800x600)
return optimalPreviewSize;
}
class ComapreSupportedPreviewByWidth implements Comparator<Point> {
@Override
public int compare(Point lhs, Point rhs) {
return lhs.x - rhs.x;
}
У меня проблема с слишком растягивающим предварительным просмотром камеры. Это слишком растягивается в вертикальном и ландшафтном режиме.
Таким образом, в манифесте я добавил screenOrentation="Portrait", но это не помогло, предварительный просмотр по-прежнему масштабируется в любом положении (вертикальный - предварительный просмотр слишком широкий или пейзаж слишком длинный), вы можете увидеть это на экранах. Хотелось бы добавить у Samsung ace III все нормально но у LG Nexus 4 растягивается
package pl.probs.camera.component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import pl.probs.lib.debug.L;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private static boolean showLogs = true;
private SurfaceHolder mHolder;
private Camera mCamera;
private Context context;
private Parameters resolution;
private List<Size> lSuportedPreviewSize;
private static int cOrientation = 0; // aktualny kat orientacji
private static boolean cOrientationChanged = false; // Stan orientacji
// zostal zmieniony
// wzgledem poprzedniego
private Display display; // Rozmiar ekranu
private Point displaySize; // Zmienna przechowuje Rozmiar Ekranu
private Point optimalPreviewSize;
public CameraPreview(Context context, Camera camera, int resolution) {
super(context);
this.optimalPreviewSize = new Point();
this.context = context;
this.mCamera = camera;
setDisplaySize(this.display);
setFocusable(true);
setFocusableInTouchMode(true);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
this.resolution = getMinResolution(resolution);
this.optimalPreviewSize = getOptimalPreviewResolution(this.display);
Size s = mCamera.getParameters().getPreviewSize(); // Sprawdzenie jaki
// prewiev ustawiony
}
public Point getOptimalPreviewSize() {
return optimalPreviewSize;
}
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewDisplay(holder);
Size s = mCamera.getParameters().getPreviewSize();
mCamera.startPreview();
}
} catch (IOException e) {
L.d("Błąd ustawiania podglÄ…du: " + e.getMessage());
}
}
protected void onPause() {
// Because the Camera object is a shared resource, it's very
// important to release it when the activity is paused.
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters setPrevOrientation = mCamera.getParameters();
if (mHolder.getSurface() == null)
return;
try {
mCamera.stopPreview();
Size sizeBefore = mCamera.getParameters().getPreviewSize();
setPrevOrientation.setRotation(setCameraDisplayOrientation((Activity) context, getCameraId(), mCamera));
// Orientacja Portrait np 640x480 Landscape 480x640
this.resolution.setPreviewSize(this.optimalPreviewSize.x, this.optimalPreviewSize.y);
mCamera.setParameters(this.resolution);
Size sizeAfter = mCamera.getParameters().getPreviewSize();
} catch (RuntimeException e) {
L.d("Podgląd nie istnieje");
}
try {
mCamera.stopPreview();
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
L.d("błąd podgladu: " + e.getMessage());
}
}
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mCamera.autoFocus(new AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
// do something
}
});
}
return true;
}
private static int setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.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;
}
cOrientation = degrees;
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
return result;
}
private int getCameraId() {
int cameraId = -1;
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
cameraId = i;
break;
}
}
return cameraId;
}
private Parameters getMinResolution(int desireResolutionInMpx) {
int height[], width[], size;
float megapixels;
Camera.Parameters p = mCamera.getParameters();
size = p.getSupportedPictureSizes().size();
height = new int[size];
width = new int[size];
for (int i = 0; i < size; i++) {
height[i] = p.getSupportedPictureSizes().get(i).height;
width[i] = p.getSupportedPictureSizes().get(i).width;
megapixels = (float) (((float) height[i] * (float) width[i]) / 1024000);
if (megapixels <= desireResolutionInMpx) {
p.setPictureSize(width[i], height[i]);
break;
}
}
return p;
}
private Point getOptimalPreviewResolution(Display displaySize) {
lSuportedPreviewSize = mCamera.getParameters().getSupportedPreviewSizes();
Point optimalPreviewSize = new Point();
int displayWidth = displaySize.getWidth(); // szerokosc ekranu
int displayHeight = displaySize.getHeight(); // wysokosc ekranu
int cameraHeight; // wspierana wysokosc kamery
int cameraWidth; // wspierana szerokosc kamery
// Lista przechowywujace SupportedPreviewSize kamery, wszyskie
// rozdzielczosci mniejsze od szerokosc i wysokosci ekranu
List<Point> lOptimalPoint = new ArrayList<Point>();
// Pomocniczo do listowania zawartosci listy
// TODO manta displayHeight cameraHeight brak oraz width brak zgodnosci
// (
System.out.println(lOptimalPoint.toString());
for (int i = 0; i < lSuportedPreviewSize.size(); i++) {
Log.i(TAG, "w " + lSuportedPreviewSize.get(i).width + " h " + lSuportedPreviewSize.get(i).height + " \n");
}
// Wyszukanie wszystkich wysokosci kamery mniejszej od wysokosci ekranu
for (int i = 0; i < lSuportedPreviewSize.size(); i++) {
// TODO Uwazaj kamera zapisuje swoj rozmiar dla pozycji landscape
// gdzie height = 480 a width = 800
cameraHeight = lSuportedPreviewSize.get(i).width;
cameraWidth = lSuportedPreviewSize.get(i).height;
// Porownaj wysokosc ekranu urzadzenia z wysokosci supportedPreview
// dodaj do listy
if (displayHeight > cameraHeight) {
lOptimalPoint.add(new Point(cameraHeight, cameraWidth));
}
}
// Sortowanie rosnaco
Collections.sort(lOptimalPoint, new ComapreSupportedPreviewByWidth());
// Ostatni element listy optymalny
optimalPreviewSize = lOptimalPoint.get(lOptimalPoint.size()-1);
// Zwracana rozdzielczosc landscape aparatu np (800x600)
return optimalPreviewSize;
}
private void setDisplaySize(Display display) {
Activity activity = (Activity) this.context; // Pobierz aktywnosc aby
// znać rozmiar ekranu
this.display = activity.getWindowManager().getDefaultDisplay();
}
class ComapreSupportedPreviewByWidth implements Comparator<Point> {
@Override
public int compare(Point lhs, Point rhs) {
return lhs.x - rhs.x;
}
}
}