I've fooling around with 2D graphics in the Android SDK and I'm having trouble with what should be a simple example.
I'm assuming that I'm just misunderstanding something fundamental/basic.
public class DrawView extends View {
Paint paint = new Paint();
Canvas canvas = new Canvas();
public DrawView(Context context) {
super(context);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
this.canvas = canvas;
this.canvas.drawLine(0,0, 500, 500, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("DrawView", "onTouchEvent: " + event.getX() + "," + event.getY() );
canvas.drawLine(0,500, 500, 0, paint);
return true;
}
}
The code above draws a single line from 0,0 to 500,500 when the app start. That parts works just fine.
The issue is that the second line isn't drawn on the touch event. The onTouchEvent is definitely being called because I see the coordinates debug message in the log.
Can someone point out what silly thing I'm doing wrong?
You're supposed to call invalidate() at the end of onTouchEvent() to tell the system to update the screen. Calling invalidate() will call onDraw().
Also, what is fundamentally wrong is that you create a canvas in this class you have. That does absolutely nothing for you. The canvas to draw in is the one that you get from the onDraw() method. The call to canvas.drawLine() in onTouchevent isn't doing anything for you and shouldn't be there. That is an empty canvas and isn't the one that will get "posted."
In onTouchEvent() you should only gather the touch event data, and also do some processing on it if you need to. You shouldn't make any calls to drawing methods there. However, as I said, if you want to trigger a draw from onTouchEvent(), you call invalidate(). If you want to draw lines based on where you are touching, you will need to create class variables that are X and Y coordinates. You update these X and Y variables in onTouchEvent(), and then you use them in onDraw() to draw whatever you need based on these X and y variables.
Call postInvalidate() function. This function inform that view should be redrawed (event loop call onDraw() function).
You can declare a bool variable in your class, so that you can pass it to your ondraw() method that the user has touched and also pass X and Y with other float variables to ondraw() methode !
But you have to vall invalidate in onTouchEvet() so that the system will redraw the canvas using your new touch orders!
Related
I have drawn a bitmap image over a canvas.
Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.sq);
canvas.drawColor(color.black);
Rect dstRectForRender = new Rect(0,0,320,450);
canvas.drawBitmap(image, null,dstRectForRender,null);
The image gets displayed based on my screnn on a cnavs.
On my touch input, I need to pass the x and y co-ordinate position of the image and fill that pixel with a color to show that the image is painted on a drag event.
How can I pass the x and y coo-ordinate parameters? Which functions should I use to plot the pixels on the image?
I appreciate your help and sweet time.
I'm not sure if this is the best way to do this, but it would be much easier to do this if you defined your own subclass of ImageView and name it something like DrawableImageView. You'd have to make sure you implement all the basic constructors from ImageView, then override the onTouchEvent method. From that event you can get the touch coordinates and store them in an ArrayList<Point> and use that ArrayList by overriding the onDraw method and "painting" the image.
public class DrawableImageView extends ImageView {
ArrayList<Point> list = new ArrayList<Point>();
//constructors..
#Override
public boolean onTouchEvent (MotionEvent event) {
float x = event.getX();
float y = event.getY();
list.add(new Point(x,y));
invalidate();
}
This is just a very brief overview of how to start your class, and may not be the most accurate way of doing things (depending on your specific code). Now, instead of using <ImageView> tags in your xml (or, loading an ImageView programatically), you refer to your subclass like so:
<your.package.name.DrawableImageView
/>
Edit
In response to your comment, there is no predetermined way to draw over an image. You must implement this yourself, which is why I recommended storing Points in an ArrayList. I'm not really sure what you're trying to achieve here, but to draw (for example) black dots over an image you have to override onDraw:
public void onDraw(Canvas c) {
super.onDraw(c);
for(Point p : list) {
//Draw black point at x and y.. I'm posting from my cell so I can't go into much detail
}
}
Also, to force a View to redraw itself you need to use the invalidate() method in your onTouchEvent() (which I've added above).
I want an action to be performed when the view is touched. However, the touches do not respond. The app doesn't crash, it just seems to ignore it.
public class CustomDrawableView extends View implements OnTouchListener
{
static final int width = 100;
static final int height = 50;
public CustomDrawableView(Context context)
{
super(context);
setFocusable(true);
setOnTouchListener(mCustomDrawableView);
mDrawable = new ShapeDrawable(new OvalShape());
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x, y, x + width, y + height);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN )
{
x = 400;
return true;
}
else {
x = 300;
return false;
}
}
protected void onDraw(Canvas canvas)
{
int mCanvasHeight = canvas.getHeight();
int mCanvasWidth = canvas.getWidth();
canvas.save();
canvas.rotate(R,x,y);
if (y >= mCanvasHeight-100) {
y = 0;
}
RectF oval = new RectF(x, y, x + width, y
+ height); // set bounds of rectangle
Paint p = new Paint(); // set some paint options
p.setColor(Color.BLUE);
canvas.drawOval(oval, p);
canvas.restore();
invalidate();
}
}
I have tried a bunch of different code to fix it. None of it does anything except if I change setOnTouchListener(mCustomDrawable) to mCustomDrawableView.setOnTouchListener(this) the app crashes. There is a bunch more code in the activity that I did not put up.
I'm guessing you believe that the onTouch() isn't firing because you're not seeing your graphics change in response to changes in x or whatever. If that's the case, it looks like you're missing a call to invalidate() in your touch handler to cause the View to redraw itself again (via a call to onDraw()).
Also, you have an invalidate() actually inside onDraw() itself which really shouldn't be there. It would certainly cause your View to redraw itself over and over - I suppose - so actually, I guess you should be seeing updates because that's there. But that isn't the way you should make a View animate; you should instead use a Thread or Handler to schedule a regular, periodic invalidate() - or some other means to regularly schedule an update.
Also there should be no need to implement 'OnTouchListener' when you can just override onTouch() as you have done. There are a couple of ways you can detect touch events for a View: (1) Override onTouch() as you have done, to get touch events on that View. (2) Register a listener using setOnTouchListener(). This latter option enables you to have a listener that listens to touch events from one View or multiple Views, and it also 'sees' touch events before a registered View's own onTouch() sees them.
Another thing I see is that you're setting x to 400 when you get an ACTION_DOWN event but then you're setting it to 300 for any other kind of event such as an ACTION_MOVE. Considering it's actually quite difficult to keep your finger still enough to never cause a string of ACTION_MOVE events immediately after an ACTION_DOWN, perhaps you're just never seeing the graphics when x is at 400 or something.
You really need to post a complete example (for instance your code as you've posted has setOnTouchListener(mCustomDrawableView) where mCustomDrawableView has not been defined anywhere) together with specific errors from Logcat. Also, you should use the debugger to see if your onTouch ever executes (stick a breakpoint in it).
Also, you should accept some answers.
I am trying to write a small android app that redraws an image everytime the screen is touched.
I expected the image to be redrawn to the new x,y coordinates provided by event.getX() and event.getY() when I override the onTouch(Event) method in an activity.
Can anyone help?
I presume, you'd be having the Bitmap object for the image.
You could call the onDraw() method from the onTouch() method, to draw the bitmap. Here's a code sample which shows the overridden onDraw() method:
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(imgBitmap, x, y, null);
}
x,y are the new co-ordinates of the touch. imgBitmap is the Bitmap object for the image. You can update the instance variables x and y in the onTouch() method.
Hope this helps!!
so i created a view called "drawable view"
class DrawableView extends View{
Context mContext;
int touches=0,k,Xoffs,clicks=0;
double x_1 = 0,x_2=0;
private float mLastTouchX, mLastTouchY;
public DrawableView(Context context) {
super(context);
mContext = context;
}
....
#Override
protected void onDraw(Canvas canvas){
Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
canvas.drawColor(Color.BLUE);
myPaint.setColor(Color.WHITE);
canvas.drawCircle(200, 100, 20, myPaint);
}
..... more code....
}
and it can only be invalidated within the ondraw command! ie: calling "invalidate();" at the end of the ondraw command causes it to loop.
I have tried many times to call g_draw.invalidate(); or g_draw.postInvalidate(); (g_draw is the name of the created Drawable View)from other classes and even the main activity class and it doesnt work. why and how can i fix it?
thanks
If you want continious onDraw invoking try doing it in another thread. Create a thread, and from its run method try doing postInvalidate.
It always worked for me.
Another thing is that when you draw a circle once, next time wont make any difference - it will look the same.
You may want to call invalidate() somewhere in your DrawableView class. For example, if you want your view to redraw itself after any touch event, you would do something like this:
public boolean onTouchEvent( MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
invalidate();
}
}
This is how I draw the movable pieces in my puzzle game.
i have a method which displays a map. i call this method in onDraw method. but on Action move i need to redraw the map and need to call that method again but i am not getting canvas reference to redraw the map tiles. i used invalidate method but it start refreshing the onDraw frequently which made my map movement too to slow..
here is my onDraw method.
protected void onDraw(Canvas canvas)
{
Log.i("On Draw Call", "On Draw call");
mapMaker.getMapForScreenArea(map.getiScrnArea(), mapType, input, canvas);
invalidate();
this.canvas = canvas;
}
any help will be appreciative.
thanks a lot.
onDraw() gets called again and again because you invalidate() every time.
And also this.canvas = canvas is unnecessary.