Signatures in Android - java

I want to make a class which captures a human signature for my Android app. I have this code so far:
public class DrawView extends View implements OnTouchListener {
private static final String TAG = "DrawView";
List<Point> points = new ArrayList<Point>();
Paint paint = new Paint();
long oldTime = 0;
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// paint.setColor(Color.BLACK);
// paint.setAntiAlias(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.BLACK);
}
#Override
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);
}
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();
return true;
}
return super.onTouchEvent(event);
}
public boolean checkTime() {
//Check if there was an input 400 ms ago
long newTime = System.currentTimeMillis();
long diffirence = newTime - oldTime;
if (oldTime == 0) {
oldTime = System.currentTimeMillis();
return true;
} else if (diffirence <= 400) {
oldTime = System.currentTimeMillis();
return true;
}
return false;
}
}
class Point {
float x, y;
float dx, dy;
#Override
public String toString() {
return x + ", " + y;
}
}
The problem is that the line will connect to the latest point when I start drawing again, even when I stop drawing for a while. This is of course not very useful for capturing human signatures, that's why I created the checkTime method. If you stop drawing for 400ms it returns false, when you start drawing again it will start a new line which is not connected to the old line. I only just don't know how I can implement my method, I tried a lot but the line keeps connecting to the latest point, maybe someone can help me.

It's probably faster to create the path in onTouch and only draw it in onDraw.
Replace onTouch with
Path path = new Path();
int prevx=0;
int prevy=0;
int prevdx=0;
int prevdy=0;
public boolean onTouch(View view, MotionEvent event)
{
int x = event.getX();
int y = event.getY();
int dx = (x-prevx)/3;
int dy = (y-prevy)/3;
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
path.moveTo(x, y);
}
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
path.cubicTo(prevx + prevdx, prevy + prevdy, x - dx, y - dy, x, y);
}
prevx=x;
prevy=y;
prevdx=dx;
prevdy=dy;
invalidate();
return true;
}
onDraw will be just
public void onDraw(Canvas canvas)
{
canvas.drawPath(path, paint);
}

I used this post from Lars Vogel, but many thanks to Marc Van Daele!
This is code wich paints the View:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class SingleTouchEventView extends View {
private Paint paint = new Paint();
private Path path = new Path();
public SingleTouchEventView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(6f);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
// nothing to do
break;
default:
return false;
}
// Schedules a repaint.
invalidate();
return true;
}
}
You have to call this activity to start paiting:
import android.app.Activity;
import android.os.Bundle;
public class SingleTouchActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SingleTouchEventView(this, null));
}
}

Related

Is there any way to get the endpoints of a line when using Path class?

