Oke, jadi saya pikir tidak ada jawaban yang memadai untuk masalah peregangan pratinjau kamera umum. Atau setidaknya saya tidak menemukannya. Aplikasi saya juga mengalami sindrom peregangan ini dan saya butuh beberapa saat untuk mencari solusi dari semua jawaban pengguna di portal dan internet ini.
Saya mencoba solusi @ Hesam tetapi tidak berhasil dan membuat pratinjau kamera saya sangat terdistorsi.
Pertama saya menunjukkan kode solusi saya (bagian penting dari kode) dan kemudian saya menjelaskan mengapa saya mengambil langkah-langkah itu. Ada ruang untuk modifikasi performa.
Tata letak xml aktivitas utama:
<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>
Pratinjau Kamera:
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;
}
}
Aktifitas utama:
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;
if (widthRatio < heightRatio) {
Point calcDimensions = new Point();
calcDimensions.x = displayDim.x;
calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width;
return calcDimensions;
}
if (widthRatio > heightRatio) {
Point calcDimensions = new Point();
calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height;
calcDimensions.y = displayDim.y;
return calcDimensions;
}
return null;
}
}
Komentar saya:
Inti dari semua ini adalah, bahwa meskipun Anda menghitung ukuran kamera yang optimal dalam getOptimalPreviewSize()
Anda hanya memilih rasio yang paling dekat untuk menyesuaikan layar Anda. Jadi, kecuali jika rasionya persis sama , pratinjau akan meregang.
Mengapa bisa meregang? Karena pratinjau kamera FrameLayout Anda disetel di layout.xml menjadi match_parent dalam lebar dan tinggi. Itulah sebabnya pratinjau akan meregang ke layar penuh.
Yang perlu dilakukan adalah menyetel lebar dan tinggi tata letak pratinjau kamera agar sesuai dengan rasio ukuran kamera yang dipilih , sehingga pratinjau mempertahankan rasio aspeknya dan tidak akan mendistorsi.
Saya mencoba menggunakan CameraPreview
kelas untuk melakukan semua kalkulasi dan perubahan tata letak, tetapi saya tidak bisa memahaminya. Saya mencoba menerapkan solusi ini , tetapi SurfaceView
tidak mengenali getChildCount ()
atau getChildAt (int index)
. Saya pikir, akhirnya saya membuatnya berfungsi dengan referensi ke maLayoutPreview
, tetapi itu berperilaku buruk dan menerapkan rasio yang ditetapkan ke seluruh aplikasi saya dan itu melakukannya setelah gambar pertama diambil. Jadi saya melepaskannya dan memindahkan modifikasi tata letak ke MainActivity
.
Di CameraPreview
saya berubah prSupportedPreviewSizes
dan getOptimalPreviewSize()
menjadi publik sehingga saya bisa menggunakannya di MainActivity
. Kemudian saya membutuhkan dimensi tampilan (tanpa bilah navigasi / status jika ada) dan memilih ukuran kamera yang optimal . Saya mencoba mendapatkan ukuran RelativeLayout (atau FrameLayout) alih-alih ukuran tampilan, tetapi mengembalikan nilai nol. Solusi ini tidak berhasil untuk saya. Tata letak mendapatkan nilainya setelahnya onWindowFocusChanged
(diperiksa di log).
Jadi saya memiliki metode untuk menghitung dimensi tata letak agar sesuai dengan rasio aspek ukuran kamera yang dipilih. Sekarang Anda hanya perlu mengatur LayoutParams
tata letak pratinjau kamera Anda. Ubah lebar, tinggi, dan pusatkan di induk.
Ada dua pilihan cara menghitung dimensi pratinjau. Entah Anda ingin menyesuaikan layar dengan bilah hitam (jika windowBackground disetel ke null) di samping atau atas / bawah. Atau Anda ingin pratinjau diperbesar ke layar penuh . Saya meninggalkan komentar dengan informasi lebih lanjut di calcCamPrevDimensions()
.