Android Kamera Önizleme Gerildi


136

Özel kamera etkinliğimi Android'de yapmaya çalışıyorum, ancak kamerayı döndürürken, yüzey görünümünün en boy oranı bozuluyor.

Etkinlik için oluşturduğum fotoğraf makinesinin parametrelerini gösteren yüzey görünümünü tutan çerçeve düzenini ayarladım.

//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);

Sonra, Yüzey görünümünde kameranın parametrelerini gösterilecek şekilde ayarladım

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();
        }

    }

resim açıklamasını buraya girin

Telefon döndürüldüğünde LEGO insanının daha uzun ve daha da büyüdüğünü görebilirsiniz:

Kamera görünümüm için en boy oranının doğru olmasını nasıl sağlayabilirim?


@scientific u pls bana nerede aynı yöntemi karşı karşıya verilen yöntem im yazmalıyım yardımcı olabilir?
user3233280

Ben benzer bir sorun gibi görünüyordu, ama ne sabit olarak adlandırıyordu şu diyordu: kamera için SurfaceView - SurfaceCreated () yönteminde mCamera.setDisplayOrientation (90)
Greg

Yanıtlar:


163

Önizleme Boyutumu almak için API Demos'u temel alan bu yöntemi kullanıyorum:

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;
    }

Gördüğünüz gibi ekranınızın genişliğini ve yüksekliğini girmeniz gerekiyor. Bu yöntem, bu değerlere göre ekran oranını hesaplar ve daha sonra desteklenen Önizleme Boyutları listesinden, uygun olanlardan sizin için en iyisini seçecektir. Desteklenen Önizleme Boyutu listenizi kullanarak Kamera nesnesinin boş olmadığı yerlerde

mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();

Ve sonra onMeasure'da optimum önizlemenizi alabilirsiniz

@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);
        }
    }

Ve sonra (SurfaceChanged yöntemindeki kodumda, CameraActivity kodunun API Demos yapısını kullanıyorum dediğim gibi, Eclipse'de oluşturabilirsiniz):

Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();

Ve senin için bir ipucu, çünkü senin gibi neredeyse aynı uygulamayı yaptım. Kamera Etkinliği için iyi bir uygulama StatusBar'ı gizlemektir. Instagram gibi uygulamalar yapıyor. Ekran yükseklik değerinizi azaltır ve oran değerinizi değiştirir. Bazı cihazlarda garip Önizleme Boyutları elde etmek mümkündür (SurfaceView'unuz biraz kesilir)


Sorunuzu cevaplamak için önizleme oranınızın doğru olup olmadığını nasıl kontrol edersiniz? Ardından, ayarladığınız parametrelerin yüksekliğini ve genişliğini alın:

mCamera.setParameters(parameters);

ayarladığınız oran yükseklik / genişliğe eşittir. Kameranın ekranınızda iyi görünmesini istiyorsanız, kameraya ayarladığınız parametrelerin yükseklik / genişlik oranı, ekranınızın yükseklik (eksi durum çubuğu) / genişlik oranı ile aynı olmalıdır.


2
Sadece bir soru: Gördüğünüz gibi 2 farklı hesaplanmış oran var: çift ​​targetRatio = (çift) s / b ve çift ​​oran = (çift) boyut . Genişlik / boyut . Yükseklik . Birincisi, genişliğin bölündüğü genişlik, diğeri ise tam tersi, genişliği ise bölü yüksekliktir. Aynı hesaplama olmamalı mı?
Ocak'ta phyzalis

Önemli değil, çünkü boyutların listesi her olasılığı göz önünde bulundurur: 480x680 gibi bir şey bulacaksınız, ancak er ya da geç 680x480 (manzara için) olacak, bu yüzden listeyi gözden geçirip ihtiyaçlarınızı karşılar. Er ya da geç bulacaksınız.
F1

Ayrıca, resimlerinizin de bu boyutta olmasını istiyorsanız (muhtemelen olması gerektiği gibi), resim boyutunu ayarladınız parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height).
Matt Logan

2
@ F1sher, her zaman boş gösterici istisna parametreleri alıyorum. SetPreviewSize (mPreviewSize.width, mPreviewSize.height); Herhangi bir çözüm bulunamıyor.
FIXI