I am building an app where a user can draw lines in a custom canvas with a finger (multiple fragments have a canvas and this works) using the Path class.
I also managed to use SharedPreferences to save and load the drawn lines but when loaded the lines start from the top left (i.e (0, 0)) and the shape has changed to lines with a slight curve at the start (I say start because I found that the line ends from where the touch started).
The start points are kept in Path but from what I can see there are no endpoints kept. Is there any way I can get the endpoints?
I have previously tried passing the required variables to another ArrayList that uses another constructor with the endpoints (found with a method used for when the finger stops touching the screen) but the drawings no longer showed on the canvas unlike before.
Edit
I have changed to finding multiple points as I believe that getting the endpoint won't be enough and have altered the shown code to show my attempt with getPosTan but no drawings get shown as the elements in testing are null for some reason, so it won't go in the else.
Update
I found that pathMeasure.getLength() produces 0.0 so it isn't going into the while and therefore resulting in null elements but I don't know why it's producing 0.0 as somePath isn't null
PaintView.java
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.EmbossMaskFilter;
import android.graphics.MaskFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
public class PaintView extends View {
public static int BRUSH_SIZE = 10;
public static final int DEFAULT_COLOR = Color.WHITE;
public static int DEFAULT_BG_COLOR = Color.GRAY;
private static final float TOUCH_TOLERANCE = 4;
private float mX, mY;
private Path mPath;
private Paint mPaint;
private static ArrayList<FingerPath> paths = new ArrayList<>();
private int currentColor;
private int backgroundColor = DEFAULT_BG_COLOR;
private int strokeWidth;
private boolean emboss;
private boolean blur;
private MaskFilter mEmboss;
private MaskFilter mBlur;
private Bitmap mBitmap;
public Canvas mCanvas;
private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
public PaintView(Context context) {
this(context, null);
}
public PaintView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(DEFAULT_COLOR);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setXfermode(null);
mPaint.setAlpha(0xff);
mEmboss = new EmbossMaskFilter(new float[] {1, 1, 1}, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(5, BlurMaskFilter.Blur.NORMAL);
}
public ArrayList getPaths() {
return paths;
}
public ArrayList setPaths(ArrayList<FingerPath> list) {
return this.paths = list;
}
public void init(DisplayMetrics metrics) {
int height = metrics.heightPixels;
int width = metrics.widthPixels;
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
loadDrawing(mCanvas);
currentColor = DEFAULT_COLOR;
strokeWidth = BRUSH_SIZE;
}
public void normal() {
emboss = false;
blur = false;
}
public void clear() {
backgroundColor = DEFAULT_BG_COLOR;
paths.clear();
normal();
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
mCanvas.drawColor(backgroundColor);
for (FingerPath fp : paths) {
mPaint.setColor(fp.color);
mPaint.setStrokeWidth(fp.strokeWidth);
mPaint.setMaskFilter(null);
if (fp.emboss)
mPaint.setMaskFilter(mEmboss);
else if (fp.blur)
mPaint.setMaskFilter(mBlur);
mCanvas.drawPath(fp.path, mPaint);
}
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.restore();
}
private void touchStart(float x, float y) {
mPath = new Path();
FingerPath fp = new FingerPath(currentColor, emboss, blur, strokeWidth, mPath, x, y);
paths.add(fp);
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touchMove(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 touchUp() {mPath.lineTo(mX, mY);}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE :
touchMove(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP :
touchUp();
invalidate();
break;
}
return true;
}
private FloatPoint[] getPoint(Path somePath, float x, float y){
FloatPoint[] pointArray = new FloatPoint[20];
PathMeasure pathMeasure = new PathMeasure(somePath, false);
float length = pathMeasure.getLength();
float distance = 0f;
float speed = length / 20;
int counter = 0;
float[] aCoordinates = {x, y};
while ((distance < length) && (counter < 20)) {
pathMeasure.getPosTan(distance, aCoordinates, null);
pointArray[counter] = new FloatPoint(aCoordinates[0],
aCoordinates[1]);
counter++;
distance = distance + speed;
}
return pointArray;
}
public void loadDrawing(Canvas canvas) {
if (mCanvas != null) {
currentColor = DEFAULT_COLOR;
strokeWidth = BRUSH_SIZE;
if (! paths.isEmpty()) {
canvas.save();
mCanvas.drawColor(backgroundColor);
for (FingerPath fp : paths) {
mPaint.setColor(fp.color);
mPaint.setStrokeWidth(fp.strokeWidth);
mPaint.setMaskFilter(null);
if (fp.emboss)
mPaint.setMaskFilter(mEmboss);
else if (fp.blur)
mPaint.setMaskFilter(mBlur);
FloatPoint[] testing = getPoint(fp.path, fp.x, fp.y);
//need to figure out how to for loop testing
float sectionTestX = 0.0f;
float sectionTestY = 0.0f;
for (FloatPoint testingPoint : testing) {
if (sectionTestX == 0.0f && sectionTestY == 0.0f) {
sectionTestX = testingPoint.getX();
sectionTestY = testingPoint.getY();
continue;
} else {
fp.path.quadTo(sectionTestX, sectionTestY,
testingPoint.getX(), testingPoint.getY());
sectionTestX = testingPoint.getX();
sectionTestY = testingPoint.getY();
}
}
/*xTest = fp.x;
yTest = fp.y;*/
}
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.restore();
}
}
}
}
FloatPoint.java
public class FloatPoint {
static float x, y;
public FloatPoint(float x, float y) {
this.x = x;
this.y = y;
}
public static float getX() {
return x;
}
public static float getY() {
return y;
}
}

How can I draw multiple circles in my canvas?

I can draw one circle but my problem is to know how can I draw multiple circles every time I clicked.
The center of the circle has to be the X and the Y coordinates that are clicked and I need to store that point on my Vector, and the vector cannot be destroyed everytime I click the View.
If I don't have the invalidate on the end of if (figure == 1) it doesn't draw the circle at all.
My center is a Point2D which has int x, int y stored.
package com.example.soalr.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.Vector;
public class MyView extends View {
Paint paint = null;
int figure;
int lados_poly;
int cor;
int deletar;
Circulo cir;
Circulo raio;
int CursorX, CursorY;
int nrCliques;
Vector<Ponto2D> ptsCirc = new Vector<Ponto2D>();
public MyView(Context context) {
super(context);
paint = new Paint();
figure = 0;
cor = 0;
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setStrokeWidth(10);
figure = 0;
cor = 0;
Ponto2D centroCirc = new Ponto2D();
centroCirc.x = CursorX;
centroCirc.y = CursorY;
cir = new Circulo(centroCirc);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
figure = 0;
cor = 0;
}
public void clickEcra() {
setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
nrCliques++;
case MotionEvent.ACTION_UP:
CursorX = (int)event.getX();
CursorY = (int)event.getY();
if (figure == 1) {
Ponto2D centroCirc = new Ponto2D();
centroCirc.x = CursorX;
centroCirc.y = CursorY;
ptsCirc.add(centroCirc);
invalidate();
}
default:
return false;
}
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
clickEcra();
Ponto2D p3 = new Ponto2D();
p3.x = 600;
p3.y = 150;
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#F5F1E0"));
canvas.drawPaint(paint);
paint.setColor(Color.parseColor("#3F5866"));
//cores
if (cor == 1) {
paint.setColor(Color.parseColor("#393E46"));
} else if (cor == 2){
paint.setColor(Color.parseColor("#00ADB5"));
} else if (cor == 3) {
paint.setColor(Color.parseColor("#F8B500"));
} else if (cor == 4) {
paint.setColor(Color.parseColor("#FC3C3C"));
}
//figuras
if (figure == 1) {
if (ptsCirc.size() > 0) {
for (int a = 0; a <= ptsCirc.size(); a++) {
Circulo raio = new Circulo(ptsCirc.get(a));
raio.radius = 100;
canvas.drawCircle(ptsCirc.get(a).x, ptsCirc.get(a).y, raio.radius, paint);
}
}
}
else if (figure == 2) {
clickEcra();
Ponto2D p1 = new Ponto2D();
Ponto2D p2 = new Ponto2D();
Reta retinha = new Reta(p1, p2);
if (nrCliques == 1) {
p1.x = CursorX;
p1.y = CursorY;
System.out.println("x1: " + p1.x + " y1: " + p1.y);
} else if (nrCliques == 2) {
p2.x = CursorX;
p2.y = CursorY;
System.out.println("x2: " + p2.x + " y2: " + p2.y);
}
System.out.println("x2_: " + retinha.pfinal.x + " y2_: " + retinha.pfinal.y);
System.out.println("x1_: " + retinha.pinicial.x + " y1_: " + retinha.pinicial.y);
canvas.drawLine(retinha.pinicial.x, retinha.pinicial.y, retinha.pfinal.x, retinha.pfinal.y, paint);
} else if (figure == 3) {
Poligono poly = new Poligono(lados_poly);
if (lados_poly >= 3) {
for (int i = 0; i <= lados_poly - 1; i++) {
Ponto2D ponto4 = new Ponto2D(poly.pontosPolig.get(i).x, poly.pontosPolig.get(i).y);
Ponto2D ponto5 = new Ponto2D();
if (i < lados_poly) {
ponto5.x = poly.pontosPolig.get(i + 1).x;
ponto5.y = poly.pontosPolig.get(i + 1).y;
Reta retaPoli = new Reta(ponto4, ponto5);
int var_lados = lados_poly - 1;
if (i != var_lados) {
canvas.drawLine(retaPoli.pinicial.x, retaPoli.pinicial.y, retaPoli.pfinal.x, retaPoli.pfinal.y, paint);
} else {
canvas.drawLine(poly.pontosPolig.firstElement().x, poly.pontosPolig.firstElement().y, retaPoli.pinicial.x, retaPoli.pinicial.y, paint);
}
}
}
}
}
if (deletar == 2){
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#F5F1E0"));
canvas.drawPaint(paint);
nrCliques = 0;
}
}
public void setfigure(int a) {
this.figure = a;
}
public void Cor1_mudar(int text_cor) {
this.cor = text_cor;
}
public void verLados (int lados){
this.lados_poly = lados;
}
public void Resetar(int delete){
this.deletar = delete;
}
}
This is Circle.java:
package com.example.soalr.myapplication;
public class Circulo {
int radius;
Ponto2D centro;
public Circulo(Ponto2D c) {
radius = 0;
centro = c;
}
}
You have two problems here:
You only see 1 circle. This is probably because nrCliques is not the value you expect. Instead of iterating from 0 to nrCliques, consider iterating from 0 to ptsCirc.size(), or using a for..each loop. For problems like this, the debugger is your friend.
You don't see any circles unless you have that invalidate() call in onDraw(). Calling invalidate() from onDraw() causes the framework to call onDraw() repeatedly, as fast as it can. This doesn't hurt anything but it uses a lot of processor power for no good reason. It's better to "cue" your View to redraw at the moment that its attendant data changes. In this case, this means putting the invalidate() call after that call to ptsCirc.add(). Then, you'll only get roughly 1 call to onDraw() each time you tap the screen.
This is working code, the problem was I wasn't verifying if the Vector had anything inside him.
public class MyView extends View {
Paint paint = null;
int figure;
int lados_poly;
int cor;
int deletar;
int CursorX, CursorY;
int nrCliques;
Vector<Ponto2D> ptsCirc = new Vector<Ponto2D>();
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
figure = 0;
cor = 0;
}
public void clickEcra() {
setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
CursorX = (int)event.getX();
CursorY = (int)event.getY();
if (figure == 1) {
Ponto2D centroCirc = new Ponto2D();
centroCirc.x = CursorX;
centroCirc.y = CursorY;
ptsCirc.add(centroCirc);
invalidate();
}
default:
return false;
}
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
clickEcra();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#F5F1E0"));
canvas.drawPaint(paint);
paint.setColor(Color.parseColor("#3F5866"));
//cores
if (cor == 1) {
paint.setColor(Color.parseColor("#393E46"));
} else if (cor == 2){
paint.setColor(Color.parseColor("#00ADB5"));
} else if (cor == 3) {
paint.setColor(Color.parseColor("#F8B500"));
} else if (cor == 4) {
paint.setColor(Color.parseColor("#FC3C3C"));
}
//figuras
if (figure == 1) {
if (ptsCirc.size() > 0) {
for (int a = 0; a < ptsCirc.size(); a++) {
Circulo raio = new Circulo(ptsCirc.get(a));
raio.radius = 100;
canvas.drawCircle(ptsCirc.get(a).x, ptsCirc.get(a).y, raio.radius, paint);
}
}
}
if (deletar == 2){
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#F5F1E0"));
canvas.drawPaint(paint);
nrCliques = 0;
ptsCirc.removeAllElements();
ptsReta.removeAllElements();
}
}
public void setfigure(int a) {
this.figure = a;
}
public void Cor1_mudar(int text_cor) {
this.cor = text_cor;
}
public void Resetar(int delete){
this.deletar = delete;
}
}

