Anteprima fotocamera Android allungata


136

Ho lavorato per rendere la mia attività della fotocamera personalizzata su Android, ma quando si ruota la fotocamera, le proporzioni della vista della superficie vengono incasinate.

Nel mio oncreate per l'attività, ho impostato il framelayout che contiene la vista della superficie che mostra i parametri della fotocamera.

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

Quindi, nella vista Superficie ho impostato i parametri della videocamera da visualizzare

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

    }

inserisci qui la descrizione dell'immagine

Puoi vedere che l'uomo LEGO diventa più alto e magro quando il telefono viene ruotato:

Come posso assicurarmi che le proporzioni della vista della mia fotocamera siano corrette?


@scientific mi potete aiutare dove devo scrivere il metodo dato che sto affrontando lo stesso problema?
user3233280,

Mi è sembrato di avere un problema simile, ma ciò che ha risolto stava chiamando il seguente: mCamera.setDisplayOrientation (90) nel metodo surfaceCreated () del mio SurfaceView per la fotocamera
Greg

Risposte:


163

Sto usando questo metodo -> basato su demo API per ottenere le dimensioni dell'anteprima:

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

Come puoi vedere devi inserire la larghezza e l'altezza dello schermo. Questo metodo calcolerà il rapporto dello schermo in base a questi valori e quindi dall'elenco di SupportPreviewSizes sceglierà il meglio per te tra quelli disponibili. Ottieni il tuo elenco SupportPreview supportato nel punto in cui l'oggetto Camera non è nullo utilizzando

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

E poi su onMeasure puoi ottenere l'anteprima ottimale Dimensione così:

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

E poi (nel mio codice nel metodo surfaceChanged, come ho detto sto usando la struttura Demo API del codice CameraActivity, puoi generarlo in Eclipse):

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

E un suggerimento per te, perché ho fatto quasi la stessa app come te. La buona pratica per l'attività della fotocamera è quella di nascondere StatusBar. Applicazioni come Instagram lo stanno facendo. Riduce il valore dell'altezza dello schermo e modifica il valore del rapporto. È possibile ottenere strane dimensioni di anteprima su alcuni dispositivi (il tuo SurfaceView verrà leggermente tagliato)


E per rispondere alla tua domanda, come verificare se il rapporto di anteprima è corretto? Quindi ottieni altezza e larghezza dei parametri impostati in:

mCamera.setParameters(parameters);

il rapporto impostato è uguale a altezza / larghezza. Se si desidera che la fotocamera abbia un bell'aspetto sullo schermo, il rapporto altezza / larghezza dei parametri impostati sulla fotocamera deve essere uguale al rapporto altezza (barra di stato meno) / larghezza dello schermo.


2
Solo una domanda: vedo che stai usando 2 diversi rapporti calcolati: double targetRatio = (double) h / w e double ratio = (double) size.width / size.height . Il primo è l'altezza divisa per la larghezza e l'altro è l'opposto, la larghezza divisa per l'altezza. Non dovrebbero essere lo stesso calcolo?
phyzalis,

Non importa perché l'elenco delle dimensioni prende in considerazione ogni possibilità, ad esempio: troverai qualcosa come 480x680, ma prima o poi ci saranno 680x480 (per il paesaggio), quindi devi solo andare su quell'elenco e trovarne uno che corrisponde alle tue esigenze. Lo troverai prima o poi.
F1sher

Inoltre, se vuoi che anche le tue immagini siano di queste dimensioni (come probabilmente dovrebbero essere), hai impostato le dimensioni dell'immagine con parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height).
Matt Logan,

2
@ F1sher, ottengo sempre un'eccezione puntatore null parameter.setPreviewSize (mPreviewSize.width, mPreviewSize.height); Non riesco a trovare alcuna soluzione.
FIXI,

3
@phyzalis Sono d'accordo con te. In effetti, ho dovuto apportare quella correzione affinché il codice funzionasse come previsto.
fernio

149

La soluzione di F1Sher è bella ma a volte non funziona. In particolare, quando SurfaceView non copre l'intero schermo. In questo caso è necessario sovrascrivere il metodo onMeasure (). Ho copiato il mio codice qui come riferimento.

Da quando ho misurato surfaceView in base alla larghezza, ho un po 'di spazio bianco alla fine del mio schermo che ho riempito in base alla progettazione. Puoi risolvere questo problema se mantieni l'altezza e aumenti la larghezza moltiplicandola per il rapporto. Tuttavia, schiaccia leggermente 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;
    }
}

11
questa dovrebbe essere la risposta corretta, gestisce il caso quando l'anteprima della fotocamera non copre l'intero schermo. +1.
Houcine,

1
quando mi gonfiavo nella mia applicazione di attività era chiusa <com.app.camera.CameraPreview android: id = "@ + id / camera_preview" android: layout_width = "match_parent" android: layout_height = "match_parent" />
fish40

È necessario apportare alcune modifiche, ma accetto questo come soluzione. Up-votato.
JaydeepW,

2
Ciao @ adnan9011, questo tutorial potrebbe essere utile probabilmente. airpair.com/android/android-camera-surface-view-fragment
Hesam

