Here is code working perfect for me draw arrow head while drawing line on canvas
package com.example.canvasexample;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
public class DrawerViewArrow extends View {
private ArrayList<Path> drawingLinePath;
private ArrayList<Path> drawingArrowPath;
private ArrayList<Paint> drawingLinePaint;
private int pathIndex = 0;
private float startX = -1, startY = -1;
private float mX = -1, mY = -1;
public int arrowLength = 80;
public int arrowWidth = 45;
public int strokeWidth = 10;
public DrawerViewArrow(Context context) {
super(context);
initPath();
}
public DrawerViewArrow(Context context, @NonNull AttributeSet attrs) {
super(context, attrs);
initPath();
}
public DrawerViewArrow(Context context, @NonNull AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPath();
}
private Paint initPaint() {
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(strokeWidth);
return mPaint;
}
private void initPath() {
drawingLinePath = new ArrayList<>();
drawingArrowPath = new ArrayList<>();
drawingLinePath.add(new Path());
drawingArrowPath.add(new Path());
drawingLinePaint = new ArrayList<>();
drawingLinePaint.add(initPaint());
pathIndex++;
}
private Path createPath(MotionEvent event) {
Path path = new Path();
path.moveTo(event.getX(), event.getY());
return path;
}
private void updateIndex(MotionEvent event) {
if (pathIndex == drawingLinePath.size()) {
drawingLinePath.add(createPath(event));
drawingArrowPath.add(createPath(event));
drawingLinePaint.add(initPaint());
pathIndex++;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (startX > -1 && mX > -1) {
canvas.drawLine(startX, startY, mX, mY, initPaint());
drawArrow(canvas);
}
for (int index = 0; index < pathIndex; index++) {
Path path = drawingLinePath.get(index);
Path arrow_path = drawingArrowPath.get(index);
Paint paint = drawingLinePaint.get(index);
canvas.drawPath(path, paint);
canvas.drawPath(arrow_path, paint);
}
}
private void drawArrow(Canvas canvas) {
double angle = calculateAngle(startX, startY, mX, mY);
float final_angle = (float) (180 - angle);
Path arrow_path = new Path();
Matrix arrow_matrix = new Matrix();
arrow_matrix.postRotate(final_angle, mX, mY);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX - arrowWidth, mY + arrowLength);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX + arrowWidth, mY + arrowLength);
arrow_path.lineTo(mX - (arrowWidth), mY + arrowLength);
arrow_path.transform(arrow_matrix);
canvas.drawPath(arrow_path, initPaint());
}
private void saveArrow() {
if (mX == -1 || mY == -1) {
return;
}
double angle = calculateAngle(startX, startY, mX, mY);
float final_angle = (float) (180 - angle);
Path arrow_path = drawingArrowPath.get(pathIndex - 1);
Matrix arrow_matrix = new Matrix();
arrow_matrix.postRotate(final_angle, mX, mY);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX - arrowWidth, mY + arrowLength);
arrow_path.moveTo(mX, mY);
arrow_path.lineTo(mX + arrowWidth, mY + arrowLength);
arrow_path.lineTo(mX - (arrowWidth), mY + arrowLength);
arrow_path.transform(arrow_matrix);
}
public double calculateAngle(double x1, double y1, double x2, double y2) {
double angle = Math.toDegrees(Math.atan2(x2 - x1, y2 - y1));
angle = angle + Math.ceil(-angle / 360) * 360; //Keep angle between 0 and 360
return angle;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case ACTION_UP:
actionUp(event);
break;
case ACTION_MOVE:
actionMove(event);
break;
case ACTION_DOWN:
actionDown(event);
break;
}
invalidate();
return true;
}
private void actionDown(MotionEvent event) {
updateIndex(event);
startX = event.getX();
startY = event.getY();
}
private void actionMove(MotionEvent event) {
mX = event.getX();
mY = event.getY();
}
private void actionUp(MotionEvent event) {
drawingLinePath.get(pathIndex - 1).lineTo(event.getX(), event.getY());
saveArrow();
startX = -1;
startY = -1;
mX = -1;
mY = -1;
}
}
moveTo
method to let it know where to start drawing from! I assume it create such a weird looking object because it must have drawn from (0,0). Thank you for the help! – Commonly