Drag a rectangle on Canvas Android

I'm drawing a rectangle on a Canvas in Android. How do I drag it around on the screen? I have an onTouchEvent() method in my class that listens to MotionEvent(s). How do I use this to drag my rectangle around while preserving it's size.
My code is as follows:
public class CustomSquareView3 extends View {
private Rect rect;
private Paint paint;
public static int rotation = 45;
Canvas canvas;
int x1 = 200;
int x2 = 400;
int y1 = 200;
int y2 = 400;
public CustomSquareView3(Context context){
super(context);
int x = 50;
int y = 50;
int sideLength = 200;
rect = new Rect(x1,y1, x2, y2);
paint = new Paint();
paint.setColor(Color.GRAY);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.save();
canvas.rotate(rotation, (x1+x2)/2, (y1+y2)/2);
canvas.drawRect(rect, paint);
Paint paint2 = new Paint();
paint2.setColor(Color.WHITE);
paint2.setTextSize(50);
canvas.drawText("Hi", (x1+x2)/2, (y1+y2)/2, paint2);
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int motionEvent = event.getAction();
float xMove = event.getX();
float yMove = event.getY();
switch (motionEvent){
case MotionEvent.ACTION_DOWN : {
x1 = (int) event.getX();
y1 = (int) event.getY();
rect = new Rect(x1, y1, x2, y2);
break;
}
case MotionEvent.ACTION_MOVE : {
x1 = (int) event.getX();
y1 = (int) event.getY();
rect = new Rect(x1, y1, x2, y2);
break;
}
case MotionEvent.ACTION_UP : {
x1 = (int) event.getX();
y1 = (int) event.getY();
rect = new Rect(x1, y1, x2, y2);
break;
}
default:
return false;
}
invalidate();
return true;
}
}
The above code resizes the rectangle every time I touch the screen. How do I get a smooth drag gesture in the code such that the size of the rectangle is maintained when it is dragged around the screen?
See this example:
class ViewPort extends View {
List<Layer> layers = new LinkedList<Layer>();
int[] ids = {R.drawable.layer0, R.drawable.layer1, R.drawable.layer2};
public ViewPort(Context context) {
super(context);
Resources res = getResources();
for (int i = 0; i < ids.length; i++) {
Layer l = new Layer(context, this, BitmapFactory.decodeResource(res, ids[i]));
layers.add(l);
}
}
#Override
protected void onDraw(Canvas canvas) {
for (Layer l : layers) {
l.draw(canvas);
}
}
private Layer target;
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
target = null;
for (int i = layers.size() - 1; i >= 0; i--) {
Layer l = layers.get(i);
if (l.contains(event)) {
target = l;
layers.remove(l);
layers.add(l);
invalidate();
break;
}
}
}
if (target == null) {
return false;
}
return target.onTouchEvent(event);
}
}
class Layer implements MatrixGestureDetector.OnMatrixChangeListener {
Matrix matrix = new Matrix();
Matrix inverse = new Matrix();
RectF bounds;
View parent;
Bitmap bitmap;
MatrixGestureDetector mgd = new MatrixGestureDetector(matrix, this);
public Layer(Context ctx, View p, Bitmap b) {
parent = p;
bitmap = b;
bounds = new RectF(0, 0, b.getWidth(), b.getHeight());
matrix.postTranslate(50 + (float) Math.random() * 50, 50 + (float) Math.random() * 50);
}
public boolean contains(MotionEvent event) {
matrix.invert(inverse);
float[] pts = {event.getX(), event.getY()};
inverse.mapPoints(pts);
if (!bounds.contains(pts[0], pts[1])) {
return false;
}
return Color.alpha(bitmap.getPixel((int) pts[0], (int) pts[1])) != 0;
}
public boolean onTouchEvent(MotionEvent event) {
mgd.onTouchEvent(event);
return true;
}
#Override
public void onChange(Matrix matrix) {
parent.invalidate();
}
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, matrix, null);
}
}
class MatrixGestureDetector {
private static final String TAG = "MatrixGestureDetector";
private int ptpIdx = 0;
private Matrix mTempMatrix = new Matrix();
private Matrix mMatrix;
private OnMatrixChangeListener mListener;
private float[] mSrc = new float[4];
private float[] mDst = new float[4];
private int mCount;
interface OnMatrixChangeListener {
void onChange(Matrix matrix);
}
public MatrixGestureDetector(Matrix matrix, MatrixGestureDetector.OnMatrixChangeListener listener) {
this.mMatrix = matrix;
this.mListener = listener;
}
public void onTouchEvent(MotionEvent event) {
if (event.getPointerCount() > 2) {
return;
}
int action = event.getActionMasked();
int index = event.getActionIndex();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
int idx = index * 2;
mSrc[idx] = event.getX(index);
mSrc[idx + 1] = event.getY(index);
mCount++;
ptpIdx = 0;
break;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < mCount; i++) {
idx = ptpIdx + i * 2;
mDst[idx] = event.getX(i);
mDst[idx + 1] = event.getY(i);
}
mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount);
mMatrix.postConcat(mTempMatrix);
if(mListener != null) {
mListener.onChange(mMatrix);
}
System.arraycopy(mDst, 0, mSrc, 0, mDst.length);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerId(index) == 0) ptpIdx = 2;
mCount--;
break;
}
}
}
You have to redo it a little. Use your rectangle instead of bitmaps.