4
l'altezza e la larghezza dovevano essere commutate getOptimalPreviewSizeaffinché questo funzionasse per me. è perché l'orientamento del display è stato impostato su 90? Altrimenti, i calcoli rapporto erano nemmeno vicino (bersaglio era 1,7 e rapporti telecamere erano meno di 1)
ono

23

NOTA: LA MIA SOLUZIONE È UNA CONTINUA DELLA SOLUZIONE DI HESAM: https://stackoverflow.com/a/22758359/1718734

A cosa mi rivolgo: Hesam ha detto che su alcuni telefoni potrebbe apparire un piccolo spazio bianco, come questo:

NOTA: sebbene le proporzioni siano corrette, la fotocamera non riempie l'intero schermo.

Hesam suggerì una seconda soluzione, ma che schiaccia l'anteprima. E su alcuni dispositivi, distorce pesantemente.

Quindi, come possiamo risolvere questo problema. È semplice ... moltiplicando le proporzioni fino a riempire lo schermo. Ho notato che diverse app popolari come Snapchat, WhatsApp, ecc funzionano allo stesso modo.

Tutto quello che devi fare è aggiungere questo al metodo 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);
    }

Questo calcolerà l'altezza dello schermo e otterrà il rapporto tra l'altezza dello schermo e l'altezza mPreviewSize. Quindi moltiplica la larghezza e l'altezza della telecamera per il nuovo rapporto di altezza e imposta di conseguenza la dimensione misurata.

inserisci qui la descrizione dell'immagine

E la prossima cosa che sai, finisci con questo: D

inserisci qui la descrizione dell'immagine

Funziona bene anche con la fotocamera frontale. Credo che questo sia il modo migliore per farlo. Ora l'unica cosa rimasta per la mia app è salvare l'anteprima stessa facendo clic su "Cattura". Ma sì, eccolo.


Funziona molto bene !! ma non funziona correttamente in Landscape: /
Matan Dahan,

il tuo codice ha risolto il mio problema con lo spazio nero vuoto di 2 mesi :)
karthik kolanji

1
Funziona, ma nel mio caso .. Ho una barra di azione in alto, quindi la barra bianca era più piccola, tuttavia dopo aver aggiunto il codice la mia macchina fotografica ha ingrandito del 40-50%. Quando si passa alla fotocamera frontale, praticamente non riesco a vedere il mio viso dritto (solo un po 'di capelli), a causa di quello zoom elevato.
Makalele,

@Yoosuf Ricevo una schermata bianca quando ho seguito il tuo codice. Nel onMeasure(int widthMeasureSpec, int heightMeasureSpec)metodo ottengo sempre larghezza = 0 e altezza = 0;
Kush Patel,

10

OK, quindi penso che non ci sia una risposta sufficiente per il problema di stretching dell'anteprima generale della fotocamera. O almeno non ne ho trovato uno. Anche la mia app ha sofferto di questa sindrome da stretching e mi ci è voluto un po 'di tempo per trovare una soluzione da tutte le risposte degli utenti su questo portale e su Internet.

Ho provato la soluzione di @ Hesam ma non ha funzionato e l'anteprima della mia fotocamera è stata notevolmente distorta.

Per prima cosa mostro il codice della mia soluzione (le parti importanti del codice) e poi spiego perché ho preso quei passaggi. C'è spazio per le modifiche alle prestazioni.

Attività principale layout 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>

Anteprima della fotocamera:

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

Attività principale:

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

Il mio commento:

Il punto di tutto questo è che, anche se si calcola la dimensione ottimale della telecamera in getOptimalPreviewSize()solamente scegliere il rapporto più vicino per adattarla allo schermo. Quindi, a meno che il rapporto sia esattamente lo stesso, l'anteprima si allungherà.

Perché si allungherà? Perché l'anteprima della videocamera FrameLayout è impostata in layout.xml su match_parent in larghezza e altezza. Ecco perché l'anteprima si estende a schermo intero.

Ciò che deve essere fatto è impostare la larghezza e l'altezza del layout dell'anteprima della fotocamera in modo che corrispondano al rapporto dimensioni della telecamera scelto , in modo che l'anteprima mantenga le proporzioni e non distorca.

Ho provato a usare la CameraPreviewclasse per fare tutti i calcoli e le modifiche al layout, ma non sono riuscito a capirlo. Ho provato ad applicare questa soluzione , ma SurfaceViewnon lo riconoscogetChildCount () o getChildAt (int index). Penso che alla fine abbia funzionato con un riferimento a maLayoutPreview, ma si è comportato male e ha applicato il rapporto impostato a tutta la mia app e lo ha fatto dopo aver scattato la prima foto. Quindi l'ho lasciato andare e ho spostato le modifiche al layout in MainActivity.

In CameraPreviewho cambiato prSupportedPreviewSizese getOptimalPreviewSize()al pubblico in modo da poterlo utilizzare in MainActivity. Quindi avevo bisogno delle dimensioni del display (meno la barra di navigazione / stato, se presente) e ho scelto le dimensioni ottimali della videocamera . Ho cercato di ottenere la dimensione RelativeLayout (o FrameLayout) anziché la dimensione di visualizzazione, ma stava restituendo un valore zero. Questa soluzione non ha funzionato per me. Il layout ha ottenuto il suo valore dopo onWindowFocusChanged(verificato nel registro).

Quindi ho i miei metodi per calcolare le dimensioni del layout in modo che corrispondano alle proporzioni della dimensione della telecamera scelta. Ora devi solo impostare LayoutParamsil layout di anteprima della fotocamera. Modifica la larghezza, l'altezza e centrala nel genitore.

Esistono due opzioni su come calcolare le dimensioni dell'anteprima. O vuoi che si adatti allo schermo con barre nere (se windowBackground è impostato su null) sui lati o in alto / in basso. Oppure vuoi che l'anteprima venga ingrandita a schermo intero . Ho lasciato un commento con maggiori informazioni in calcCamPrevDimensions().


6

Ciao getOptimalPreview () che è qui non ha funzionato per me, quindi voglio condividere la mia versione:

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

si converte in una fotocamera quadrata, dato che l'ingresso è (1920x1080) dopo aver ottimizzato la larghezza e l'altezza fornite (1088x1088), il mio dispositivo di test è Samsung S6. Posso sapere qual è il motivo. per favore condividi se hai qualche idea in merito a questo problema.
MohanRaj S,

2

Solo per rendere questa discussione più completa sto aggiungendo la mia versione di risposta:

Cosa volevo ottenere: la vista della superficie non dovrebbe essere allungata e dovrebbe coprire l'intero schermo. Inoltre, nella mia app c'era solo una modalità orizzontale.

Soluzione:

La soluzione è un'estensione estremamente piccola della soluzione di F1sher:

=> Il primo passo è integrare la soluzione di F1sher.

=> Ora, potrebbe sorgere uno scenario nella soluzione di F1sher quando la vista della superficie non copre l'intero schermo, La soluzione è quella di rendere la vista della superficie maggiore delle dimensioni dello schermo in modo che copra l'intero schermo, per quello:

    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

Ho capito qual è il problema: è con i cambiamenti di orientamento . Se cambi l'orientamento della fotocamera a 90 o 270 gradi rispetto a quello necessario, devi cambiare larghezza e altezza delle dimensioni supportate e tutto andrà bene.

Inoltre, la vista della superficie dovrebbe trovarsi in una disposizione del telaio e avere la gravità centrale.

Ecco un esempio su 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;
    }
}

