Drawline on canvas does not follow my fingers touch

425 Views Asked by At

I try to draw on ImageView from a file resource. Here is my code:

private Bitmap myBitmap;
private Bitmap mutableBitmap;

private Canvas canvas;
private Paint paint;
private ImageView img;

@Override
protected void onCreate (Bundle savedInstanceState) {

    super.onCreate (savedInstanceState);
    setContentView (R.layout.layout_imagefullscreen);

    paint = new Paint();
    paint.setStrokeWidth(11);
    paint.setColor(Color.YELLOW);
    paint.setStrokeJoin(Paint.Join.ROUND);   
    paint.setStrokeCap(Paint.Cap.ROUND); 

    View decorView = getWindow().getDecorView();
    int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
    decorView.setSystemUiVisibility(uiOptions);
...

    if (imageFileFullPathName != null) {
        try {
            File imgFile = new File (imageFileFullPathName);
            if (imgFile.exists ()) {
                myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
                mutableBitmap = myBitmap.copy(Bitmap.Config.ARGB_8888, true);
                canvas = new Canvas(mutableBitmap);
                img.setImageBitmap(mutableBitmap);
                Matrix m = img.getImageMatrix();
                canvas.setMatrix(m);

            }
        } catch (Exception e) {
            e.printStackTrace ();
        }
    }


    img.setOnTouchListener (new OnTouchListener() {

        float startX, startY;
        float stopX, stopY;

        @Override
        public boolean onTouch (View v, MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                startY = event.getY();
                Log.i("---> DOWN X", String.valueOf(startX));
                Log.i("---> DOWN Y", String.valueOf(startY));
                break;
            case MotionEvent.ACTION_MOVE:
                stopX = event.getRawX();
                stopY = event.getRawY();
                Log.i("---> MOVE X", String.valueOf(stopX));
                Log.i("---> MOVE Y", String.valueOf(stopY));        
                canvas.drawLine(startX, startY, stopX, stopY, paint);
                img.invalidate();     
                startX = event.getRawX();
                startY = event.getRawY();
                img.setImageBitmap(mutableBitmap);
                break;
            case MotionEvent.ACTION_UP:
                stopX = event.getRawX();
                stopY = event.getRawY();
                Log.i("---> UP X", String.valueOf(stopX));
                Log.i("---> UP Y", String.valueOf(stopY));        
                canvas.drawLine(startX, startY, stopX, stopY, paint);
                img.invalidate();     
                break;
            default:
                break;
            }
            v.performClick();
            return true;
        }
    });
}
}'

But when I move my finger on the ImageView, the line does not follow my finger, it limits only on the top left of the screen instead. I also try to change getRawX() to getX(), but this issue persists. I believe the problem is something about coordinates transformation, but could not figure how to solve it. Please help, thanks!

Layout xml file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="@color/Black">

<ImageView
    android:id="@+id/imagefullscreenview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:src="@drawable/ic_launcher"/>

</LinearLayout>
1

There are 1 best solutions below

2
Hemendra Sharma On

From your code, it looks like you are trying to drawing following the touched finger. So here is how you draw...

public class MainActivity extends Activity implements SurfaceHolder.Callback, OnClickListener, OnTouchListener {

    SurfaceView sv;
    SurfaceHolder sh;
    RenderingThread thread;
    Canvas canvas = null;
    int Width = 0, Height = 0;
    Button btnClear;

    Path path = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        try{
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            setContentView(R.layout.activity_main);

            sv = (SurfaceView)findViewById(R.id.sv);
            sv.setOnTouchListener(this);
            btnClear = (Button)findViewById(R.id.btnClear);
            path = new Path();

            sh = sv.getHolder();
            if(sh != null)
            {
                sh.addCallback(this);
            }
            else
            {
                Toast.makeText(getApplicationContext(), "Failed to initialize", Toast.LENGTH_SHORT).show();
                finish();
            }
        }catch(Throwable ex)
        {
            ex.printStackTrace();
            Toast.makeText(getApplicationContext(), "Failed to start", Toast.LENGTH_SHORT).show();
            finish();
        }
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        try{
            Width = width;
            Height = height;
            thread = new RenderingThread(holder, width, height);
            canvas = new Canvas(thread.drawingBitmap);
            thread.start();
        }catch(Throwable ex) {
            ex.printStackTrace();
            Toast.makeText(getApplicationContext(), "Failed to initialize", Toast.LENGTH_SHORT).show();
            finish();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        sv.setWillNotDraw(false);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        thread.RequestStop();
    }

    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction())
        {
        case MotionEvent.ACTION_DOWN:
            path.moveTo(event.getRawX(), event.getRawY());
            break;
        case MotionEvent.ACTION_MOVE:
            Paint paint = new Paint();
            paint.setColor(Color.RED);
            paint.setAntiAlias(true);
            paint.setColor(Color.BLUE);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);

            paint.setStrokeWidth(5);

            CornerPathEffect pathEffect = new CornerPathEffect(45);
            paint.setPathEffect(pathEffect);

            path.lineTo(event.getRawX(), event.getRawY());
            canvas.drawPath(path, paint);
            //
            Log.i("minor",""+event.getTouchMinor());
            Log.i("major",""+event.getTouchMajor());
            break;
        case MotionEvent.ACTION_UP:
            path.reset();
            break;
        }
        return true;
    }


    @Override
    public void onClick(View v) {
        switch(v.getId())
        {
        case R.id.btnClear:
            canvas.drawColor(Color.WHITE);
            break;
        }
    }
}

Here btnClear is a button in XML from which you can clear the canvas. Here is the XML

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <SurfaceView
        android:id="@+id/sv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/btnClear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Clear"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:background="#555555"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:onClick="onClick" />

</RelativeLayout>

And here is the rendering thread...

public class RenderingThread extends Thread {

    private boolean keepRunning = true;
    private SurfaceHolder holder = null;
    public Bitmap drawingBitmap = null;
    private int width = 0, height = 0;

    public RenderingThread(SurfaceHolder holder, int width, int height)
    {
        this.holder = holder;
        this.width = width;
        this.height = height;
        drawingBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    }

    public void RequestStop()
    {
        keepRunning = false;
        interrupt();
    }

    @Override
    public void run() {
        while(keepRunning)
        {
            try{
                Thread.sleep(30);
                //
                if(holder != null && drawingBitmap != null)
                {
                    Canvas canvas = holder.lockCanvas();
                    if(canvas != null)
                    {
                        canvas.drawColor(Color.WHITE);
                        Rect src = new Rect(0, 0, drawingBitmap.getWidth(), drawingBitmap.getHeight());
                        Rect dst = new Rect(0, 0, width, height);
                        canvas.drawBitmap(drawingBitmap, src, dst, new Paint());
                        //
                        holder.unlockCanvasAndPost(canvas);
                    }
                }
            }catch(Throwable ex) {
                ex.printStackTrace();
            }
        }
    }

}

Good Luck. :)