Android - How do I call invalidate() method from another class?

I need to call invalidate() method from my MainActivity class. I can call it from my CanvasView class, but from MainActivity, it gives me an error. Says invalidate is non-static method. How do I get past this? I have searched and tried some solutions, but haven't got it to work.
I want to delete drawn items from the screen, but when I select Undo from menu list, it won't update the screen. Only when I touch the screen, it Undos elements.
My CanvasView class
package com.example.HW3;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* Android Kimp.
* #author Martin
*
*/
public class CanvasView extends View {
/**
* Rectangles.
*/
private Rect rectangle = new Rect();
/**
* These help calculating rectangle coordinates.
*/
private float d1 = 0, d2 = 0;
/**
* Canvas.
*/
private Canvas canvas = new Canvas();
/**
* Paint to draw shapes.
*/
private Paint paint = new Paint();
/**
* Paint to draw shapes.
*/
private Paint paint2 = new Paint();
/**
* Paint to draw shapes.
*/
private Paint paint3 = new Paint();
/**
* Paint to draw shapes.
*/
private Paint paint4 = new Paint();
/**
* Path.
*/
private Path path = new Path();
/**
* Point.
*/
private Point point = new Point();
/**
* List for lines.
*/
private static List<Path> lines = new ArrayList<Path>();
private static List<Path> lines2 = new ArrayList<Path>();
/**
* List for points.
*/
private static List<Point> points = new ArrayList<Point>();
private static List<Point> points2 = new ArrayList<Point>();
/**
* List for rectangles.
*/
private static List<Rect> rectangles = new ArrayList<Rect>();
private static List<Rect> rectangles2 = new ArrayList<Rect>();
private static List<Integer> elements = new ArrayList<Integer>();
private static List<Integer> elements2 = new ArrayList<Integer>();
/**
* Touch coordinates.
*/
private float x, startX, endX;
/**
* Touch coordinates.
*/
private float y, startY, endY;
/**
* This checks whether user draws a line or a point.
*/
private int whichShape;
private boolean touchStarted = false;
/**
* Enum to hold the value of DrawMode, whether user wants to draw lines or rectangles.
*
*/
public enum undoRedo {
DoUndoRedo, DoNothing
};
public static undoRedo currentState = undoRedo.DoNothing;
public enum DrawMode {
/**
* DrawMode possibilities.
*/
FreeDrawMode, RectDrawMode, SelectAndDrag
};
/**
* Sets current DrawMode, when application starts.
*/
public static DrawMode currentDrawMode = DrawMode.FreeDrawMode;
/**
* Sets DrawMode.
* #param newDrawMode
*/
final Handler handler = new Handler();
/**
* Runnable interface to detect long press.
*/
Runnable mLongPressed = new Runnable() {
public void run() {
for (int i = 0; i < getRectangles().size(); i++) {
Log.i("", "Long press!");
Rect a = getRectangles().get(i);
System.out.println(a + " " + i);
double x = getRectangles().get(i).left;
System.out.println(a + " " + x);
currentDrawMode = DrawMode.SelectAndDrag;
}
}
};
/**
* CanvasView.
* #param context
* #param attrs
*/
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(3);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint2.setAntiAlias(true);
paint2.setStrokeWidth(3);
paint2.setColor(Color.RED);
paint2.setStyle(Paint.Style.STROKE);
paint2.setStrokeJoin(Paint.Join.ROUND);
paint3.setAntiAlias(true);
paint3.setColor(Color.BLACK);
paint3.setStrokeWidth(3);
paint3.setStyle(Paint.Style.STROKE);
paint3.setStrokeJoin(Paint.Join.ROUND);
paint4.setAntiAlias(true);
paint4.setColor(Color.BLACK);
paint4.setStrokeWidth(3);
paint4.setStyle(Paint.Style.STROKE);
paint4.setShadowLayer(10,5,5,Color.BLACK);
paint4.setStrokeJoin(Paint.Join.ROUND);
}
#Override
protected void onDraw(Canvas canvas) {
for (Rect r : getRectangles()) {
canvas.drawRect(r, paint);
}
canvas.drawRect(rectangle, paint3);
for (Path p : getLines()) {
canvas.drawPath(p, paint);
}
canvas.drawPath(path, paint2);
for (Point point : getPoints()) {
canvas.drawCircle(point.x, point.y, 1, paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
handler.postDelayed(mLongPressed, 500);
}
if ((event.getAction() == MotionEvent.ACTION_MOVE)
|| (event.getAction() == MotionEvent.ACTION_UP)) {
handler.removeCallbacks(mLongPressed);
}
if (currentDrawMode == DrawMode.FreeDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set new starting point
paint2.setColor(Color.RED);
path = new Path();
path.moveTo(x, y);
touchStarted = true;
break;
case MotionEvent.ACTION_MOVE:
touchStarted = false;
path.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
if (touchStarted) {
point = new Point();
point.x = (int) x;
point.y = (int) y;
paint2.setColor(Color.BLACK);
whichShape = 1;
getElements().add(whichShape);
getPoints().add(point);
getElements2().clear();
touchStarted = false;
System.out.println("here");
} else {
System.out.println("there");
paint2.setColor(Color.BLACK);
whichShape = 2;
getElements().add(whichShape);
getLines().add(path);
getElements2().clear();
}
break;
default:
return false;
}
} else if (currentDrawMode == DrawMode.RectDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
rectangle = new Rect();
paint3.setColor(Color.RED);
startX = event.getX();
startY = event.getY();
System.out.println("down");
break;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = event.getY();
System.out.println("move");
System.out.println(startX + " " + endX + " " + startY + " " + endY);
if (startX >= endX && startY >= endY) {
rectangle.set((int) endX, (int) endY, (int) startX,
(int) startY);
canvas.drawRect(rectangle, paint2);
} else if (startX >= endX && startY <= endY) {
d1 = startX + (endX - startX);
d2 = startY + (endY - startY);
rectangle.set((int) d1, (int) startY, (int) startX,
(int) d2);
canvas.drawRect(rectangle, paint3);
} else if (startX <= endX && startY >= endY) {
d1 = startY + (endY - startY);
d2 = startX + (endX - startX);
rectangle.set((int) startX, (int) d1, (int) d2,
(int) startY);
canvas.drawRect(rectangle, paint3);
System.out.println("olen siin");
} else {
rectangle.set((int) startX, (int) startY, (int) endX,
(int) endY);
canvas.drawRect(rectangle, paint3);
}
break;
case MotionEvent.ACTION_UP:
System.out.println("up");
paint3.setColor(Color.BLACK);
whichShape = 3;
getElements().add(whichShape);
getRectangles().add(rectangle);
getElements2().clear();
break;
default:
return false;
}
} else if (currentDrawMode == DrawMode.SelectAndDrag) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
}
// Makes our view repaint and call onDraw
invalidate();
return true;
}
public static List<Integer> getElements() {
return elements;
}
public static void setElements(List<Integer> elements) {
CanvasView.elements = elements;
}
public static List<Point> getPoints() {
return points;
}
public static void setPoints(List<Point> points) {
CanvasView.points = points;
}
public static List<Path> getLines() {
return lines;
}
public static void setLines(List<Path> lines) {
CanvasView.lines = lines;
}
public static List<Rect> getRectangles() {
return rectangles;
}
public static void setRectangles(List<Rect> rectangles) {
CanvasView.rectangles = rectangles;
}
public static List<Point> getPoints2() {
return points2;
}
public static void setPoints2(List<Point> points2) {
CanvasView.points2 = points2;
}
public static List<Integer> getElements2() {
return elements2;
}
public static void setElements2(List<Integer> elements2) {
CanvasView.elements2 = elements2;
}
public static List<Path> getLines2() {
return lines2;
}
public static void setLines2(List<Path> lines2) {
CanvasView.lines2 = lines2;
}
public static List<Rect> getRectangles2() {
return rectangles2;
}
public static void setRectangles2(List<Rect> rectangles2) {
CanvasView.rectangles2 = rectangles2;
}
}
My MainActivity class
package com.example.HW3;
import com.example.HW3.CanvasView.undoRedo;
import com.example.androiddrawing.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
/**
* Android application.
* #author Martin
*
*/
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.default_drawing) {
CanvasView.currentDrawMode = CanvasView.DrawMode.FreeDrawMode;
return true;
} else if (id == R.id.draw_rectangle) {
CanvasView.currentDrawMode = CanvasView.DrawMode.RectDrawMode;
return true;
} else if (id == R.id.undo) {
int listSize = CanvasView.getElements().size();
System.out.println(CanvasView.getElements().size());
if (CanvasView.getElements().size() == 0) {
return true;
} else if (CanvasView.getElements().get(listSize - 1) == 1) {
// I WANT TO CALL INVALIDATE() FROM HERE CanvasView.getPoints2().add(CanvasView.getPoints().get(CanvasView.getPoints().size() - 1));
CanvasView.getElements2().add(1);
CanvasView.getPoints().remove(CanvasView.getPoints().size() - 1);
CanvasView.getElements().remove(CanvasView.getElements().size() - 1);
CanvasView.currentState = CanvasView.undoRedo.DoUndoRedo;
return true;
} else if (CanvasView.getElements().get(listSize - 1) == 2) {
CanvasView.getLines2().add(CanvasView.getLines().get(CanvasView.getLines().size() - 1));
CanvasView.getElements2().add(2);
CanvasView.getLines().remove(CanvasView.getLines().size() - 1);
CanvasView.getElements().remove(CanvasView.getElements().size() - 1);
CanvasView.currentState = CanvasView.undoRedo.DoUndoRedo;
return true;
} else if (CanvasView.getElements().get(listSize - 1) == 3) {
CanvasView.getRectangles2().add(CanvasView.getRectangles2().get(CanvasView.getRectangles2().size() - 1));
CanvasView.getElements2().add(3);
CanvasView.getRectangles().remove(CanvasView.getRectangles().size() - 1);
CanvasView.getElements().remove(CanvasView.getElements().size() - 1);
CanvasView.currentState = CanvasView.undoRedo.DoUndoRedo;
return true;
}
return true;
} else if (id == R.id.redo) {
int listSize2 = CanvasView.getElements2().size();
System.out.println(CanvasView.getElements2().size());
if (CanvasView.getElements2().size() == 0) {
return true;
} else if (CanvasView.getElements2().get(listSize2 - 1) == 1) {
CanvasView.getPoints().add(CanvasView.getPoints2().get(CanvasView.getPoints2().size() - 1));
CanvasView.getPoints2().remove(CanvasView.getPoints2().size() - 1);
CanvasView.getElements2().remove(CanvasView.getElements2().size() - 1);
CanvasView.getElements().add(1);
CanvasView.currentState = CanvasView.undoRedo.DoUndoRedo;
return true;
} else if (CanvasView.getElements2().get(listSize2 - 1) == 2) {
CanvasView.getLines().add(CanvasView.getLines2().get(CanvasView.getLines2().size() - 1));
CanvasView.getLines2().remove(CanvasView.getLines2().size() - 1);
CanvasView.getElements2().remove(CanvasView.getElements().size() - 1);
CanvasView.getElements().add(2);
CanvasView.currentState = CanvasView.undoRedo.DoUndoRedo;
return true;
} else if (CanvasView.getElements2().get(listSize2 - 1) == 3){
CanvasView.getRectangles().add(CanvasView.getRectangles2().get(CanvasView.getRectangles2().size() - 1));
CanvasView.getRectangles2().remove(CanvasView.getRectangles2().size() - 1);
CanvasView.getElements2().remove(CanvasView.getElements2().size() - 1);
CanvasView.getElements().add(3);
CanvasView.currentState = CanvasView.undoRedo.DoUndoRedo;
return true;
}
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Exit");
builder.setMessage("Are You Sure?");
builder.setNegativeButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
System.exit(0);
}
});
builder.setPositiveButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
Activiti_main.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"
android:id="#+id/rect"
tools:context="com.example.HW3.MainActivity" >
<com.example.HW3.CanvasView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
I am assuming you have defined your CanvasView in your res/layout/activity_main.xml (and assigned it an id, say "custom_view")
You should not call your Canvas methods statically (directly from the class itself) but from the actual instance created by Android. You can get it by changing your MainActivity to something like this:
public class MainActivity extends Activity {
private CanvasView canvasView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
canvasView = findViewById(R.id.custom_view)
}
...
#Override
public boolean onOptionsItemSelected(MenuItem item) {
...
canvasView.invalidate();
...
}
You mention the error states that invalidate() isn't a static method. Are you sure you're calling myCanvasInstance.invalidate() and NOT CanvasView.invalidate()?
See these code below to make it clearer:
protected void onCreate(Bundle savedInstanceState) {
// This won't work.
CanvasView.invalidate();
// This will.
CanvasView myView = new CanvasView();
myView.invalidate();
}
In fact, your code seem like treating all method calls as a static call. I don't know whether this is a typo or you are really doing it in your code. The thing is, that's not the right way to do it - CanvasView is a class, not an instance/object. You should interact with the instance, not with the class.
For starters, change these line (and all those that are similar to it):
CanvasView.getElements().size();
CanvasView.getElements2().add(1);
CanvasView.getLines().remove(CanvasView.getLines().size() - 1);
etc.
to something that looks like this:
// Define this on the top of your class (outside of any methods).
CanvasView myView;
// Do this on your onCreate() method.
myView = new CanvasView();
// ..and then code like these WILL work; even outside of onCreate().
myView.invalidate();
myView.getElements().size();
myView.getElements2().add(1);
myView.getLines().remove(CanvasView.getLines().size() - 1);
etc.
Hope this helped you.

