Предварительный просмотр камеры для Android
Я работаю над созданием пользовательской активности камеры на Android, но при повороте камеры соотношение сторон изображения поверхности становится испорченным.
В моем oncreate для активности я устанавливаю frameelayout, который содержит поверхностное представление, отображающее параметры камеры.
//FrameLayout that will hold the camera preview
FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview);
//Setting camera preview size to the best preview size
Size optimalSize = null;
camera = getCameraInstance();
double aspectRatio = 0;
if(camera != null){
//Setting the camera aspect ratio
Camera.Parameters parameters = camera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
aspectRatio = (float)optimalSize.width/optimalSize.height;
}
if(optimalSize!= null){
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
previewHolder.setLayoutParams(params);
LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
cameraPreview.setLayoutParams(surfaceParams);
}
cameraPreview.setCamera(camera);
//Adding the preview to the holder
previewHolder.addView(cameraPreview);
Затем в представлении "Поверхность" я устанавливаю параметры камеры, которые будут отображаться
public void setCamera(Camera camera) {
if (mCamera == camera) { return; }
mCamera = camera;
if (mCamera != null) {
requestLayout();
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
if(mCamera != null){
//Setting the camera aspect ratio
Camera.Parameters parameters = mCamera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(parameters);
}
/*
Important: Call startPreview() to start updating the preview surface. Preview must
be started before you can take a picture.
*/
mCamera.startPreview();
}
}
![enter image description here]()
Вы можете видеть, что человек LEGO становится выше и шире, когда телефон повернут:
![]()
Как я могу убедиться, что соотношение сторон для просмотра моей камеры верное?
Ответы
Ответ 1
Я использую этот метод → на основе API Demos, чтобы получить свой размер предварительного просмотра:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio=(double)h / w;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.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);
}
}
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;
}
Как вы можете видеть, вам нужно вводить ширину и высоту экрана. Этот метод рассчитает коэффициент отображения на основе этих значений, а затем из списка поддерживаемых представлений PreviewSizes выберет для вас лучшие из доступных. Получите ваш поддерживаемый список PreviewSize, где объект Camera не равен нулю, используя
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
И затем в onMeasure вы можете получить свой оптимальный предварительный просмотр так:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
И затем (в моем коде в методе surfaceChanged, как я уже сказал, я использую структуру API Demos кода CameraActivity, вы можете сгенерировать его в Eclipse):
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();
И один намек для вас, потому что я сделал почти то же приложение, что и вы. Хорошей практикой для деятельности камеры является скрытие StatusBar. Такие приложения, как Instagram, делают это. Это уменьшает значение высоты экрана и изменяет значение отношения. На некоторых устройствах можно получить странные размеры предварительного просмотра (ваш SurfaceView будет немного сокращен)
И чтобы ответить на ваш вопрос, как проверить правильность вашего коэффициента просмотра? Затем введите высоту и ширину параметров, которые вы установили:
mCamera.setParameters(parameters);
ваше отношение комплектации равно высоте/ширине. Если вы хотите, чтобы камера выглядела хорошо на экране, соотношение высоты и ширины параметров, которые вы установили для камеры, должно быть таким же, как и отношение высоты экрана (минус состояние)/ширины экрана.
Ответ 2
Решение F1Sher приятно, но иногда не работает. В частности, когда ваш поверхностный экран не охватывает весь экран. В этом случае вам нужно переопределить метод onMeasure().
Я скопировал свой код здесь для справки.
Так как я измерил surfaceView по ширине, тогда у меня немного белый пробел в конце моего экрана, который я заполнил его по дизайну. Вы можете исправить эту проблему, если сохранить высоту и увеличить ширину, умножив ее на коэффициент. Тем не менее, он слегка начнет слегка шевелиться.
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public CameraPreview(Context context, Camera camera) {
super(context);
mContext = context;
mCamera = camera;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
for(Camera.Size str: mSupportedPreviewSizes)
Log.e(TAG, str.width + "/" + str.height);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// empty. surfaceChanged will take care of stuff
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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 {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or reformatting changes here
// start preview with new settings
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
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);
}
if (mPreviewSize!=null) {
float ratio;
if(mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
// One of these methods should be used, second method squishes preview slightly
setMeasuredDimension(width, (int) (width * ratio));
// setMeasuredDimension((int) (width * ratio), height);
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
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;
}
}
Ответ 3
ПРИМЕЧАНИЕ. МОЕ РЕШЕНИЕ - ПРОДОЛЖЕНИЕ РЕШЕНИЯ ХЕСАМ: fooobar.com/questions/55154/...
Что я адресую: Хесэм сказал, что на некоторых телефонах есть небольшое пробел, например:
![NOTE: Although the aspect ratio is correct, the camera does not fill in the whole screen.]()
Хэсам предложил второе решение, но это преувеличивает предварительный просмотр. И на некоторых устройствах он сильно искажается.
Итак, как мы исправим эту проблему. Это просто... путем умножения пропорций до заполнения на экране. Я заметил, что несколько популярных приложений, таких как Snapchat, WhatsApp и т.д., Работают одинаково.
Все, что вам нужно сделать, это добавить это к методу onMeasure:
float camHeight = (int) (width * ratio);
float newCamHeight;
float newHeightRatio;
if (camHeight < height) {
newHeightRatio = (float) height / (float) mPreviewSize.height;
newCamHeight = (newHeightRatio * camHeight);
Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight);
setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);
Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight);
} else {
newCamHeight = camHeight;
setMeasuredDimension(width, (int) newCamHeight);
Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight);
}
Это вычислит высоту экрана и получит соотношение высоты экрана и высоты mPreviewSize. Затем он умножает ширину и высоту камеры на новое отношение высоты и соответственно устанавливает измеренный размер.
![enter image description here]()
И следующее, что вы знаете, в итоге получится: D
![enter image description here]()
Это также хорошо работает с фронтальной камерой. Я считаю, что это лучший способ сделать это. Теперь для моего приложения осталось только сохранить предварительный просмотр при нажатии "Capture". Но я, вот оно.
Ответ 4
ОК, поэтому я думаю, что нет достаточного ответа для общей проблемы растяжения предварительного просмотра камеры. Или, по крайней мере, я его не нашел. Мое приложение также страдало этим синдромом растяжения, и мне потребовалось некоторое время, чтобы разгадать решение всех ответов пользователей на этом портале и в Интернете.
Я попробовал решение @Hesam , но это не сработало и сильно исказило предварительный просмотр камеры.
Сначала я покажу код моего решения (важные части кода), а затем объясню, почему я сделал эти шаги. Существует возможность для изменения производительности.
Основной вид работы xml:
<RelativeLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/camera_preview"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
Предварительный просмотр камеры
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder prHolder;
private Camera prCamera;
public List<Camera.Size> prSupportedPreviewSizes;
private Camera.Size prPreviewSize;
@SuppressWarnings("deprecation")
public YoCameraPreview(Context context, Camera camera) {
super(context);
prCamera = camera;
prSupportedPreviewSizes = prCamera.getParameters().getSupportedPreviewSizes();
prHolder = getHolder();
prHolder.addCallback(this);
prHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
prCamera.setPreviewDisplay(holder);
prCamera.startPreview();
} catch (IOException e) {
Log.d("Yologram", "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (prHolder.getSurface() == null){
return;
}
try {
prCamera.stopPreview();
} catch (Exception e){
}
try {
Camera.Parameters parameters = prCamera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
parameters.setPreviewSize(prPreviewSize.width, prPreviewSize.height);
prCamera.setParameters(parameters);
prCamera.setPreviewDisplay(prHolder);
prCamera.startPreview();
} catch (Exception e){
Log.d("Yologram", "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);
setMeasuredDimension(width, height);
if (prSupportedPreviewSizes != null) {
prPreviewSize =
getOptimalPreviewSize(prSupportedPreviewSizes, width, height);
}
}
public Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.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);
}
}
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;
}
}
Основная деятельность:
public class MainActivity extends Activity {
...
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
maCamera = getCameraInstance();
maLayoutPreview = (FrameLayout) findViewById(R.id.camera_preview);
maPreview = new CameraPreview(this, maCamera);
Point displayDim = getDisplayWH();
Point layoutPreviewDim = calcCamPrevDimensions(displayDim,
maPreview.getOptimalPreviewSize(maPreview.prSupportedPreviewSizes,
displayDim.x, displayDim.y));
if (layoutPreviewDim != null) {
RelativeLayout.LayoutParams layoutPreviewParams =
(RelativeLayout.LayoutParams) maLayoutPreview.getLayoutParams();
layoutPreviewParams.width = layoutPreviewDim.x;
layoutPreviewParams.height = layoutPreviewDim.y;
layoutPreviewParams.addRule(RelativeLayout.CENTER_IN_PARENT);
maLayoutPreview.setLayoutParams(layoutPreviewParams);
}
maLayoutPreview.addView(maPreview);
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private Point getDisplayWH() {
Display display = this.getWindowManager().getDefaultDisplay();
Point displayWH = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(displayWH);
return displayWH;
}
displayWH.set(display.getWidth(), display.getHeight());
return displayWH;
}
private Point calcCamPrevDimensions(Point disDim, Camera.Size camDim) {
Point displayDim = disDim;
Camera.Size cameraDim = camDim;
double widthRatio = (double) displayDim.x / cameraDim.width;
double heightRatio = (double) displayDim.y / cameraDim.height;
// use ">" to zoom preview full screen
if (widthRatio < heightRatio) {
Point calcDimensions = new Point();
calcDimensions.x = displayDim.x;
calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width;
return calcDimensions;
}
// use "<" to zoom preview full screen
if (widthRatio > heightRatio) {
Point calcDimensions = new Point();
calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height;
calcDimensions.y = displayDim.y;
return calcDimensions;
}
return null;
}
}
Мой комментарий:
Дело в том, что, хотя вы вычисляете оптимальный размер камеры в getOptimalPreviewSize()
, вы выбираете только ближайший коэффициент для соответствия вашему экрану. Поэтому, если соотношение точно того же, просмотр будет растянут.
Почему это растянется? Поскольку ваш предварительный просмотр камеры FrameLayout установлен в layout.xml на match_parent по ширине и высоте. Поэтому предварительный просмотр растягивается до полного экрана.
Что нужно сделать, это установить ширину и высоту предварительного просмотра камеры в соответствии с выбранным соотношением размеров камеры, поэтому предварительный просмотр сохраняет соотношение сторон и не будет искажают.
Я попытался использовать класс CameraPreview
для выполнения всех вычислений и изменений макета, но я не мог понять это. Я попытался применить это решение, но SurfaceView
не распознает getChildCount ()
или getChildAt (int index)
. Я думаю, что в конечном итоге я работал с ссылкой на maLayoutPreview
, но это было неправильным и применило коэффициент отношения ко всему моему приложению, и он сделал это после того, как была сделана первая фотография. Поэтому я отпустил и переместил модификации макета в MainActivity
.
В CameraPreview
я изменил prSupportedPreviewSizes
и getOptimalPreviewSize()
на общедоступный, поэтому я могу использовать его в MainActivity
. Затем мне понадобились размеры дисплея (за вычетом панели навигации/состояния, если таковая имеется), и выберите оптимальный размер камеры. Я попытался получить размер RelativeLayout (или FrameLayout) вместо размера дисплея, но он возвращал нулевое значение. Это решение не помогло мне. Макет получил значение после onWindowFocusChanged
(отмечено в журнале).
Итак, у меня есть методы вычисления размеров макета в соответствии с соотношением сторон выбранного размера камеры. Теперь вам нужно установить LayoutParams
в макет предварительного просмотра камеры. Измените ширину, высоту и центрируйте ее в родительском.
Существует два варианта расчета размеров предварительного просмотра. Либо вы хотите, чтобы он соответствовал экрану с черными полосами (если windowBackground установлен в null) по бокам или сверху/снизу. Или вы хотите, чтобы предварительный просмотр увеличен до полноэкранного. Я оставил комментарий с дополнительной информацией в calcCamPrevDimensions()
.
Ответ 5
Привет, getOptimalPreview(), который здесь не работал, поэтому я хочу поделиться своей версией:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
if (sizes==null) return null;
Camera.Size optimalSize = null;
double ratio = (double)h/w;
double minDiff = Double.MAX_VALUE;
double newDiff;
for (Camera.Size size : sizes) {
newDiff = Math.abs((double)size.width/size.height - ratio);
if (newDiff < minDiff) {
optimalSize = size;
minDiff = newDiff;
}
}
return optimalSize;
}
Ответ 6
Чтобы сделать этот поток более полным, я добавляю свою версию ответа:
Чего я хотел достичь:
Поверхностный вид не должен растягиваться, и он должен покрывать весь экран. Более того, в моем приложении был только ландшафтный режим.
Решение:
Решение представляет собой чрезвычайно небольшое расширение для решения F1sher:
= > Первый шаг - интегрировать решение F1sher.
= > Теперь может возникнуть сценарий в решении F1sher, когда поверхностный вид не охватывает весь экран. Решение состоит в том, чтобы сделать вид поверхности больше размеров экрана, чтобы он покрывал весь экран, для этого
size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
double screenRatio = (double) screenHeight / screenWidth;
double previewRatio = (double) size.height / size.width;
if (previewRatio > screenRatio) /*if preview ratio is greater than screen ratio then we will have to recalculate the surface height while keeping the surface width equal to the screen width*/
{
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
params1.addRule(RelativeLayout.CENTER_IN_PARENT);
flPreview.setLayoutParams(params1);
flPreview.setClipChildren(false);
LayoutParams surfaceParams = new LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
surfaceParams.gravity = Gravity.CENTER;
mPreview.setLayoutParams(surfaceParams);
}
else /*if preview ratio is smaller than screen ratio then we will have to recalculate the surface width while keeping the surface height equal to the screen height*/
{
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
params1.addRule(RelativeLayout.CENTER_IN_PARENT);
flPreview.setLayoutParams(params1);
flPreview.setClipChildren(false);
LayoutParams surfaceParams = new LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
surfaceParams.gravity = Gravity.CENTER;
mPreview.setLayoutParams(surfaceParams);
}
flPreview.addView(mPreview);
/* The TopMost layout used is the RelativeLayout, flPreview is the FrameLayout in which Surface View is added, mPreview is an instance of a class which extends SurfaceView */
Ответ 7
Вы должны установить для параметра cameraView.getLayoutParams() height и cameraView.getLayoutParams() ширину в соответствии с желаемым соотношением сторон.
Ответ 8
Я выяснил, в чем проблема - с ориентацией ориентации. Если вы измените ориентацию камеры на 90 или 270 градусов, вам нужно подменять ширину и высоту поддерживаемых размеров, и все будет в порядке.
Также вид поверхности должен лежать в макете рамки и иметь центр тяжести.
Вот пример на С# (Xamarin):
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
_camera.StopPreview();
// find best supported preview size
var parameters = _camera.GetParameters();
var supportedSizes = parameters.SupportedPreviewSizes;
var bestPreviewSize = supportedSizes
.Select(x => new { Width = x.Height, Height = x.Width, Original = x }) // HACK swap height and width because of changed orientation to 90 degrees
.OrderBy(x => Math.Pow(Math.Abs(x.Width - width), 3) + Math.Pow(Math.Abs(x.Height - height), 2))
.First();
if (height == bestPreviewSize.Height && width == bestPreviewSize.Width)
{
// start preview if best supported preview size equals current surface view size
parameters.SetPreviewSize(bestPreviewSize.Original.Width, bestPreviewSize.Original.Height);
_camera.SetParameters(parameters);
_camera.StartPreview();
}
else
{
// if not than change surface view size to best supported (SurfaceChanged will be called once again)
var layoutParameters = _surfaceView.LayoutParameters;
layoutParameters.Width = bestPreviewSize.Width;
layoutParameters.Height = bestPreviewSize.Height;
_surfaceView.LayoutParameters = layoutParameters;
}
}
Обратите внимание, что параметры камеры должны быть установлены как исходный размер (не заменен), а размер поверхности должен быть заменен.
Ответ 9
Я попробовал все решение выше, но ни один из них не работает для меня. наконец, я решил это сам, и найти на самом деле это довольно легко. есть два момента, которые вам нужно быть осторожными.
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
этот предварительный просмотр должен быть одним из поддерживаемых камерой разрешением, которое можно получить, как показано ниже:
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
обычно один из rawSupportedSize равен разрешению устройства.
Во-вторых, поместите SurfaceView в FrameLayout и установите высоту и ширину поверхности в методе surfaceChanged, как указано выше.
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
layoutParams.height = cameraResolution.x;
layoutParams.width = cameraResolution.y;
Хорошо, все сделано, надеюсь, что это может вам помочь.
Ответ 10
Мои требования: предварительный просмотр камеры должен быть полноэкранным и поддерживать соотношение сторон.
Решение Hesam и Yoosuf было замечательным, но по какой-то причине я вижу проблему с высоким увеличением.
Идея такая же, иметь центр предварительного просмотра в родительском и увеличивать ширину или высоту в зависимости от соотношения сторон, пока он не сможет покрыть весь экран.
Следует отметить, что размер предварительного просмотра в ландшафте, поскольку мы устанавливаем ориентацию дисплея.
camera.setDisplayOrientation(90);
Контейнер, который мы добавим в представление SurfaceView:
<RelativeLayout
android:id="@+id/camera_preview_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"/>
Добавьте в него предварительный просмотр контейнера с центром в родительском объекте.
this.cameraPreview = new CameraPreview(this, camera);
cameraPreviewContainer.removeAllViews();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
cameraPreviewContainer.addView(cameraPreview, 0, params);
Внутри класса CameraPreview:
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// 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 (holder.getSurface() == null) {
// preview surface does not exist
return;
}
stopPreview();
// set preview size and make any resize, rotate or
// reformatting changes here
try {
Camera.Size nativePictureSize = CameraUtils.getNativeCameraPictureSize(camera);
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
parameters.setPictureSize(nativePictureSize.width, nativePictureSize.height);
camera.setParameters(parameters);
camera.setDisplayOrientation(90);
camera.setPreviewDisplay(holder);
camera.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 (supportedPreviewSizes != null && optimalSize == null) {
optimalSize = CameraUtils.getOptimalSize(supportedPreviewSizes, width, height);
Log.i(TAG, "optimal size: " + optimalSize.width + "w, " + optimalSize.height + "h");
}
float previewRatio = (float) optimalSize.height / (float) optimalSize.width;
// previewRatio is height/width because camera preview size are in landscape.
float measuredSizeRatio = (float) width / (float) height;
if (previewRatio >= measuredSizeRatio) {
measuredHeight = height;
measuredWidth = (int) ((float)height * previewRatio);
} else {
measuredWidth = width;
measuredHeight = (int) ((float)width / previewRatio);
}
Log.i(TAG, "Preview size: " + width + "w, " + height + "h");
Log.i(TAG, "Preview size calculated: " + measuredWidth + "w, " + measuredHeight + "h");
setMeasuredDimension(measuredWidth, measuredHeight);
}
Ответ 11
Я отказался от вычислений и просто получил размер представления, где я хочу, чтобы предварительный просмотр камеры отображался, и установите размер предварительного просмотра камеры одинаково (просто перевернутая ширина/высота из-за вращения) в моей пользовательской реализации SurfaceView:
@Override // CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Display display = ((WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
final Camera.Parameters params = camera.getParameters();
// viewParams is from the view where the preview is displayed
params.setPreviewSize(viewParams.height, viewParams.width);
camera.setDisplayOrientation(90);
requestLayout();
camera.setParameters(params);
}
// I do not enable rotation, so this can otherwise stay as is
}