1

I have a SurfaceView for barcode scan (by this tutorial).

<SurfaceView
        android:id="@+id/camera_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone" />

Need to set there cropped camera preview with 1/4 height of the screen.

I'm set a view size based on screen size:

cameraView = (SurfaceView) findViewById(R.id.camera_view);

final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);

LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(metrics.widthPixels, metrics.heightPixels / 4);
        cameraView.setLayoutParams(layoutParams);

But camera preview just shrinks inside!

Here is how i set it

final BarcodeDetector barcodeDetector =
        new BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.ALL_FORMATS)
                .build();

final CameraSource cameraSource = new CameraSource
        .Builder(this, barcodeDetector)
        .setRequestedPreviewSize(metrics.widthPixels, metrics.heightPixels / 4)
        .setAutoFocusEnabled(true)
        .build();

cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                    cameraSource.start(cameraView.getHolder());
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }
        });

How can i crop camera preview? Need just a bottom 1/4 part.

Also need to add a transparent layer with rectangle viewfinder over it.

fadden
  • 51,356
  • 5
  • 116
  • 166
Andrey Rankov
  • 1,964
  • 2
  • 17
  • 33

4 Answers4

2

What you are probably looking to use is a TextureView. SurfaceViews are not designed to be cropped - the video display adjusting to the size of the SurfaceView is the expected behaviour.

On the other hand, TextureViews, which are available from API level 14+, allow you to set a .setTransform(Matrix matrix), where the matrix contains data concerning how to scale the video, and .setTranslationX(float x) and .setTranslationY(float y) to crop the video. A good example is available here.

If you absolutely must use SurfaceView, I would recommend looking into using a GLSurfaceView with a custom renderer that scales the video. A good starting point would be here.

Community
  • 1
  • 1
M.S.
  • 1,091
  • 1
  • 11
  • 27
2

I solved it with a ViewGroup.

In layout:

    <com.superup.smartshelf.barcode.BarcodeCameraPreview
        android:id="@+id/preview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.25"/>

In class:

public class BarcodeCameraPreview extends ViewGroup {

private SurfaceView mSurfaceView;

public BarcodeCameraPreview(final Context context, AttributeSet attrs) {
    super(context, attrs);

    mSurfaceView = new SurfaceView(context);
    mSurfaceView.getHolder().addCallback(new SurfaceCallback());
    addView(mSurfaceView);
}

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // crop view
        int actualPreviewWidth = getResources().getDisplayMetrics().widthPixels;
        int actualPreviewHeight = getResources().getDisplayMetrics().heightPixels;


        if (mSurfaceView != null) {
            mSurfaceView.layout(0, 0, actualPreviewWidth, actualPreviewHeight);
        }
    }

// camera methods

}

In activity:

BarcodeCameraPreview camera = (BarcodeCameraPreview) findViewById(R.id.preview);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(metrics.widthPixels, metrics.heightPixels / 4);
camera.setLayoutParams(layoutParams);
Andrey Rankov
  • 1,964
  • 2
  • 17
  • 33
1

You cannot achieve crop by shrinking the view. You can overlay the camera preview with another layout and effectively hide the upper 3/4 of the picture.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
-1

Hi I have found the ultimate solution from one of the google Codelabs projects https://codelabs.developers.google.com/codelabs/mlkit-android-translate?hl=en#0

// overlay is your surface view's id 

overlay.apply {
    setZOrderOnTop(true)
    holder.setFormat(PixelFormat.TRANSPARENT)
    holder.addCallback(object : SurfaceHolder.Callback {
        override fun surfaceChanged(
            p0: SurfaceHolder,
            format: Int,
            width: Int,
            height: Int
        ) {
        }

        override fun surfaceDestroyed(p0: SurfaceHolder) {
        }

        override fun surfaceCreated(p0: SurfaceHolder) {
            holder?.let { drawOverlay(it, 65, 15) }
        }

    })
}

//--------------Below function is to draw the cropped overlay-------------- 


private fun drawOverlay(
    holder: SurfaceHolder,
    heightCropPercent: Int,
    widthCropPercent: Int
) {
    val canvas = holder.lockCanvas()
    val bgPaint = Paint().apply {
        alpha = 160
    }
    canvas.drawPaint(bgPaint)
    val rectPaint = Paint()
    rectPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
    rectPaint.style = Paint.Style.FILL
    rectPaint.color = Color.WHITE
    val outlinePaint = Paint()
    outlinePaint.style = Paint.Style.STROKE
    outlinePaint.color = Color.WHITE
    outlinePaint.strokeWidth = 4f
    val surfaceWidth = holder.surfaceFrame.width()
    val surfaceHeight = holder.surfaceFrame.height()

    val cornerRadius = 25f
    // Set rect centered in frame
    val rectTop = surfaceHeight * heightCropPercent / 2 / 100f
    val rectLeft = surfaceWidth * widthCropPercent / 2 / 100f
    val rectRight = surfaceWidth * (1 - widthCropPercent / 2 / 100f)
    val rectBottom = surfaceHeight * (1 - heightCropPercent / 2 / 100f)
    val rect = RectF(rectLeft, rectTop, rectRight, rectBottom)
    canvas.drawRoundRect(
        rect, cornerRadius, cornerRadius, rectPaint
    )
    canvas.drawRoundRect(
        rect, cornerRadius, cornerRadius, outlinePaint
    )
    val textPaint = Paint()
    textPaint.color = Color.WHITE
    textPaint.textSize = 50F

    val overlayText = getString(R.string.overlay_help)
    val textBounds = Rect()
    textPaint.getTextBounds(overlayText, 0, overlayText.length, textBounds)
    val textX = (surfaceWidth - textBounds.width()) / 2f
    val textY = rectBottom + textBounds.height() + 15f // put text below rect and 15f padding
    canvas.drawText(getString(R.string.overlay_help), textX, textY, textPaint)
    holder.unlockCanvasAndPost(canvas)
}

enter image description here

Md Adilur Rashid
  • 700
  • 7
  • 13