web-dev-qa-db-ja.com

正方形のSurfaceView(Instagramなど)でカメラのプレビューサイズを正方形のアスペクト比に設定するにはどうすればよいですか

独自のカメラアクティビティを開発しようとしていますが、解決できない問題があります...

私が欲しいのは、Instagramのフォトフレームに非常に似ているもので、これは私が得るものです:

My image

次のようなものを取得する必要がある場合:

Instagram image

そして...

My second image

次のようなものを取得する必要がある場合:

Instagram 2

私はSurfaceViewとカメラのプレビューをうまく管理していると思う、

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

およびカスタムSurfaceView:

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

}

私が間違っているのは何ですか?? :-(

46
cesards

前に述べたように、正しいプレビューサイズ(アスペクト比1:1)を見つける必要があり、おそらくSurfacePreviewにFrameLayoutを使用する必要があります。あなたが持っているようで、アスペクト比の問題はおそらく正しいプレビューサイズを持っているが、間違ったレイアウトに配置しているようです。

もう1つの解決策は、カメラをフルサイズにして、レイアウトの一部の領域を非表示にして正方形に見えるようにすることです(Instagramと同じです)。次に、ソフトウェアを使用して画像を切り取り、実際の正方形にする必要があります。

これがあなたを助けることを願っています

19
xramos

私のために働く解決策は2番目の答えですが、カメラを90°回転させる必要があるため、幅を高さで切り替えるには次のようになります...

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

このソリューションがお役に立てば幸いです!! :D

6
Ycastan

正方形のビューでも同じ問題が発生しました。SurfaceViewのカメラサイズを、描画したいビューのサイズに設定するだけで解決しました。複雑な計算は必要ありません。メソッド全体については、私の応答を参照してください: 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

私の解決策は、正方形のマスクを作成し、それをプレビューサーフェス上に配置するようなものです。

主に3つの要素が必要になります。最初は正方形のフレームコンポーネントです。カスタムコンポーネントを作成しました。

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

開発しているAPIバージョンAndroidによっては、別のコンストラクタオーバーロードを追加する必要がある場合があります。KitKatではこれで十分です。

2番目のステップは、レイアウトを作成することです。

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

RelativeLayout「camera_preview」はプレビューのレンダリングに使用されるものであり、中央に配置され、正方形コンポーネントを含むLinearLayoutを持っていることに注意してください。これは実際には「マスク」であり、カメラのプレビューをカバーします。透明なSquaredFrameを除き、他の2つは背景色が黒であることに注意してください。

ここで、アスペクト比に応じてサーフェスのサイズが変更されるカメラプレビューのサーフェスビュー。

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

アクティビティクラスですべてを結び付ける必要があります

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

少し長くなりますが、複数のユーザーに役立つことを願っています。 :-)

2
YadirHB