Android SDK - camera2 - Draw rectangle over TextureView

17,962

Solution 1

  1. First, for the camera2 api android example there is open source google sample code. https://github.com/googlesamples/android-Camera2Basic
  2. Second, for the part where you want to draw a rectangle(touch to focus), follow these steps -
    • Create a custom class extending SurfaceView.
    • Call the onTouchListener method in this custom class to detect finger coordinates and draw your rectangle using paint class in android. -Add this custom class above your TextureView that is displaying preview from camera2.
    • Turn the custom view transparent.
    • When you touch at some point on device screen then onTouchListener will be called which will draw on the canvas of custom view you added above you camera preview.
    • Finally, clear your canvas so you don't keep adding rectangles to the custom view since you only need one rect at one point of time.
    • Also, if not touched for some time the rectangle should disappear. Do this using a handler from custom view.

I am giving you some tested code to do this. I hope it helps you. BEST OF LUCK.

 private class CustomView extends SurfaceView {

    private final Paint paint;
    private final SurfaceHolder mHolder;
    private final Context context;

    public CustomView(Camera2BasicFragment context) {
        super(context.getActivity().getBaseContext());
        mHolder = getHolder();
        mHolder.setFormat(PixelFormat.TRANSPARENT);
        this.context = context.getActivity().getBaseContext();
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            invalidate();
            if (mHolder.getSurface().isValid()) {
                final Canvas canvas = mHolder.lockCanvas();
                Log.d("touch", "touchReceived by camera");
                if (canvas != null) {
                    Log.d("touch", "touchReceived CANVAS STILL Not Null");
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                    canvas.drawColor(Color.TRANSPARENT);
                    canvas.drawCircle(event.getX(), event.getY(), 100, paint);
                    mHolder.unlockCanvasAndPost(canvas);
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Canvas canvas1 = mHolder.lockCanvas();
                            if(canvas1 !=null){
                                canvas1.drawColor(0, PorterDuff.Mode.CLEAR);
                                mHolder.unlockCanvasAndPost(canvas1);
                            }

                        }
                    }, 1000);

                }
                mHolder.unlockCanvasAndPost(canvas);
            }
        }

        return false;
    }
}

Solution 2

For those who are looking for a full code, this sample code draws a small rectangle over the camera preview. Just add the surface view and modify the onViewCreated function in the Camera2BasicFragment project.

public class Camera2BasicFragment extends Fragment
        implements View.OnClickListener, ActivityCompat.OnRequestPermissionsResultCallback {

    SurfaceView surfaceView;

    @Override
    public void onViewCreated(final View view, Bundle savedInstanceState) {
        view.findViewById(R.id.picture).setOnClickListener(this);
        view.findViewById(R.id.info).setOnClickListener(this);
        mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture);

        surfaceView = (SurfaceView) view.findViewById(R.id.surfaceView);
        surfaceView.setZOrderOnTop(true);
        SurfaceHolder mHolder = surfaceView.getHolder();
        mHolder.setFormat(PixelFormat.TRANSPARENT);
        mHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Canvas canvas = holder.lockCanvas();
                if (canvas == null) {
                    Log.e(TAG, "Cannot draw onto the canvas as it's null");
                } else {
                    Paint myPaint = new Paint();
                    myPaint.setColor(Color.rgb(100, 20, 50));
                    myPaint.setStrokeWidth(10);
                    myPaint.setStyle(Paint.Style.STROKE);
                    canvas.drawRect(100, 100, 200, 200, myPaint);

                    holder.unlockCanvasAndPost(canvas);
                }
            }

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

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });
    }

And the fragment_camera2_basic.xml file has an additional surface view

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.example.android.camera2basic.AutoFitTextureView
            android:id="@+id/texture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true" />
        <SurfaceView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/surfaceView"/>

        <FrameLayout
            android:id="@+id/control"
            android:layout_width="match_parent"
            android:layout_height="112dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentStart="true"
            android:background="@color/control_background">



            <Button
                android:id="@+id/picture"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="@string/picture" />

            <ImageButton
                android:id="@+id/info"
                android:contentDescription="@string/description_info"
                style="@android:style/Widget.Material.Light.Button.Borderless"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|right"
                android:padding="20dp"
                android:src="@drawable/ic_action_info" />

        </FrameLayout>

    </RelativeLayout>

Solution 3

I solved it -I had to set my surface view to drawColor(Color.TRANSPARENT) with a clear flag and all was fine.

Share:
17,962

Related videos on Youtube

aescript
Author by

aescript

Updated on June 04, 2022

Comments

  • aescript
    aescript almost 2 years

    Im new to android development, and I'm finding it hard to find good examples on the camera2 api.

    Im working my way slowly through most issues, but on this one I am stuck. In the default camera, when you touch the screen to focus, it shows a rectangle of the focus area for a moment. I want to do something similar (Or in this case, the exact same thing to start off with so i can figure it out).

    I read somewhere (I think the TextureView page in the SDK docs) that you cant draw on a textureview while its being used as a camera preview - and calling the lock method will return null rather than a canvas.

    I found this online: https://github.com/commonsguy/vidtry/ But i cant get it to work. I either get errors saying my my main view cant be cast to my drawable view, or vice versa - Or my drawable view is on top and makes the screen black - Or its on the bottom and wont respond to touch events (and trying to force the performClick from the view above it casues crashes.)

    Im stuck! Can anyone give me an explanation or example of how i can draw my rectangle over the event position for a few sconds?

    Thanks!

    • fadden
      fadden almost 9 years
      Don't draw on the TextureView. Put a (mostly transparent) View on top, and draw on that.
  • David
    David almost 7 years
    Sahil Reader: can you explain how to add it ? in step one i downloaded the Camera2Basic code. can I just add this class to Camera2BasicFragment.java in the sample code from step 1?
  • insomnium_
    insomnium_ over 5 years
    Any thoughts, why SurfaceView might ignore the set transparency and be displayed as black rectangle over the TextureView? I'm trying to implement this functionality with no luck, see my question