Гаразд, тому я думаю, що недостатньо відповіді на загальну проблему розтягування попереднього перегляду камери. Або принаймні я його не знайшов. Мій додаток також страждав від цього синдрому розтягування, і мені знадобилося певний час, щоб загадати рішення з усіх відповідей користувачів на цьому порталі та в Інтернеті.
Я намагався @ 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, щоб він відповідав ширині та висоті. Тож тому попередній перегляд пошириться на весь екран.
Що потрібно зробити, це встановити ширину та висоту макета попереднього перегляду камери, щоб вони відповідали вибраному співвідношенню розміру камери , щоб попередній перегляд зберігав співвідношення сторін і не спотворював.
Я намагався використовувати CameraPreview
клас, щоб зробити всі обчислення та зміни верстки, але не зміг це зрозуміти. Я намагався застосувати це рішення , але SurfaceView
не визнає getChildCount ()
або getChildAt (int index)
. Я думаю, я змусив це працювати врешті з посиланням на maLayoutPreview
, але це було недоброзичливо і застосував встановлене співвідношення до всього мого додатка, і це було зроблено після того, як зроблено першу фотографію. Тож я відпустив це і перемістив модифікації верстки до MainActivity
.
В CameraPreview
я змінив prSupportedPreviewSizes
і getOptimalPreviewSize()
на публіку , так що я можу використовувати його в MainActivity
. Тоді мені знадобилися розміри дисплея (мінус навігаційна / смуга стану, якщо є) та вибрали оптимальний розмір камери . Я намагався отримати розмір RelativeLayout (або FrameLayout) замість розміру дисплея, але він повертав нульове значення. Це рішення не спрацювало для мене. Макет отримав це значення після onWindowFocusChanged
(перевірено в журналі).
Тому я маю свої методи розрахунку розмірів макета, щоб відповідати співвідношенню сторін обраного розміру камери. Тепер вам просто потрібно встановити LayoutParams
макет попереднього перегляду камери. Змініть ширину, висоту і відцентруйте її в батьківському.
Існує два варіанти обчислення розмірів попереднього перегляду. Ви хочете, щоб він розміщувався на екрані з чорними смугами (якщо встановлено значення "windowBackground") збоку або зверху / внизу. Або ви хочете, щоб попередній перегляд збільшено на весь екран . Я залишив коментар з додатковою інформацією в calcCamPrevDimensions()
.