3
@phyzalis Sana katılıyorum. Aslında, kodun beklendiği gibi çalışması için bu düzeltmeyi yapmak zorunda kaldım.
Mart'ta fernio

149

F1Sher'in çözümü güzel ama bazen işe yaramıyor. Özellikle surfaceView'iniz tüm ekranı kaplamıyorsa. Bu durumda onMeasure () yöntemini geçersiz kılmanız gerekir. Referansınız için kodumu buraya kopyaladım.

SurfaceView'i genişliğe göre ölçtüğüm için, ekranımın sonunda tasarımla doldurduğum biraz beyaz boşluk var. Yüksekliği koruduğunuzda ve genişliği oranla çarparak artırdığınızda bu sorunu çözebilirsiniz. Bununla birlikte, yüzey görünümü ezecek Biraz.

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;
    }
}

11
bu doğru cevap olmalı, kameranın önizlemesi tüm ekranı kaplamıyorsa durumu ele alır. +1.
Houcine

1
etkinlik uygulamamda şişirdiğimde kapatıldı <com.app.camera.CameraPreview android: id = "@ + id / camera_preview" android: layout_width = "match_parent" android: layout_height = "match_parent" />
fish40

Yine de bazı değişiklikler yapmanız gerekiyor, ancak bunu bir çözüm olarak kabul ediyorum. Yukarı-oy kullandı.
JaydeepW

2
Merhaba @ adnan9011, bu eğitim muhtemelen yardımcı olabilir. airpair.com/android/android-camera-surface-view-fragment
Hesam

4
bunun getOptimalPreviewSizebenim için çalışması için yükseklik ve genişliğin değiştirilmesi gerekiyordu . bunun nedeni ekran yönü 90 olarak ayarlanmış mı? Aksi takdirde, oran hesaplamaları daha da yakın değildi (hedef 1.7 idi ve kamera oranları 1'den azdı)
Ono

23

NOT: ÇÖZÜM HESAM ÇÖZÜMÜNÜN DEVAMI: https://stackoverflow.com/a/22758359/1718734

Neye hitap ediyorum: Hesam, bazı telefonlarda görünebilecek küçük bir beyaz alan olduğunu söyledi:

NOT: En boy oranı doğru olmasına rağmen, kamera tüm ekranı doldurmaz.

Hesam ikinci bir çözüm önerdi, ancak bu önizlemeyi bozuyor. Ve bazı cihazlarda, büyük ölçüde bozuluyor.

Peki bu sorunu nasıl çözeriz? Bu basittir ... en boy oranlarını ekranda dolana kadar çarparak. Farkettim, Snapchat, WhatsApp, vb.Gibi birçok popüler uygulama aynı şekilde çalışıyor.

Tek yapmanız gereken bunu onMeasure yöntemine eklemek:

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);
    }

Bu, ekran yüksekliğini hesaplar ve ekran yüksekliği ile mPreviewSize yüksekliğinin oranını alır. Ardından kameranın genişliğini ve yüksekliğini yeni yükseklik oranıyla çarpar ve ölçülen boyutu buna göre ayarlar.

resim açıklamasını buraya girin

Ve bildiğiniz bir sonraki şey, bununla sonuçlanır: D

resim açıklamasını buraya girin

Bu aynı zamanda ön kamerasıyla da iyi çalışır. Bunun bu konuda en iyi yol olduğuna inanıyorum. Artık uygulamam için kalan tek şey "Yakala" yı tıkladıktan sonra önizlemenin kendisini kaydetmektir. Ama siz, bu kadar.


Bu çok iyi çalışıyor !! ancak
Manzara'da

kodunu
çözdüm

1
Çalışıyor, ancak benim durumumda .. Üstte eylem çubuğu var, bu yüzden beyaz çubuk daha küçüktü, ancak kodunuzu ekledikten sonra kameram% 40-50 oranında büyütülmüş görünüyor. Öne bakan kameraya geçerken, yüksek zoomdan dolayı yüzümün düz göründüğünü (sadece biraz saç) göremiyorum.
Makalele

