15

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
  • 51,356
  • 5
  • 116
  • 166
aescript
  • 1,775
  • 4
  • 20
  • 30
  • 1
    Don't draw on the TextureView. Put a (mostly transparent) View on top, and draw on that. – fadden Jul 01 '15 at 23:55
  • I tried - couldnt get that to work. can you give an example? – aescript Jul 02 '15 at 03:23
  • Okay so i have another view on top of it - however i can only see my cam preview if i set this views alpha to 0 and bakcground:="#0000" but then i cant see my rectangle.... even if i try and give it alpha... Am i doing something wrong or is this SDK just terribly confusing compared to things like GL/QT – aescript Jul 02 '15 at 04:28
  • Post the `onDraw()` for the View that draws the rectangle. I assume you're clearing it to transparent black and drawing the rectangle with an opaque color? – fadden Jul 02 '15 at 04:32
  • I sovled it just before seeing your comment - Yes, i wasnt filling it with Color.TRANSPARENT so that was the issue, i didnt know that was a thing, i figured setting it to drawColor with 0 alpha would do the trick. Oh well. Thanks though! Now on to my next task ive been messing with for hours.... haha – aescript Jul 02 '15 at 06:12
  • @aescript hey, do you still have a working code for this? I also need to finger style draw on top of a textrure view.. your code might come handy – David May 26 '17 at 17:14
  • Hey David. I'm sure I do somewhere. Please let me know if you still need it. Sorry for the late response – aescript Aug 23 '17 at 03:06

3 Answers3

6
  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;
    }
}
Community
  • 1
  • 1
Sahil Deswal
  • 66
  • 2
  • 6
  • 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? – David May 26 '17 at 07:51
  • 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](https://stackoverflow.com/questions/51858755/surfaceview-over-textureview) – insomnium_ Aug 16 '18 at 06:57
4

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>
Sourav301
  • 1,259
  • 1
  • 14
  • 24
1

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

aescript
  • 1,775
  • 4
  • 20
  • 30