Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing on SurfaceView

Tags:

I'm trying to draw on surfaceview using onTouch listener, but i'm getting weird drawings (the edge of the line moves on it's own) as you can see in the GIF bellow:

enter image description here

Here's my code:

public class MainActivity extends AppCompatActivity  implements SurfaceHolder.Callback {

    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;
    Canvas canvas;
    private Path path;
    Paint mPaint = new Paint();


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

        surfaceView = (SurfaceView) findViewById( R.id.surfaceView );
        surfaceHolder = surfaceView.getHolder();
        surfaceView.getHolder().addCallback( this );

        canvas = surfaceView.getHolder().lockCanvas();

        mPaint = new Paint();
        mPaint.setAntiAlias( true );
        mPaint.setDither( true );
        //  mPaint.setColor(0xff000000);
        mPaint.setStyle( Paint.Style.STROKE );
        mPaint.setStrokeJoin( Paint.Join.ROUND);
        mPaint.setStrokeCap( Paint.Cap.ROUND);
        mPaint.setStrokeWidth( 50);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d( "surfaceCreated", "surfaceCreated " );


        path = new Path();
        surfaceHolder = holder;
        surfaceView.setOnTouchListener( new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float X = event.getX();
                float Y = event.getY();
                switch (event.getActionMasked()) {
                    case MotionEvent.ACTION_DOWN:
                        Log.d( "surfaceCreated", "action down x="+X );

//                      canvas = surfaceHolder.lockCanvas();

                        path.moveTo(X,Y);



                        //  mv.touch_start(X,Y);
                        //  canvas = surfaceHolder.lockCanvas();

                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.d( "surfaceCreated", "action move  x="+X );

                        path.lineTo(X,Y);
                        break;
                    case MotionEvent.ACTION_UP:
                        Log.d( "surfaceCreated", "action up  x="+X );

                        path.lineTo(event.getX(),event.getY());

                        Canvas canvas = surfaceHolder.lockCanvas();
                        canvas.drawPath(path, mPaint);
                        path.reset();
                        surfaceHolder.unlockCanvasAndPost(canvas);

                        //  mCanvas.drawLine( downx, downy, upx, upy, mPaint );
                        break;

                }
                if(path != null){
                    Log.d( "surfaceCreated", "path is not null"+path );
                    Canvas canvas = surfaceHolder.lockCanvas();
                    canvas.drawPath(path, mPaint);
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
                return true;

            }
        });


    }

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

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
}

How i can solve the issue? and how i can make the surfaceview white, as you can see it's black at the beginning. Thank you!

like image 917
Nizar Avatar asked Sep 01 '19 02:09

Nizar


2 Answers

Try the following:

1) Background Problem:

According to:

https://developer.android.com/reference/android/view/SurfaceView

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

and based on xav's answer: Set the Background Image of a SurfaceView

In order to change your surface background color, you can place a view (overlapping the surface view) on top of the surfaceView with a surfaceHolder pixel format of transparent.

2) Weird Drawing Problem: "the edge of the line moves on it's own"

You already got your answer: Thanks to Guillaume Adam

3) Example:

MainActivity.class

public class MainActivity extends AppCompatActivity  implements SurfaceHolder.Callback {

private SurfaceView surfaceView;
private View surfaceBackground;
private Button b_change_surface_background_color;
private Button b_clear;
private Path path;
private Paint mPaint = new Paint();
private int[] colors = new int[]{Color.WHITE, Color.GREEN, Color.MAGENTA, Color.BLUE};
private int currentSurfaceBackgroundColor = Color.WHITE;

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

    b_change_surface_background_color = (Button) findViewById(R.id.b_change_surface_background_color);
    b_change_surface_background_color.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int colorIndex = new Random().nextInt(colors.length);
            currentSurfaceBackgroundColor = colors[colorIndex];
            changeSurfaceBackgroundColor(currentSurfaceBackgroundColor);
        }
    });

    surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
    surfaceView.setZOrderOnTop(true);
    surfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
    surfaceView.getHolder().addCallback(this);

    surfaceBackground = (View) findViewById(R.id.surfaceBackground);

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(Color.BLACK);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(50);
}

@SuppressLint("ClickableViewAccessibility")
@Override
public void surfaceCreated(SurfaceHolder holder) {
    path = new Path();
    surfaceView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            float X = event.getX();
            float Y = event.getY();
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    path.reset();
                    path.moveTo(X, Y);
                    break;
                case MotionEvent.ACTION_MOVE:
                    path.lineTo(X, Y);
                    break;
                case MotionEvent.ACTION_UP:
                    path.lineTo(event.getX(),event.getY());
                    Canvas canvas1 = surfaceView.getHolder().lockCanvas();
                    canvas1.drawPath(path, mPaint);
                    surfaceView.getHolder().unlockCanvasAndPost(canvas1);
                    break;

            }
            if(path != null){
                Canvas canvas = surfaceView.getHolder().lockCanvas();
                canvas.drawPath(path, mPaint);
                surfaceView.getHolder().unlockCanvasAndPost(canvas);
            }
            return true;

        }
    });


}

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

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

private void changeSurfaceBackgroundColor(@ColorInt int color) {
    if (surfaceBackground != null) {
        surfaceBackground.setBackgroundColor(color);
    }
}

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Change Surface Background Color"
    android:textAllCaps="false"
    android:layout_alignParentTop="true"
    android:id="@+id/b_change_surface_background_color">
</Button>

<SurfaceView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@id/b_change_surface_background_color"
    android:id="@+id/surfaceView">
</SurfaceView>

<View
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/surfaceBackground"
    android:layout_below="@id/b_change_surface_background_color"
    android:background="@android:color/white">
</View>

</RelativeLayout>

4) Output

output

like image 53
Brainnovo Avatar answered Sep 28 '22 18:09

Brainnovo


It may be because you reset the path before you synced the canvas with the surface view.

Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawPath(path, mPaint);
path.reset(); // move this line out
surfaceHolder.unlockCanvasAndPost(canvas);

Try to move path.reset() just before path.moveTo(X,Y).

path.reset(); // add just above moveTo
path.moveTo(X,Y);
like image 23
Guillaume Adam Avatar answered Sep 28 '22 19:09

Guillaume Adam