web-dev-qa-db-fra.com

Comment puis-je définir la taille de l'aperçu de la caméra au format carré dans une SurfaceView carrée (comme Instagram)

J'essaie de développer ma propre activité de caméra, mais j'ai un problème que je n'arrive pas à résoudre ...

Ce que je veux, c'est quelque chose de très similaire au cadre photo instagram, et voici ce que j'obtiens:

My image

Quand devrais-je obtenir quelque chose comme ça:

Instagram image

et...

My second image

quand je devrais obtenir quelque chose comme:

Instagram 2

Je pense que je gère bien l'aperçu de SurfaceView et de la caméra, en utilisant uniquement

Camera.Parameters parameters = camera.getParameters();
camera.setDisplayOrientation(90);

et SurfaceView personnalisé:

public class SquaredSurfaceView extends SurfaceView {

private int width;
private int height;

public SquaredSurfaceView(Context context) {
    super(context);
}

public SquaredSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public SquaredSurfaceView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    width = MeasureSpec.getSize(widthMeasureSpec);
    height = width;
    setMeasuredDimension(width, width);
}

public int getViewWidth() {
    return width;
}

public int getViewHeight() {
    return height;
}

}

Qu'est-ce que je fais mal ?? :-(

46
cesards

Comme indiqué précédemment, vous devez trouver la taille d'aperçu correcte (celle avec le rapport d'aspect 1: 1) et vous devez probablement utiliser FrameLayout pour SurfacePreview. Il semble que vous ayez un problème de rapport d'aspect, peut-être avez-vous la bonne taille d'aperçu, mais vous le placez dans une disposition incorrecte.

Une autre solution pourrait être (tout comme Instagram) de rendre votre appareil photo en taille réelle, puis de masquer certaines zones de la mise en page juste pour le faire ressembler à un carré. Ensuite, par logiciel, il faudrait couper l'image pour en faire un vrai carré.

J'espère que cela vous aide

19
xramos

La solution qui fonctionne pour moi est la 2e réponse, mais parce que nous devons faire pivoter la caméra de 90º, il est nécessaire de commuter la LARGEUR avec la HAUTEUR, quelque chose comme ça ...

   camera.setDisplayOrientation(90);
   Camera.Parameters params= camera.getParameters();
   surfaceView.getLayoutParams().width=params.getPreviewSize().height;
   surfaceView.getLayoutParams().height=params.getPreviewSize().width;

J'espère que cette solution vous aidera !! :RÉ

6
Ycastan

J'ai eu le même problème avec une vue carrée - l'a résolu simplement en réglant la taille de la caméra SurfaceView sur la taille de la vue où je voulais la dessiner. Pas de calculs compliqués et ça marche pour moi. Voir ma réponse ici pour toute la méthode: https://stackoverflow.com/a/39430615/5181489

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        ...
        params.setPreviewSize(viewParams.height, viewParams.width);
        camera.setParameters(params);
2
Martin Šuráb

ma solution est plus comme la construction d'un masque carré, puis pour le placer sur la surface d'aperçu.

Vous aurez besoin de 3 choses principalement, d'abord un composant de cadre carré. J'ai créé un composant personnalisé:

package com.example.squaredviewer;

import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.Display;
import Android.view.WindowManager;
import Android.widget.RelativeLayout;

/**
* Created by yadirhb on 14-08-2015.
*/
public class SquaredFrame extends RelativeLayout{
    public SquaredFrame(Context context, AttributeSet attrs) {
         super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
        setMeasuredDimension(size, size);
    }
}

En fonction de la version Android de l'API pour laquelle vous développez, vous devrez peut-être ajouter une autre surcharge de constructeur. Pour KitKat, c'est très bien.

La deuxième étape consiste à créer la mise en page:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="horizontal"
Android:visibility="visible">

<RelativeLayout
    Android:id="@+id/camera_preview"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentLeft="false"
    Android:layout_alignParentTop="false"
    Android:layout_centerInParent="true"
    Android:background="#ffffff">

</RelativeLayout>

<LinearLayout
    Android:orientation="horizontal"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <LinearLayout
        Android:orientation="vertical"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_weight="1"
        Android:background="#131008">

    </LinearLayout>

    <com.example.squaredviewer.SquaredFrame
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_centerInParent="true"></com.example.squaredviewer.SquaredFrame>

    <LinearLayout
        Android:orientation="vertical"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_weight="1"
        Android:background="#131008" />
</LinearLayout>
</RelativeLayout>

Notez que le RelativeLayout "camera_preview" est celui utilisé pour rendre l'aperçu, il est centré et a un LinearLayout qui contient le composant carré. Il s'agit en fait du "masque" et il couvre l'aperçu de la caméra. Notez également qu'à l'exception du SquaredFrame, qui est transparent, les deux autres sont colorés en arrière-plan en noir.

Maintenant, la vue de la surface, pour l'aperçu de la caméra dans lequel la surface est dimensionnée selon le rapport d'aspect.

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private final String TAG = "PIC-FRAME";

    private SurfaceHolder mHolder;
    private Camera mCamera;
    private Display display;

    public CameraPreview(Activity context, Camera camera) {
        super(context);
        mCamera = camera;
        display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

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

        setKeepScreenOn(true);
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
        this.getHolder().removeCallback(this);
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int 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;
        }

        try {
            // stop preview before making changes
            mCamera.stopPreview();
            // set preview size and make any resize, rotate or
            // reformatting changes here
            // Now that the size is known, set up the camera parameters and begin
            // the preview.
            Camera.Parameters parameters = mCamera.getParameters();
            // You need to choose the most appropriate previewSize for your app
            Camera.Size previewSize = parametes.getSupportedPreviewSizes().get(0);
            parameters.setPreviewSize(previewSize.width, previewSize.height);


             // start preview with new settings
             mCamera.setParameters(parameters);

             // Set the holder size based on the aspect ratio
             int size = Math.min(display.getWidth(), display.getHeight());
             double ratio = (double) previewSize.width / previewSize.height;

             mHolder.setFixedSize((int)(size * ratio), size);
             mCamera.setPreviewDisplay(mHolder);
             mCamera.startPreview();

         } catch (Exception e) {
             Log.d(TAG, "Error starting camera preview: " + e.getMessage());
         }
      }
   }
}

Maintenant, tout doit être lié dans la classe d'activité

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_picture_taker);

    mDecorView = getWindow().getDecorView();

    //mCamera = a camera instance;

    // Create our Preview view and set it as the content of our activity.
    mPreview = new CameraPreview(this, mCamera);

    //Layout where camera preview is shown.
    RelativeLayout preview = (RelativeLayout) findViewById(R.id.camera_preview);
    //FrameLayout stack controllers inside and superpose them.
    preview.addView(mPreview, 0);

    // TODO
}

Un peu long, mais j'espère que ce sera utile pour plus d'un. :-)

2
YadirHB