@Yoosuf Kodunuzu takip ettiğimde beyaz ekran alıyorum. Gelen onMeasure(int widthMeasureSpec, int heightMeasureSpec)yöntemi her zaman olsun genişliği = 0 ve yükseklik = 0;
Kush Patel

10

Tamam, bence yeterli cevap yok genel kamera önizleme germe sorunu için . Ya da en azından bir tane bulamadım. Uygulamam da bu germe sendromu yaşadı ve bu portal ve internetteki tüm kullanıcı cevaplarından bir çözüm birlikte bulmaca yapmak biraz zaman aldı.

Hesam'ın çözümünü denedim ama işe yaramadı ve kamera önizlememi büyük ölçüde .

Önce çözümümün kodunu (kodun önemli kısımlarını) gösterdim ve neden bu adımları attığımı açıklıyorum. Performans değişiklikleri için yer var.

Ana faaliyet xml düzeni:

<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>

Kamera Önizleme:

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;
}
}

Ana aktivite:

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;
}   
}

Yorumum:

Tüm bu noktada size hesaplamak rağmen yani, optimum kamera boyutunu içinde getOptimalPreviewSize()size sadece seçmek en yakın oran ekranınıza sığacak şekilde. Dolayısıyla, oran tam olarak aynı değilse önizleme uzayacaktır.

Neden esneyecek? FrameLayout kamera önizlemeniz layout.xml dosyasında genişlik ve yükseklikte match_parent olarak ayarlandığından . Bu yüzden önizleme tam ekrana kadar uzanır.

Yapılması gereken, kamera önizleme düzeni genişliğini ve yüksekliğini seçilen kamera boyutu oranına uyacak şekilde ayarlamaktır , böylece önizleme en boy oranını korur ve deforme olmaz.

CameraPreviewTüm hesaplamaları ve düzen değişikliklerini yapmak için sınıfı kullanmaya çalıştım , ama anlayamadım. Bu çözümü uygulamaya çalıştım ama SurfaceViewtanımıyor getChildCount ()ya da getChildAt (int index). Sanırım, sonunda referans olarak çalıştım maLayoutPreview, ancak yanlış davranıyordu ve set oranını tüm uygulamama uyguladı ve ilk resim çekildikten sonra bunu yaptı. Bu yüzden bıraktım ve düzen değişikliklerini MainActivity.

Gelen CameraPreviewDeğiştim prSupportedPreviewSizesve getOptimalPreviewSize()hiç kamuoyuna ben bunu kullanabilmesi MainActivity. Ardından ekran boyutlarına (varsa eksi gezinme / durum çubuğuna) ihtiyacım vardı ve en uygun kamera boyutunu seçtim . Görüntü boyutu yerine RelativeLayout (veya FrameLayout) boyutunu almaya çalıştım, ancak sıfır değeri döndürüyordu. Bu çözüm benim için işe yaramadı. Düzen, değerinden sonra varonWindowFocusChanged (günlükte kontrol edilir).

Bu yüzden, seçilen kamera boyutunun en boy oranına uyması için düzen boyutlarını hesaplama yöntemlerim var. Şimdi sadeceLayoutParams kamera önizleme düzeninizi . Genişliği, yüksekliği değiştirin ve üst öğede ortalayın.

Önizleme boyutlarının nasıl hesaplanacağı iki seçenek vardır. Ya ekranın kenarlarında veya üstte / altta siyah çubuklarla (windowBackground boş olarak ayarlanmışsa) sığmasını istiyorsunuz. Veya önizlemenin tam ekrana yakınlaştırılmasını istiyorsunuz . İçinde daha fazla bilgi içeren yorum bıraktım calcCamPrevDimensions().


6

Merhaba getOptimalPreview () burada benim için işe yaramadı bu yüzden sürümümü paylaşmak istiyorum:

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;
}

kare bir kameraya dönüşür, verilen genişlik ve yüksekliği optimize ettikten sonra verilen giriş (1920x1080) (1088x1088), Test cihazım Samsung S6. Sebebini öğreneyim. Bu konuda herhangi bir fikriniz varsa lütfen paylaşın.
MohanRaj S

2

Sadece bu konuyu daha eksiksiz hale getirmek için cevap sürümümü ekliyorum:

