Android How to Draw a Smooth Line Following Your Finger

Android How to draw a smooth line following your finger

An easy solution, as you mentioned, is to simply connect the points with a straight line. Here's the code to do so:

public void onDraw(Canvas canvas) {
Path path = new Path();
boolean first = true;
for(Point point : points){
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else{
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}

make sure you change your paint from fill to stroke:

paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);

Another option is to connect the points with iterpolation using the quadTo method:

public void onDraw(Canvas canvas) {
Path path = new Path();
boolean first = true;
for(int i = 0; i < points.size(); i += 2){
Point point = points.get(i);
if(first){
first = false;
path.moveTo(point.x, point.y);
}

else if(i < points.size() - 1){
Point next = points.get(i + 1);
path.quadTo(point.x, point.y, next.x, next.y);
}
else{
path.lineTo(point.x, point.y);
}
}

canvas.drawPath(path, paint);
}

This still results in some sharp edges.

If you're really ambitious, you can start to calculate the cubic splines as follows:

public void onDraw(Canvas canvas) {
Path path = new Path();

if(points.size() > 1){
for(int i = points.size() - 2; i < points.size(); i++){
if(i >= 0){
Point point = points.get(i);

if(i == 0){
Point next = points.get(i + 1);
point.dx = ((next.x - point.x) / 3);
point.dy = ((next.y - point.y) / 3);
}
else if(i == points.size() - 1){
Point prev = points.get(i - 1);
point.dx = ((point.x - prev.x) / 3);
point.dy = ((point.y - prev.y) / 3);
}
else{
Point next = points.get(i + 1);
Point prev = points.get(i - 1);
point.dx = ((next.x - prev.x) / 3);
point.dy = ((next.y - prev.y) / 3);
}
}
}
}

boolean first = true;
for(int i = 0; i < points.size(); i++){
Point point = points.get(i);
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else{
Point prev = points.get(i - 1);
path.cubicTo(prev.x + prev.dx, prev.y + prev.dy, point.x - point.dx, point.y - point.dy, point.x, point.y);
}
}
canvas.drawPath(path, paint);
}

Also, I found that you needed to change the following to avoid duplicate motion events:

public boolean onTouch(View view, MotionEvent event) {
if(event.getAction() != MotionEvent.ACTION_UP){
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d(TAG, "point: " + point);
return true;
}
return super.onTouchEvent(event);
}

and add the dx & dy values to the Point class:

class Point {
float x, y;
float dx, dy;

@Override
public String toString() {
return x + ", " + y;
}
}

This produces smooth lines, but sometimes has to connect the dots using a loop.
Also, for long drawing sessions, this becomes computationally intensive to calculate.

Hope that helps... fun stuff to play around with.

Edit

I threw together a quick project demonstrating these different techniques, including Square's suggessted signature implementation. Enjoy: https://github.com/johncarl81/androiddraw

Android drawing a line to follow your finger

You shouldn't draw in onTouchEvent, don't store a reference to canvas, move drawLine to

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

invalidate() will call draw() for you which in turn calls onDraw().

In general: use UI events like onTouchEvent and onClick to modify the state of the view, then call invalidate() from those events and implement onDraw so it always renders the state of the object.

FingerLine.java

import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.*;

public class FingerLine extends View {
private final Paint mPaint;
private float startX;
private float startY;
private float endX;
private float endY;

public FingerLine(Context context) {
this(context, null);
}

public FingerLine(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
}

@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(startX, startY, endX, endY, mPaint);
}

@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
// Set the end to prevent initial jump (like on the demo recording)
endX = event.getX();
endY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
endX = event.getX();
endY = event.getY();
invalidate();
break;
}
return true;
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<net.twisterrob.android.view.FingerLine
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

MainActivity.java

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

Result

example

Note: I'm using <application android:theme="@style/Theme.AppCompat.Light">, but I don't think that could make a difference.

Android Canvas.drawLine not smooth, not consistent

Please check this answer out because it is similar to your question:

Android How to draw a smooth line following your finger

Please let me know if this helps!

Draw a smooth line with finger touch on iPhone

I ended up using Open GL and a particle. Good example to check out for this is Apple's GLPaint example code.



Related Topics



Leave a reply



Submit