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
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
Best Way to Convert an Arraylist to a String
How to Parse a Dynamic JSON Key in a Nested JSON Result
Android Studio Google Jar File Causing Gc Overhead Limit Exceeded Error
How to Change Menu Item Text Dynamically in Android
How to Delete Firebase Data After "N" Days
Wait Until Tomcat Finishes Starting Up
Windows: Moving a File That Was Previously Mapped in Memory Fails
"Java.Sql.Sqlexception: I/O Error: Connection Reset" in Linux Server
In Linux, How to Execute Java Jar File with External Jar Files
Hadoop Hdfs Showing Ls: '/Home/Hduser/Input/': No Such File or Directory Error
Deserialize a List≪T≫ Object With Gson
Android Listview Selected Item Stay Highlighted
Get Battery Level and State in Android
Sqlite in Android How to Update a Specific Row
Division in Java Always Results in Zero (0)
How to Support Both Ipv4 & Ipv6 on Java