Ne elde etmek istedim: Yüzey görünümü gerilmemeli ve tüm ekranı kapsamalıdır, Ayrıca, benim app sadece bir manzara modu vardı.

Çözüm:

Çözüm, F1sher'in çözümünün son derece küçük bir uzantısıdır:

=> İlk adım F1sher'in çözümünü entegre etmektir.

=> Şimdi, yüzey görünümü tüm ekranı kapsamadığında F1sher çözümünde bir senaryo ortaya çıkabilir, Çözüm, yüzey görünümünü ekran boyutlarından daha büyük yapmaktır, böylece bunun için:

    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  */

1

Sorunun ne olduğunu anladım - yönelim değişiklikleriyle. Kamera yönünü, genişliği ve yüksekliği değiştirmeniz gerekenden 90 veya 270 dereceye değiştirirseniz , desteklenen boyutların ve hepsi iyi olur.

Ayrıca yüzey görünümü bir çerçeve düzeninde yer almalı ve orta ağırlıkta olmalıdır.

İşte C # (Xamarin) ile ilgili örnek:

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;
    }
}

Kamera parametrelerinin orijinal boyut olarak ayarlanmasına (değiştirilmemesine) ve yüzey görüntüleme boyutunun değiştirilmesine dikkat edin.


1

yukarıdaki tüm çözümü denedim ama hiçbiri benim için çalışmıyor. Sonunda kendim çözdüm ve aslında oldukça kolay olduğunu düşünüyorum. dikkatli olmanız gereken iki nokta var.

parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);

Bu önizleme Boyut aşağıdaki gibi alınabilecek kamera destekli çözünürlüklerden biri olmalıdır:

List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes(); 

genellikle rawSupportedSize öğelerinden biri aygıt çözünürlüğüne eşittir.

İkinci olarak SurfaceView'inizi bir FrameLayout'a yerleştirin ve surfaceChanged yönteminde yüzey düzeni yüksekliğini ve genişliğini yukarıdaki gibi ayarlayın

FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
layoutParams.height = cameraResolution.x;
layoutParams.width = cameraResolution.y;

Tamam, işler bitti, umarım bu sana yardımcı olabilir.


0

Benim gereksinimlerim kamera önizlemesinin tam ekran olması ve en boy oranını tutması gerekiyor. Hesam ve Yoosuf'un çözümü harikaydı ama bir nedenden dolayı yüksek zum sorunu görüyorum.

Fikir aynıdır, önizleme kapsayıcı merkezini üstte bulundurun ve tüm ekranı kaplayana kadar en / boy oranlarına bağlı olarak genişliği veya yüksekliği artırın.

Dikkat edilmesi gereken bir şey, ekran yönünü ayarladığımız için önizleme boyutunun peyzajda olmasıdır.

camera.setDisplayOrientation (90);

SurfaceView görünümünü ekleyeceğimiz kapsayıcı:

<RelativeLayout
    android:id="@+id/camera_preview_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"/>

Önizlemeyi, etkinliğinizde üst ebeveyn merkezi olacak şekilde kapsayıcısına ekleyin.

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 sınıfının içinde:

@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);
}

-1

CameraView.getLayoutParams (). Height ve cameraView.getLayoutParams (). Genişliğini istediğiniz en boy oranına göre ayarlamalısınız.


bu benim kamera görünümümün parametresini şu şekilde ayarlamaktan farklıdır (şu anda yapıyorum): parameters.setPreviewSize (optimalSize.width, optimalSize.height); mCamera.setParameters (parametreler);
Bilim

-2

Hesaplamaları bıraktım ve sadece özel önizleme uygulamamda kamera önizlemesinin görüntülenmesini istediğim görünümün boyutunu alıyorum ve kameranın önizleme boyutunu aynı şekilde ayarladım (yalnızca döndürme nedeniyle genişlik / yükseklik):

@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
}

Android dokümanı şöyle diyor: Önizleme boyutunu ayarlarken, getSupportedPreviewSizes () öğesindeki değerleri kullanmalısınız. SetPreviewSize () yönteminde rasgele değerler ayarlamayın.
G. Steigert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.