web-dev-qa-db-fra.com

Android Aperçu de l'appareil photo étiré

Je travaille sur la création d'une activité de caméra personnalisée sous Android, mais lors de la rotation de la caméra, le rapport de format de la vue de surface est altéré.

Dans ma création pour l'activité, je règle le framelayout qui contient la vue de surface qui affiche les paramètres de la caméra.

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

Ensuite, dans la vue Surface, je règle les paramètres de la caméra à afficher

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

    }

enter image description here

Vous pouvez voir que l'homme LEGO grandit et devient plus maigre lorsque le téléphone est en rotation:

Comment puis-je m'assurer que le rapport d'aspect de ma vue caméra est correct?

127
scientiffic

J'utilise cette méthode -> basée sur des démonstrations d'API pour obtenir ma taille de prévisualisation:

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

Comme vous pouvez le constater, vous devez saisir la largeur et la hauteur de votre écran. Cette méthode calcule le rapport d'écran en fonction de ces valeurs, puis, à partir de la liste des tailles de vues de support prises en charge, sélectionne le meilleur pour vous parmi celles disponibles. Obtenez votre liste supportedPreviewSize à la place où l'objet Camera n'est pas null en utilisant

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

Et puis dans onMeasure, vous pouvez obtenir votre previewSize optimal comme ceci:

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

Et puis (dans mon code dans la méthode surfaceChanged, comme je l'ai dit, j'utilise la structure API Demos du code CameraActivity, vous pouvez le générer dans Eclipse):

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

Et un indice pour vous, parce que j'ai fait presque la même application que vous. Une bonne pratique pour Camera Activity consiste à masquer StatusBar. Des applications comme Instagram le font. Cela réduit la valeur de votre hauteur d’écran et change votre valeur de ratio. Il est possible d’obtenir des tailles de prévisualisation étranges sur certains appareils (votre SurfaceView sera un peu coupé)


Et pour répondre à votre question, comment vérifier si votre taux de prévisualisation est correct? Ensuite, obtenez la hauteur et la largeur des paramètres que vous avez définis dans:

mCamera.setParameters(parameters);

votre ratio défini est égal à hauteur/largeur. Si vous voulez que la caméra ait une belle apparence sur votre écran, le rapport hauteur/largeur des paramètres que vous définissez comme étant la caméra doit être identique à la hauteur (moins la barre d'état)/largeur de votre écran.

159
F1sher

La solution de F1Sher est Nice mais parfois ne fonctionne pas. En particulier, lorsque votre surfaceView ne couvre pas tout l'écran. Dans ce cas, vous devez remplacer la méthode onMeasure (). J'ai copié mon code ici pour votre référence.

Depuis que j'ai mesuré surfaceView en fonction de la largeur, j'ai un petit espace blanc à la fin de mon écran que je l'ai rempli par la conception. Vous pouvez résoudre ce problème si vous gardez la hauteur et augmentez la largeur en le multipliant en ratio. Cependant, il va légèrement écraser 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;
    }
}
142
Hesam

REMARQUE: MA SOLUTION IS UNE SUITE DE LA SOLUTION HESAM: https://stackoverflow.com/a/22758359/1718734

À quoi je m'adresse: Hesam a déclaré qu'un petit espace blanc pouvait apparaître sur certains téléphones, par exemple:

NOTE: Although the aspect ratio is correct, the camera does not fill in the whole screen.

Hesam a suggéré une deuxième solution, mais qui écrase l'aperçu. Et sur certains appareils, cela fausse fortement.

Alors, comment pouvons-nous résoudre ce problème. C'est simple ... en multipliant les ratios d'aspect jusqu'à ce qu'il remplisse l'écran. J'ai remarqué que plusieurs applications populaires telles que Snapchat, WhatsApp, etc. fonctionnent de la même manière.

Tout ce que vous avez à faire est d’ajouter ceci à la méthode 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);
    }

Cela va calculer la hauteur de l'écran et obtenir le rapport entre la hauteur de l'écran et la hauteur de mPreviewSize. Ensuite, il multiplie la largeur et la hauteur de la caméra par le nouveau rapport de hauteur et définit la dimension mesurée en conséquence.

enter image description here

Et la prochaine chose que vous savez, vous vous retrouvez avec ceci: D

enter image description here

Cela fonctionne aussi bien avec la caméra frontale. Je crois que c'est la meilleure façon de s'y prendre. Maintenant, la seule chose qui me reste pour mon application est de sauvegarder l'aperçu en cliquant sur "Capturer". Mais voilà, c'est ça.

23
Yoosuf

OK, donc je pense qu'il n'y a pas de réponse suffisante pour résoudre le problème d'étirement de l'aperçu de la caméra. Ou du moins je n'en ai pas trouvé. Mon application souffrait également de ce syndrome d'étirement et il m'a fallu un certain temps pour trouver ensemble une solution parmi toutes les réponses des utilisateurs sur ce portail et sur Internet.

J'ai essayé @ Hesam's solution mais cela n'a pas fonctionné et j'ai laissé l'aperçu de ma caméra fortement déformé.

Je montre d'abord le code de ma solution (les parties importantes du code), puis j'explique pourquoi j'ai pris ces mesures. Il y a place pour des modifications de performances.

Mise en page de l'activité principale:

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

Aperçu de la caméra vidéo:

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

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

Mon commentaire:

