Saya telah bekerja untuk membuat aktivitas kamera khusus saya di Android, tetapi ketika memutar kamera, rasio aspek tampilan permukaan menjadi kacau.
Di oncreate saya untuk aktivitas, saya mengatur framelayout yang menahan tampilan permukaan yang menampilkan parameter kamera.
//FrameLayout that will hold the camera preview
FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview);
//Setting camera's preview size to the best preview size
Size optimalSize = null;
camera = getCameraInstance();
double aspectRatio = 0;
if(camera != null){
//Setting the camera's 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);
Kemudian, dalam tampilan Permukaan saya mengatur parameter kamera yang akan ditampilkan
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's 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();
}
}
Anda dapat melihat bahwa pria LEGO tumbuh lebih tinggi dan lebih kurus ketika telepon diputar:
Bagaimana saya bisa memastikan bahwa rasio aspek untuk tampilan kamera saya sudah benar?
android
android-camera
surfaceview
preview
aspect-ratio
scientiffic
sumber
sumber
Jawaban:
Saya menggunakan metode ini -> berdasarkan Demo API untuk mendapatkan Ukuran Pratinjau saya:
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; }
Seperti yang Anda lihat, Anda harus memasukkan lebar dan tinggi layar Anda. Metode ini akan menghitung rasio layar berdasarkan nilai-nilai tersebut dan kemudian dari daftar yang didukungPreviewSizes akan memilih yang terbaik untuk Anda dari yang tersedia. Dapatkan daftar supportPreviewSize Anda di tempat di mana objek Kamera tidak null dengan menggunakan
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
Dan kemudian di onMeasure Anda bisa mendapatkan previewSize optimal Anda seperti itu:
@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); } }
Dan kemudian (dalam kode saya dalam metode surfaceChanged, seperti yang saya katakan, saya menggunakan struktur API Demo dari kode CameraActivity, Anda dapat membuatnya di Eclipse):
Dan satu petunjuk untuk Anda, karena saya melakukan aplikasi yang hampir sama seperti Anda. Praktik yang baik untuk Aktivitas Kamera adalah menyembunyikan StatusBar. Aplikasi seperti Instagram sedang melakukannya. Ini mengurangi nilai tinggi layar Anda dan mengubah nilai rasio Anda. Dimungkinkan untuk mendapatkan Ukuran Pratinjau yang aneh di beberapa perangkat (SurfaceView Anda akan dipotong sedikit)
Dan untuk menjawab pertanyaan Anda, bagaimana cara memeriksa apakah rasio pratinjau Anda sudah benar? Kemudian dapatkan parameter tinggi dan lebar yang Anda tetapkan:
mCamera.setParameters(parameters);
rasio set Anda sama dengan tinggi / lebar. Jika Anda ingin kamera terlihat bagus di layar Anda, maka rasio tinggi / lebar parameter yang Anda atur ke kamera harus sama dengan rasio tinggi (minus status bar) / lebar layar Anda.
sumber
parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height)
.Solusi F1Sher bagus tetapi terkadang tidak berhasil. Terutama, saat surfaceView Anda tidak menutupi seluruh layar. Dalam hal ini Anda perlu mengganti metode onMeasure (). Saya telah menyalin kode saya di sini untuk referensi Anda.
Karena saya mengukur surfaceView berdasarkan lebar, maka saya memiliki sedikit celah putih di ujung layar yang saya isi dengan desain. Anda dapat memperbaiki masalah ini jika Anda menjaga tinggi dan menambah lebar dengan mengalikannya dengan rasio. Namun, itu akan sedikit meremas 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; } }
sumber
getOptimalPreviewSize
agar ini bekerja untuk saya. apakah ini karena orientasi tampilan disetel ke 90? Jika tidak, perhitungan rasio tidak bahkan dekat (target adalah 1,7 dan rasio kamera kurang dari 1)CATATAN: SOLUSI SAYA ADALAH LANJUTAN DARI SOLUSI HESAM: https://stackoverflow.com/a/22758359/1718734
Yang saya bahas: Hesam mengatakan ada sedikit ruang putih yang mungkin muncul di beberapa ponsel, seperti ini:
Hesam menyarankan solusi kedua, tetapi itu menghentikan pratinjau. Dan pada beberapa perangkat, itu sangat terdistorsi.
Jadi bagaimana kita memperbaiki masalah ini. Ini sederhana ... dengan mengalikan rasio aspek hingga memenuhi layar. Saya perhatikan, beberapa aplikasi populer seperti Snapchat, WhatsApp, dll bekerja dengan cara yang sama.
Yang harus Anda lakukan adalah menambahkan ini ke metode 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); }
Ini akan menghitung tinggi layar dan mendapatkan rasio tinggi layar dan tinggi mPreviewSize. Kemudian, lebar dan tinggi kamera dikalikan dengan rasio tinggi baru dan setel dimensi yang diukur sesuai.
Dan hal berikutnya yang Anda ketahui, Anda berakhir dengan ini: D
Ini juga bekerja dengan baik dengan kamera depan. Saya percaya ini adalah cara terbaik untuk melakukan ini. Sekarang satu-satunya hal yang tersisa untuk aplikasi saya adalah menyimpan pratinjau itu sendiri setelah mengklik "Tangkap." Tapi ya, ini dia.
sumber
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
metode saya selalu mendapatkan lebar = 0 dan tinggi = 0;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; // 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; } }
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 , tetapiSurfaceView
tidak mengenaligetChildCount ()
ataugetChildAt (int index)
. Saya pikir, akhirnya saya membuatnya berfungsi dengan referensi kemaLayoutPreview
, 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 keMainActivity
.Di
CameraPreview
saya berubahprSupportedPreviewSizes
dangetOptimalPreviewSize()
menjadi publik sehingga saya bisa menggunakannya diMainActivity
. 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 setelahnyaonWindowFocusChanged
(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()
.sumber
Hai getOptimalPreview () yang ada di sini tidak berfungsi untuk saya, jadi saya ingin membagikan versi saya:
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; }
sumber
Hanya untuk membuat utas ini lebih lengkap, saya menambahkan jawaban versi saya:
Yang ingin saya capai: Tampilan permukaan tidak boleh direntangkan, dan harus menutupi seluruh layar, Selain itu, hanya ada mode lanskap di aplikasi saya.
Larutan:
Solusinya adalah perluasan yang sangat kecil untuk solusi F1sher:
=> Langkah pertama adalah mengintegrasikan solusi F1sher.
=> Nah, mungkin akan timbul skenario pada solusi F1sher ketika surface view tidak mencakup seluruh layar, Solusinya adalah membuat surface view lebih besar dari dimensi layar sehingga mencakup keseluruhan layar, untuk itu:
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 */
sumber
Saya menemukan apa masalahnya - dengan perubahan orientasi . Jika Anda mengubah orientasi kamera ke 90 atau 270 derajat daripada Anda perlu menukar lebar dan tinggi dari ukuran yang didukung dan semuanya akan baik-baik saja.
Juga tampilan permukaan harus terletak pada tata letak bingkai dan memiliki gravitasi pusat.
Berikut adalah contoh di C # (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; } }
Perhatikan bahwa parameter kamera harus disetel sebagai ukuran asli (tidak ditukar), dan ukuran tampilan permukaan harus ditukar.
sumber
saya mencoba semua solusi di atas tetapi tidak ada yang berhasil untuk saya. akhirnya saya memecahkannya sendiri, dan ternyata itu cukup mudah. Ada dua hal yang perlu Anda perhatikan.
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
previewSize ini harus menjadi salah satu resolusi yang didukung kamera, yang bisa didapatkan seperti di bawah ini:
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
biasanya salah satu rawSupportedSize sama dengan resolusi perangkat.
Kedua, tempatkan SurfaceView Anda di FrameLayout dan setel tinggi dan lebar tata letak permukaan dalam metode surfaceChanged seperti di atas
Oke, semuanya sudah selesai, semoga ini bisa membantu Anda.
sumber
@Hesam 's jawabannya benar,
CameraPreview
akan bekerja di semua perangkat portrait, tetapi jika perangkat berada dalam modus lansekap atau dalam mode multi-window, kode ini sempurna bekerja, hanya menggantionMeasure()
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); int rotation = ((Activity) mContext).getWindowManager().getDefaultDisplay().getRotation(); if ((Surface.ROTATION_0 == rotation || Surface.ROTATION_180 == rotation)) {//portrait mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, height, width); } else mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);//landscape if (mPreviewSize == null) return; float ratio; if (mPreviewSize.height >= mPreviewSize.width) { ratio = (float) mPreviewSize.height / (float) mPreviewSize.width; } else ratio = (float) mPreviewSize.width / (float) mPreviewSize.height; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && ((Activity) mContext).isInMultiWindowMode()) { if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT || !(Surface.ROTATION_0 == rotation || Surface.ROTATION_180 == rotation)) { setMeasuredDimension(width, (int) (width / ratio)); } else { setMeasuredDimension((int) (height / ratio), height); } } else { if ((Surface.ROTATION_0 == rotation || Surface.ROTATION_180 == rotation)) { Log.e("---", "onMeasure: " + height + " - " + width * ratio); //2264 - 2400.0 pix c -- yes //2240 - 2560.0 samsung -- yes //1582 - 1440.0 pix 2 -- no //1864 - 2048.0 sam tab -- yes //848 - 789.4737 iball -- no //1640 - 1600.0 nexus 7 -- no //1093 - 1066.6667 lenovo -- no //if width * ratio is > height, need to minus toolbar height if ((width * ratio) < height) setMeasuredDimension(width, (int) (width * ratio)); else setMeasuredDimension(width, (int) (width * ratio) - toolbarHeight); } else { setMeasuredDimension((int) (height * ratio), height); } } requestLayout(); }
sumber
Persyaratan saya adalah pratinjau kamera harus layar penuh dan menjaga rasio aspek. Solusi Hesam dan Yoosuf sangat bagus tetapi saya melihat masalah zoom tinggi karena suatu alasan.
Idenya sama, buatlah pusat penampung pratinjau sebagai induk dan tambah lebar atau tinggi tergantung pada rasio aspek hingga dapat menutupi seluruh layar.
Satu hal yang perlu diperhatikan adalah ukuran pratinjau dalam bentuk landscape karena kita mengatur orientasi tampilan.
Kontainer yang akan kita tambahkan tampilan SurfaceView:
<RelativeLayout android:id="@+id/camera_preview_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true"/>
Tambahkan pratinjau ke penampungnya dengan pusat di induk dalam aktivitas Anda.
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);
Di dalam kelas 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); }
sumber
Anda harus menyetel cameraView.getLayoutParams (). Height dan cameraView.getLayoutParams (). Lebar sesuai dengan rasio aspek yang Anda inginkan.
sumber
Saya menyerah kalkulasi dan hanya mendapatkan ukuran tampilan di mana saya ingin pratinjau kamera ditampilkan dan mengatur ukuran pratinjau kamera yang sama (hanya membalik lebar / tinggi karena rotasi) dalam implementasi SurfaceView kustom saya:
@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 }
sumber