Question

What I want to do is to draw a line that will follow my finger. I've created a custom view, and I have an onTouchEvent() that works.

I can draw a static line in the onDraw() method without much trouble.

I'm not really sure how to get the line to draw as my finger moves though.

  public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            Log.e(TAG, " - DOWN -");
            Log.e(TAG, " getX: " + event.getX());
            break;
        }
        case MotionEvent.ACTION_UP: {
            Log.e(TAG, " - UP -");
            Log.e(TAG, " getX: " + event.getX());
            break;
        }
        }
        return true;
    }

Any hints you guys who have been doing it a while can give?

Do I need to set coordinates on the onTouchEvent() and constantly invalidate the view so the little line segments draw?

In the end I just want to be able to basically doodle on the screen using my finger for this experiment.

Was it helpful?

Solution

You're only tracking the up and down events. Track the ACTION_MOVE event too. Beware that it will track continuously, even if the person's finger isn't apparently moving. Your code should go something like this:

ACTION_DOWN: Store position.

ACTION_MOVE: If position is different from stored position then draw a line from stored position to current position, and update stored position to current.

ACTION_UP: Stop.

In the ACTION_MOVE bit, it might be a good idea to check if the position is at least 2 or 3 pixels away from the stored position. If you're going to store all of the plot points, so you can do something with the data later, then maybe increase that to 10 pixels so you don't end up with hundreds of points for a simple line.

OTHER TIPS

Here's what I ended up doing. Hopefully this helps some other beginners out there get started.

I have a Sprite class that represents the object I want to move on the screen:

   public class Sprite {
    private final String TAG = "Sprite";
    private Drawable drawable;
    private int x; // the X coordinate
    private int y; // the Y coordinate
    private boolean touched; // if droid is touched/picked up
    private Speed speed; // the speed with its directions

    public Sprite(Drawable drawable, int x, int y) {
        this.drawable = drawable;
        this.x = x;
        this.y = y;
        this.speed = new Speed();
    }

    public void draw(Canvas canvas) {
        drawable.setBounds(new Rect(x, y, x+drawable.getIntrinsicWidth(), y+drawable.getIntrinsicHeight()));
        drawable.draw(canvas);
    }

    public void move() {
        if (!touched) {
            x += (speed.getXv() * speed.getxDirection());
            y += (speed.getYv() * speed.getyDirection());
        }
    }

    public void handleActionDown(int eventX, int eventY) {
        if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth() / 2))) {
            if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) {
                // droid touched
                setTouched(true);
            } else {
                setTouched(false);
            }
        } else {
            setTouched(false);
        }
    }
}

Then I have a main game loop. That loops through and calls my mainPanel's render and update methods which look like this:

    public void render(Canvas canvas) {
    canvas.drawColor(Color.BLACK);
    sprite.draw(canvas);
}

public void update() {
    sprite.move();
}

The position of where the sprite will move is handled in the motion event capture:

        if (event.getAction() == MotionEvent.ACTION_MOVE) {
        // the gestures
        if (sprite.isTouched()) {
            sprite.setX((int) event.getX());
            sprite.setY((int) event.getY());
        }
    }

Hopefully that is helpful. If I trimmed out too much and there is something you don't get let me know.

Next step, making the object follow the line!

A touch event is associated with a list of pointer counts retrievable like the following:

     int p = event.getPointerCount();

iterating over these and drawing points can cause a continuous line to appear

if (event.getAction() == MotionEvent.ACTION_MOVE
    || event.getAction() == MotionEvent.ACTION_DOWN) {

  int p = event.getPointerCount();
     for (int i = 0; i < p; i++) { 
       c.drawPoint(event.getX(i), event.getY(i), paint);
     }
}

Assuming paint is already set and c is the canvas, which may need to be locked (Eg- in a multithread application), prior to drawing on it.

For newbies this code will help you to create doodle image and export it into Png image here Is the Complete CODE and this the Activity Class With contains a View class too..

public class MainActivity extends Activity {
    private Bitmap DrawBitmap;
    private Canvas mCanvas;
    private Path mPath;
    private Paint DrawBitmapPaint;
    RelativeLayout Rl;
    CustomView View;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View = new CustomView(this);
        Rl = (RelativeLayout) findViewById(R.id.Rel);
        Rl.addView(View);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(getResources()
                .getColor(android.R.color.holo_green_dark));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(20);

    }

    private Paint mPaint;

    public class CustomView extends View {

        @SuppressWarnings("deprecation")
        public CustomView(Context c) {

            super(c);
            Display Disp = getWindowManager().getDefaultDisplay();
            DrawBitmap = Bitmap.createBitmap(Disp.getWidth(), Disp.getHeight(),
                    Bitmap.Config.ARGB_4444);

            mCanvas = new Canvas(DrawBitmap);

            mPath = new Path();
            DrawBitmapPaint = new Paint(Paint.DITHER_FLAG);
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            setDrawingCacheEnabled(true);
            canvas.drawBitmap(DrawBitmap, 0, 0, DrawBitmapPaint);
            canvas.drawPath(mPath, mPaint);
            canvas.drawRect(mY, 0, mY, 0, DrawBitmapPaint);
        }

        private float mX, mY;
        private static final float TOUCH_TOLERANCE = 4;

        private void touch_start(float x, float y) {
            mPath.reset();
            mPath.moveTo(x, y);
            mX = x;
            mY = y;
        }

        private void touch_move(float x, float y) {
            float dx = Math.abs(x - mX);
            float dy = Math.abs(y - mY);
            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
                mX = x;
                mY = y;
            }
        }

        private void touch_up() {
            mPath.lineTo(mX, mY);

            mCanvas.drawPath(mPath, mPaint);

            mPath.reset();
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
            }
            return true;
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        mPaint.setXfermode(null);
        switch (item.getItemId()) {
        case R.id.erase: 
               mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
               break;
        case R.id.DELETE: 
             View =  new CustomView(this);
               break;
        case R.id.draw: 
               mPaint.setXfermode(null);

               break;
        case R.id.Save:
            String pattern = "mm ss";
            SimpleDateFormat formatter = new SimpleDateFormat(pattern);
            String time = formatter.format(new Date());
            String path = ("/d-codepages" + time + ".png");

            File file = new File(Environment.getExternalStorageDirectory()
                    + path);

            try {
                DrawBitmap.compress(Bitmap.CompressFormat.PNG, 100,
                        new FileOutputStream(file));
                Toast.makeText(this, "File Saved ::" + path, Toast.LENGTH_SHORT)
                        .show();
            } catch (Exception e) {
                Toast.makeText(this, "ERROR" + e.toString(), Toast.LENGTH_SHORT)
                        .show();
            }

        }
        return super.onOptionsItemSelected(item);
    }

}

Also check out the Java Path class. You can use this to draw path...as you move your finger across the screen. with every update(however you implement this - every so many pixels from the last update for example) you add the x,y coordinate to your Path and re-render the total path through a loop. just an idea that I am playing around with now.

Its been a while but this post still gets some views so I figured i'll post some helpful stuff:

Tutorial on how to make an object follow a line: http://www.rengelbert.com/tutorial.php?id=182

This is a good free game engine that the above tutorial uses also: http://libgdx.badlogicgames.com/

Hope this helps someone out!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top