Prestare attenzione al fatto che i parametri della fotocamera devono essere impostati come dimensioni originali (non scambiate) e che le dimensioni della vista della superficie devono essere scambiate.


1

ho provato tutte le soluzioni sopra ma nessuna di esse funziona per me. alla fine l'ho risolto da solo, e in realtà è abbastanza facile. ci sono due punti che devi fare attenzione.

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

questa anteprima La dimensione deve essere una delle risoluzioni supportate dalla fotocamera, che può essere ottenuta come di seguito:

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

di solito uno dei rawSupportedSize è uguale alla risoluzione del dispositivo.

In secondo luogo, posiziona SurfaceView in un FrameLayout e imposta l'altezza e la larghezza del layout della superficie nel metodo surfaceChanged come sopra

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

Ok, cose fatte, spero che questo possa aiutarti.


0

Le mie esigenze sono che l'anteprima della fotocamera debba essere a schermo intero e mantenere le proporzioni. La soluzione di Hesam e Yoosuf è stata fantastica, ma per qualche motivo vedo un problema di zoom elevato.

L'idea è la stessa, avere il centro del contenitore di anteprima nel genitore e aumentare la larghezza o l'altezza dipende dalle proporzioni fino a quando non può coprire l'intero schermo.

Una cosa da notare è che le dimensioni dell'anteprima sono in orizzontale perché impostiamo l'orientamento del display.

camera.setDisplayOrientation (90);

Il contenitore a cui aggiungeremo la vista SurfaceView a:

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

Aggiungi l'anteprima al suo contenitore con il centro nell'elemento principale nella tua attività.

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

All'interno della classe 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);
}

-1

È necessario impostare cameraView.getLayoutParams (). Height e cameraView.getLayoutParams (). Width in base alle proporzioni desiderate.


è diverso dall'impostare il parametro della mia videocamera in questo modo (cosa che sto facendo attualmente): parametri.setPreviewSize (optimumSize.width, optimumSize.height); mCamera.setParameters (parametri);
scientiffic,

-2

Ho rinunciato ai calcoli e ho semplicemente ottenuto le dimensioni della vista in cui desidero visualizzare l'anteprima della videocamera e ho impostato le stesse dimensioni dell'anteprima della videocamera (solo larghezza / altezza capovolte a causa della rotazione) nella mia implementazione personalizzata di 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
}

Il documento Android dice: Quando si impostano le dimensioni dell'anteprima, è necessario utilizzare i valori di getSupportedPreviewSizes (). Non impostare valori arbitrari nel metodo setPreviewSize ().
G. Steigert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.