how to reduce the amount of memory my android app uses

recently i have started working on this app what it does is show a little joystick on the screen and when you press the arrow to android icon moves to where its pressed
this works fine in the emulator but as soon as i try it on my phone it crashes instantly
im guessing my program is using to memory so how can i limit this?
thanks
package net.jegard.software.zombie.main;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class zombView extends SurfaceView{
private Bitmap bmp, grass, joystick;
private SurfaceHolder holder;
Timer t = new Timer();
float x = 0, y = 0;
boolean forward, left, down, right;
public zombView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(final SurfaceHolder holder) {
t.scheduleAtFixedRate(new TimerTask(){
public void run(){
Canvas c = holder.lockCanvas(null);
onDraw(c);
holder.unlockCanvasAndPost(c);
onTouchEvent(null);
}
},200,100);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
grass = BitmapFactory.decodeResource(getResources(), R.drawable.grassland);
joystick = BitmapFactory.decodeResource(getResources(), R.drawable.joystic);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(grass, getWidth() - getWidth(), getHeight() - getHeight(), null);
canvas.drawBitmap(joystick, getWidth() - getWidth(),joystick.getHeight(), null);
canvas.drawBitmap(bmp, x, y, null);
if(right){
x = x + 10;
}
if(forward){
y = y - 10;
}if(left){
x = x - 10;
}if(down){
y = y + 10;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
float tx = event.getX();
float ty = event.getY();
if(tx > (joystick.getWidth()/3)*2 && tx < (joystick.getWidth()/3)*3 && ty > (getHeight() - joystick.getHeight()) + (joystick.getHeight()/3) && ty < (getHeight() - joystick.getHeight()) + (joystick.getHeight()/3)*2){
touch_right(tx, ty);
invalidate();
}if(tx > (joystick.getWidth()/3) && tx < (joystick.getWidth()/3)*2 && ty > (getHeight() - joystick.getHeight()) && ty < (getHeight() - joystick.getHeight()) + (joystick.getHeight()/3)){
touch_forward(tx, ty);
invalidate();
}if(tx > (joystick.getWidth() - joystick.getWidth()) && tx < (joystick.getWidth()/3) && ty > (getHeight() - joystick.getHeight()) + (joystick.getHeight()/3) && ty < (getHeight() - joystick.getHeight()) + (joystick.getHeight()/3)*2){
touch_left(tx, ty);
invalidate();
}if(tx > (joystick.getWidth()/3) && tx < (joystick.getWidth()/3)*2 && ty > ((getHeight() - joystick.getHeight()) + (joystick.getHeight()/3)*2) && ty < (getHeight() - joystick.getHeight()) + (joystick.getHeight())){
touch_down(tx, ty);
invalidate();
}
break;
case MotionEvent.ACTION_MOVE:
touch_move();
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
private void touch_right(float x2, float y2) {
right = true;
}
private void touch_left(float x2, float y2) {
left = true;
}
private void touch_down(float x2, float y2) {
down = true;
}
private void touch_forward(float x2, float y2) {
forward = true;
}
private void touch_move() {
touch_up();
}
private void touch_up() {
forward = false;
right = false;
down = false;
left = false;
}
}
It may not be a memory issue at all, try connecting your phone in debug mode and check the logs. It will give you the clear picture of the real problem.

Categories

Resources