Le but de tout cela est que, bien que vous calculiez la taille optimale de la caméra dans getOptimalPreviewSize(), vous ne sélectionnez que le ratio le plus proche pour s'adapter à votre écran. Donc, à moins que le rapport ne soit exactement identique , l'aperçu s'étire.

Pourquoi ça va s'étirer? Parce que votre aperçu de la caméra FrameLayout est défini dans layout.xml à match_parent dans largeur et hauteur. C'est pourquoi l'aperçu s'étendra en plein écran.

Ce qui doit être fait est de définir la disposition de prévisualisation de la caméra en largeur et en hauteur pour correspondre au rapport de taille de caméra choisi , afin que l'aperçu conserve son format d'image et ne se déforme pas.

J'ai essayé d'utiliser la classe CameraPreview pour effectuer tous les calculs et toutes les modifications de mise en page, mais je ne pouvais pas le comprendre. J'ai essayé d'appliquer cette solution , mais SurfaceView ne reconnaît pas getChildCount () ni getChildAt (int index). Je pense que je l'ai finalement fait fonctionner avec une référence à maLayoutPreview, mais il s'est mal comporté et a appliqué le rapport défini à l'ensemble de mon application, et cela après la prise de la première photo. J'ai donc laissé tomber et déplacé les modifications de mise en page vers la MainActivity.

Dans CameraPreview j'ai changé prSupportedPreviewSizes et getOptimalPreviewSize() en public afin que je puisse l'utiliser dans MainActivity . Ensuite, il me fallait les dimensions d'affichage (moins la barre de navigation/d'état s'il y en a une) et choisir la taille optimale de la caméra . J'ai essayé d'obtenir la taille de RelativeLayout (ou FrameLayout) au lieu de la taille d'affichage, mais la valeur renvoyée était zéro. Cette solution n'a pas fonctionné pour moi. La mise en page a sa valeur après onWindowFocusChanged (cochée dans le journal).

J'ai donc mes méthodes pour calculer les dimensions de la mise en page pour correspondre au rapport d'aspect de la taille de la caméra choisie. Maintenant, il vous suffit de définir LayoutParams de la présentation de votre aperçu de caméra. Changez la largeur, la hauteur et centrez-la dans le parent.

Il y a deux choix pour calculer les dimensions de l'aperçu. Soit vous voulez qu’il corresponde à l’écran avec des barres noires (si windowBackground est défini sur null) sur les côtés ou en haut/en bas. Ou vous voulez que l'aperçu soit agrandi en plein écran . J'ai laissé un commentaire avec plus d'informations dans calcCamPrevDimensions().

10
Will Neithan

Salut la getOptimalPreview () qui est ici n'a pas fonctionné pour moi alors je veux partager ma version:

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

Juste pour rendre ce fil plus complet j'ajoute ma version de réponse:

Ce que je voulais réaliser: la vue de surface ne doit pas être étirée, elle doit couvrir l’ensemble de l’écran. De plus, mon application ne comportait qu’un mode paysage.

Solution:

La solution est une extension extrêmement petite de la solution de F1sher:

=> La première étape consiste à intégrer la solution de F1sher.

=> Maintenant, il pourrait y avoir un scénario dans la solution de F1sher lorsque la vue de surface ne couvre pas la totalité de l'écran. La solution consiste à rendre la vue de surface supérieure aux dimensions de l'écran afin qu'elle couvre la totalité de l'écran, pour cela:

    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  */
2
Sarthak Mittal

j'ai essayé toute la solution ci-dessus, mais aucune d'entre elles ne fonctionne pour moi. Enfin, je l'ai résolu moi-même et trouve que c'est assez facile. il faut faire attention à deux points.

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

this previewSize doit être l’une des résolutions prises en charge par la caméra, comme indiqué ci-dessous:

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

habituellement, l'une des tailles rawSupportedSize est égale à la résolution du périphérique.

Deuxièmement, placez votre SurfaceView dans un FrameLayout et définissez la hauteur et la largeur de la disposition de la surface dans la méthode surfaceChanged comme ci-dessus.

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

Ok, les choses sont faites, espérons que cela pourrait vous aider.

1
SalutonMondo

J'ai compris quel est le problème - c'est avec orientation changements. Si vous modifiez l'orientation de la caméra à 90 ou 270 degrés, il vous faudra permuter la largeur et la hauteur des tailles prises en charge et tout ira bien.

De plus, la vue de surface devrait se situer dans une disposition du cadre et avoir une gravité centrale.

Voici un exemple sur 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;
    }
}

Veillez à ce que les paramètres de la caméra soient définis en tant que taille d'origine (non échangés) et que la taille de la vue de surface soit échangée.

1
Alexander Danilov

Mes exigences sont l'aperçu de la caméra doit être en plein écran et conserver le rapport d'aspect. La solution de Hesam et Yoosuf était excellente, mais je vois un problème de zoom élevé pour une raison quelconque.

L'idée est la même: placer l'aperçu du centre du conteneur dans parent et augmenter la largeur ou la hauteur en fonction des rapports d'aspect jusqu'à ce qu'il puisse couvrir la totalité de l'écran.

Une chose à noter est que la taille de l'aperçu est en mode paysage car nous définissons l'orientation de l'affichage.

camera.setDisplayOrientation (90);

Le conteneur auquel nous allons ajouter la vue SurfaceView:

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

Ajoutez l'aperçu à son conteneur avec center in parent dans votre activité.

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

Dans la 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);
}
0
Howard Lin