I want to erase my bitmap but I don't want to erase background image. When I try to erase is white and it draw very hard in frames.
This is from CanvasView
erasePaint.setColor(Color.WHITE);
//erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
erasePaint.setAntiAlias(true);
erasePaint.setStyle(Paint.Style.STROKE);
erasePaint.setStrokeJoin(Paint.Join.ROUND);
erasePaint.setStrokeWidth(12);
//....
protected void onDraw(Canvas canvas) {
paint.setPathEffect(null);
if(bitmap!=null){
for(MyEraser e:eraserList){
canvas.drawPath(e.p,erasePaint);
invalidate();
}
final OnTouchListener eraseListener = new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//FirstActivity.ll.setVisibility(LinearLayout.GONE);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
myEraser = new MyEraser();
lastTouchX = event.getX();
lastTouchY = event.getY();
myEraser.mouseDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
resetDirtyRect(event.getX(),event.getY());
int historySize = event.getHistorySize();
for(int i=0;i<historySize;i++){
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
myEraser.mouseUp(historicalX, historicalY);
}
myEraser.mouseUp(event.getX(), event.getY());
eraserList.add(myEraser);
break;
default:
Log.d("mock it up", "Unknown touch event " + event.toString());
return false;
}
invalidate(
(int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
lastTouchX = event.getX();
lastTouchY = event.getY();
return true;
}
};
}
}
This is MyEraser
public class MyEraser {
Paint paint = new Paint();
Path p = new Path();
public MyEraser(){
paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(5);
}
public void mouseDown(float x, float y) {
//path.addCircle(x,y,5,Path.Direction.CW);
p.moveTo( x, y );
// path.lineTo(x, y);
}
public void mouseMove(Path path, float x, float y) {
// path.addCircle(x,y,5,Path.Direction.CW);
}
public void mouseUp(float x, float y){
//path.addCircle(x,y,5,Path.Direction.CW);
p.lineTo(x, y);
}
public void draw(Canvas c,Path path){
//paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(7);
c.drawPath(p, paint);
}
}
EDIT: Started over for better explanation :)
I am not familiar with Android, in fact I have never used it. Use this code as an example of how to organize your code, rather than a functional code.
class MyView extends View {
private Eraser myEraser;
private Bitmap myBackgroundImage;
private Canvas myForegroundCanvas;
public MyView(Context context, Attributes, attrs) {
myEraser = new Eraser()
myBackgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.your_background_name);
Bitmap image = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); // the width and height of the view
myForegroundCanvas = new Canvas(image);
}
public boolean onTouchEvent(MotionEvent event) {
// update your eraser path
return true;
}
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(myBackgroundImage, 0, 0, null);
//for (Eraser item : eraserList) {
// if you have multiple eraser, add them to a list
myEraser.draw(myForegroundCanvas);
//}
canvas.drawCanvas(myForegroundCanvas, 0, 0, null);
}
}
The main idea is to keep the separation between your background and foreground images. That way, you could easily change the background and it would be updated. You could also have a reset on the foreground to erase everything, etc.
I hope this helps you.
Related
I am pretty new to Android so I apologize ahead of time if I am missing something, but my problem is that I am trying to draw a circle at a certain location everytime I click on the screen.
I have tried logging and it does return an int where it matches my if statement but nothing gets drawn.
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
drawCirc = true;
xTouch = event.getX();
Log.d("keyboard", "xpos" + xTouch);
yTouch = event.getY();
break;
case MotionEvent.ACTION_UP:
drawCirc = false;
}
return true;
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
if(drawCirc) {
if (xTouch < 150 && xTouch>0) {
paint.setColor(Color.RED);
canvas.drawCircle(150, 500, 100, paint);
isPlayer1 = false;
invalidate();
}
}
}
#Javanator is right, you should do invalidate in touch listener.
Meanwhile, you can try out the code below, which adds animation when circle being drawn.
Paint paint = new Paint();
{
paint.setColor(Color.RED);
}
// The radius of the circle you want to draw
// 0 by default
private int radius = 0;
// The animator to animate circle drawing
private ObjectAnimator radiusAnimator;
public void setRadius(int newRadius) {
this.radius = newRadius;
this.invalidate();
}
private void showCircle(boolean show) {
ObjectAnimator animator = this.getRadiusAnimator();
if (show) {
animator.start();
} else {
animator.reverse();
}
}
private ObjectAnimator getRadiusAnimator() {
if (this.radiusAnimator == null) {
this.radiusAnimator = ObjectAnimator.ofInt(this, "radius", 0, 100);
}
return this.radiusAnimator;
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// drawCirc = true;
xTouch = event.getX();
Log.d("keyboard", "xpos" + xTouch);
yTouch = event.getY();
showCircle(true);
break;
case MotionEvent.ACTION_UP:
// drawCirc = false;
showCircle(false);
}
return true;
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (xTouch < 150 && xTouch > 0) {
canvas.drawCircle(xTouch, yTouch, radius, paint);
}
/*
if(drawCirc) {
if (xTouch < 150 && xTouch>0) {
paint.setColor(Color.RED);
canvas.drawCircle(150, 500, 100, paint);
isPlayer1 = false;
invalidate();
*/
}
public class CustomView extends View {
boolean drawCirc;
float xTouch;
boolean isPlayer1;
float yTouch;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
drawCirc=true;
xTouch = event.getX();
yTouch = event.getY();
invalidate();
}
return false;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
if (drawCirc) {
paint.setColor(Color.RED);
canvas.drawCircle(xTouch, yTouch, 100, paint);
isPlayer1 = false;
invalidate();
}
}}
I have solved your functionality and Implemented sample code. I have checked it and it's working and you have not put a code in onTouch Listener that's why it's not worked but now I have solved. if you want area limit then put yourif (xTouch < 150 && xTouch>0)
I can draw by fingers in canvas, but I can not zoom it by two fingers without loss of scale drawn
and I want to do infinite my canvas size
I have this code:
public class MainActivity1 extends AppCompatActivity {
DrawingView dv ;
private Paint mPaint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new DrawingView(this);
setContentView(dv);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
public class DrawingView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
public DrawingView(Context c) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath( mPath, mPaint);
canvas.drawPath( circlePath, circlePaint);
}
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;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
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;
}
}
}
Please give me advice how to do it?
And I also want to do infinite my canvas size - its optional.
Main problem is zoom
My advice - you can use opengl. It's looks like you want to create some drawer, but canvas not perfect solution for this. Currently almost all devices support opengl es. It faster than canvas and you can made many another function(as scaling, image pasting, 3d objects etc.) much more easier. Here you can find a good tutorial.
Yoo should use ScaleGestureDetector to scale your canvas. This is my sample code:
private class MyScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
return true;
}
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d("myZoomTag", "SCALE STARTED");
return true;
}
public void onScaleEnd(ScaleGestureDetector detector) {
Log.d("myZoomTag", "SCALE ENDED");
}
}
Here scaleFactor is global variable. In your onDraw method you should do something like this:
bitmapWidth *= scaleFactor;
bitmapHeight *= scaleFactor;
I'm developing an app that need the feature in object.
I have an Image A that cover an Image B. With finger I need to erase the Image A to show the Image B.
Erase must follow your finger flowing image A
I'm trying some code but still I couldn't erase the image A. this is the code that i'm using to draw a line on image (_imageToErase is Image A):
Canvas canvas;
Paint paint;
float downx = 0, downy = 0, upx = 0, upy = 0;
ImageView _imageToErase;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.g_layout);
_imageToErase = (ImageView) findViewById(R.id.image_to_erase);
_imageToErase.setOnTouchListener(this);
}
#Override
public void onWindowFocusChanged(boolean hasFocus){
int width = _imageToErase.getWidth();
int height = _imageToErase.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStrokeWidth(25);
paint.setAntiAlias(true);
_imageToErase.setImageBitmap(bitmap);
}
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
downx = event.getX();
downy = event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = event.getX();
upy = event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
_imageToErase.invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
This code produce only a line that follow the finger but not erase the image.
How to modify this code to erase the image? Thanks
EDIT
The link suggested in comments not solved my problem. Simply add this line:
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
not work for me.
PaintView.java
public class PaintView extends View implements View.OnTouchListener {
private static final String TAG = "PaintView";
Bitmap Bitmap1, Bitmap2;
Bitmap Transparent;
int X = -100;
int Y = -100;
Canvas c2;
private boolean isTouched = false;
Paint paint = new Paint();
Path drawPath = new Path();
public PaintView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Transparent = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.cake1);
Bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.cake2);
c2 = new Canvas();
c2.setBitmap(Transparent);
c2.drawBitmap(Bitmap2, 0, 0, paint);
paint.setAlpha(0);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setAntiAlias(true);
}
private static Point getDisplaySize(final Display display) {
final Point point = new Point();
point.x = display.getWidth();
point.y = display.getHeight();
return point;
}
#Override
protected void onDraw(Canvas canvas) {
System.out.println("onDraw");
if(isTouched)
{
canvas.drawBitmap(Bitmap1, 0, 0, null);
}
canvas.drawBitmap(Transparent, 0, 0, null);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
isTouched = true;
X = (int) event.getX();
Y = (int) event.getY();
paint.setStrokeWidth(60);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(X, Y);
c2.drawPath(drawPath, paint);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(X, Y);
c2.drawPath(drawPath, paint);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(X, Y);
c2.drawPath(drawPath, paint);
drawPath.reset();
count=0;
break;
default:
return false;
}
invalidate();
return true;}}class Point {
float x, y;
#Override
public String toString() {
return x + ", " + y;
}}
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new PaintView(this));
}}
Finally I found a library that works very well for me and implementation is pretty simple
https://github.com/winsontan520/Android-WScratchView
Import library in your project and then in layout.xml
<com.winsontan520.WScratchView
android:layout_width="287dp"
android:layout_height="212dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="80dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:id="#+id/image_to_erase"/>
in onCreate:
_imageToErase = (WScratchView) findViewById(R.id.image_to_erase);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img_erase);
_imageToErase.setScratchBitmap(bitmap);
_imageToErase.setOnScratchCallback(new WScratchView.OnScratchCallback() {
#Override
public void onScratch(float percentage) {
updatePercentage(percentage);
}
#Override
public void onDetach(boolean fingerDetach) {
if (mPercentage > 40) {
_imageToErase.setScratchAll(true);
updatePercentage(100);
}
}
});
and
private void updatePercentage(float percentage) {
mPercentage = percentage;
// System.out.println("percentage = "+percentage);
}
Thanks everybody
I created an app to draw and wanted to implement the functions undo / redo, I tried various methods found surfing but none of them work, can someone help me?
Here is my code:
Variables
'public class MainDrawingView extends View {
public MainDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
float TOUCH_TOLERANCE = 4;
float mX, mY;
//drawing path
private Path drawPath, drawX, drawY;
//drawing and canvas paint
private Paint drawPaint;
private Paint canvasPaint;
private View canvasback;
private Paint bccanvas;
//initial color
private int paintColor = 0xFF000000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap, trans;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();'
SetupDrawing
'private void setupDrawing() {
drawPath = new Path();
drawPaint = new Paint();
bccanvas = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.FILL_AND_STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}'
onSizeChanged
'#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}'
Touch Event
'private void touch_start(float x, float y) {
undonePaths.clear();
drawPath.reset();
drawPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx, dy;
dx = Math.abs(x - mX);
dy = Math.abs(y - mY);
if ((dx >= TOUCH_TOLERANCE) || (dy >= TOUCH_TOLERANCE)) {
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
drawPath.moveTo(mX, mY);
mX = x;
mY = y;
}
}
private void touch_up() {
drawPath.reset();
}'
onTouchEvent
'public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
drawPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
drawPath.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
drawPath.reset();
touch_up();
invalidate();
break;
}
return true;
}'
onDraw
'#SuppressLint("NewApi")
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(p, drawPaint);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
}'
onClickUndo
'public void onClickUndo () {
if (paths.size()>0){
undonePaths.add(paths.remove(paths.size()-1));
invalidate();
undonePaths.clear();
}
else
{
}
//toast the user
}'
onClickRedo
'public void onClickRedo (){
if (undonePaths.size()>0)
{
paths.add(undonePaths.remove(undonePaths.size()-1));
invalidate();
}
else
{
}
//toast the user
}'
TL;DR.
I created a similar application in Java Swing a couple of months ago. The way I implemented the 2 methods was I had created a Stack of shapes which was added to as the user was adding shapes to the whiteboard and then push/popped from this to undo/redo (storing the shape which was removed in a temp variable)
protected Shape removed; //shape object of an undo-ed object
protected Stack<Shape> shapes = new Stack<Shape>(); //stack to store the shapes
public void undo()
{
if (!shapes.empty()) //only undo if there's shapes on the board
{
removed = shapes.pop(); //pop removes a shape and returns it into 'removed' object
repaint();
}
}
public void redo()
{
if (removed != null) //only redo if something has been undone
{
shapes.push(removed); //push adds the object back onto the stack
removed = null;
repaint();
}
}
I hope this helps!
A good approach is to create an interface called something like "Undoable". It has at least 3 overrrides: do(), undo() and redo(). Then do all your work in classes that implement Undoable, and keep a stack of these around = the undo history.
I'd like to draw a series of images from touch event triggered. From the returned path inside onDraw, I will use it as a guide on where to draw the images. My problem right now is to have a series of random images displayed, not just one. Then eventually the images displayed will slowly fade out.
public class SingleTouchEventView extends View {
private Paint paint = new Paint();
private Path path = new Path();
float eventX;
float eventY;
Bitmap bmp;
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);
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
/*Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stub);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmp, 10, 10, null);*/
canvas.drawPath(path, paint);
int rand = (int) (Math.random() * 3);
Log.d("Integer", ""+rand);
if(rand==0){
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stub);
}else
if(rand==1)
{
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stub);
}
else if(rand==2)
{
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.bg_striped_img);
}
canvas.drawBitmap(bmp, eventX, eventY, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
eventX = event.getX();
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;
}
}