invalidate only works in custom view - android

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.

Related

onDraw and invalidate method

I'm doing a school project. In this project I have to do a program that have one or more ball bouncing in the screen. I did some research on google to help me in this, and I found this code :
public class BouncingBallInside extends View {
private List<Ball> balls = new ArrayList<>();
public BouncingBallInside(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BouncingBallInside(Context context) {
super(context);
init();
}
private void init(){
//Add a new ball to the view
balls.add(new Ball(50,50,100, Color.RED));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Draw the balls
for(Ball ball : balls){
//Move first
ball.move(canvas);
//Draw them
canvas.drawOval(ball.oval,ball.paint);
}
invalidate(); // See note
}
}
The ball class :
public class Ball{
public int x,y,size;
public int velX = 10;
public int velY=7;
public Paint paint;
public RectF oval;
public Ball(int x, int y, int size, int color){
this.x = x;
this.y = y;
this.size = size;
this.paint = new Paint();
this.paint.setColor(color);
}
public void move(Canvas canvas) {
this.x += velX;
this.y += velY;
this.oval = new RectF(x-size/2,y-size/2,x+size/2,y+size/2);
//Do we need to bounce next time?
Rect bounds = new Rect();
this.oval.roundOut(bounds); ///store our int bounds
//This is what you're looking for ▼
if(!canvas.getClipBounds().contains(bounds)){
if(this.x-size<0 || this.x+size > canvas.getWidth()){
velX=-velX;
}
if(this.y-size<0 || this.y+size > canvas.getHeight()){
velY=-velY;
}
}
}
}
The program works perfecly.
I studied it deeply as good as I could. But after it and after watching the documentation I couldn't understand two thing:
Where and when the method onDraw(Canvas canvas) is called the first time.
Why at the end of onDraw there is invalidate()?
I mean the documentation said :
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future.
so... if this method is used to call onDraw,why don't call it direcly? what's the difference?
1)The onDraw method will be called by the framework, whenever the view is invalid. A view is invalid when it first comes on screen, so when you set your content view for an activity they layout and all views in it will be measured, laid out, then drawn (via onDraw).
After that the UI thread will call onDraw if needed every 16ms or so (so it draws at 60 FPS).
2)Its marking the view as needing to be redrawn, so the next time the the screen is drawn onDraw will be called. Otherwise it would be skipped, as we could assume it isn't needed.
Why you don't call onDraw directly- efficiency. In a very simple drawing system you would- but drawing is time consuming, you don't want to do it more than you have to. So instead of drawing immediately (which wouldn't work anyway, you wouldn't have the right Canvas to pass to onDraw), you call invalidate and the system will call onDraw if needed at a regular interval.
Note that this isn't particularly good code. In particular, having the onDraw trigger the move which updates the balls location instead of using a timer is icky. Having onDraw call invalidate as a result is also kind of icky. A better solution would be to separate the view, model, and timer into more of an MVC or MVP system.

How to draw multiple bitmaps on canvas?

I want to develop a game that shoots bullets on every touch of the canvas.
It works but when I touch the canvas after shooting, he takes the bullet and restarts the shooting.
I just want the bitmap to create new bullet at every touch. Here is my code:
public class MainActivity extends Activity implements OnTouchListener {
DrawBall d;
int x ;
int y;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
d = new DrawBall(this);
d.setOnTouchListener(this);
setContentView(d);
}
public class DrawBall extends View {
Bitmap alien;
public DrawBall(Context context) {
super(context);
alien = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
startDraw(canvas);
}
public void startDraw(Canvas canvas){
Rect ourRect = new Rect();
ourRect.set(0,0, canvas.getWidth(), canvas.getHeight());
Paint blue = new Paint();
blue.setColor(Color.BLACK);
blue.setStyle(Paint.Style.FILL);
canvas.drawRect(ourRect, blue);
if(y < canvas.getHeight()){
y-=5;
}
canvas.drawBitmap(alien, x, y,new Paint());
invalidate();
}
}
public boolean onTouch(View v, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
return false;
}
}
I only superficially read the code. It seems that you are only keeping track of one bullet using the x y coordinates.The coordinates are resetting at every touch event, and thus you lose the previous bullet.
Use a dynamic array, or a linked list to keep track of all the bullets on the screen.
When there's a new touch, add the x,y to the array.
When drawing the bullet, iterate through your array to draw and update every bullet.
If the y-coordinate of any bullet goes out of the screen, delete the bullet from the array.
Each draw starts with a blank canvas. So to draw multiple bitmaps you need to keep track of where to draw each bullet and call drawBitmap multiple times.
Also, calling invalidate in onDraw is a bad idea- it will immediately invalidate, leading you to have performance issues. I'd suggest invalidating on a timer instead. Drawing too frequently will lead to performance issues.

Drawing to canvas onDraw works, drawing onTouchEvent doesn't

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!

Android graphics outside of onDraw method

Okay, so I'm trying to draw to a canvas on Android from outside of the onDraw method.
It's just easiest to show my code:
public class TestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Paint p = new Paint();
p.setColor(Color.GREEN);
Panel a = new Panel(this,150,150,50,p);
a.drawThing();
setContentView(a);
}
class Panel extends View{
private float radius, x, y;
private Canvas CAN;
private Paint p;
public Panel(Context context, float x, float y, float radius, Paint p){
super(context);
this.x = x;
this.y = y;
this.radius = radius;
this.p = p;
}
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
CAN = canvas;
}
public void drawThing(){
CAN.drawCircle(x, y, radius, p);
}
}
}
Do you see what I'm trying to do? But for some reason it throws a NullPointerException
Many of the graphics resources are explicitly freed/released after they've been used. I'm not exactly sure why they do this, but whatever the reason, they don't you to do what you're trying.
Instead of drawing outside of the onDraw method, use some kind of flag to change what the onDraw method is doing. When you want to draw some specific thing, you can set the right flag, and call invalidate().
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
if (doThing) {
canvas.drawCircle(x, y, radius, p);
}
}
EDIT
Something else to consider is drawing to and "off-scrren" source. This means using some kind of graphics representation like a bitmap as a buffer that you can draw to in other code. This won't update your gui, but it will give you the chance to do some heavy duty drawing without locking up the user's device. Once you are done drawing to the bitmap (or whatever) you can invalidate your view and draw it to the screen in the onDraw(Canvas) method.
I'm pretty sure that the null pointer happens because you're calling drawSomething before onDraw ever gets called. So CAN is null.
You can draw onto canvas outside of the onDraw. See this Can we have two canvases in an activity ? (OR) Having a canvas outside the onDraw() is not working for more info.

View is not automatically being drawed when overriding dispatchDraw

I'm trying to mirror a LinearLayout.
To get this work, I extended LinerLayout to create my own View component.
This is what it looks like:
public class FlipLayout extends LinearLayout implements Runnable {
private boolean isMirroring = true;
private Handler handler;
public FlipLayout(Context context) {
super(context);
this.setWillNotDraw(false);
}
public FlipLayout(Context context, AttributeSet attr) {
super(context, attr);
this.setWillNotDraw(false);
handler = new Handler();
handler.postDelayed(this, 30);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
if (isMirroring) {
canvas.rotate(180, getWidth() / 2, getHeight() / 2);
}
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (isMirroring)
event.setLocation(getWidth() - event.getX(),
getHeight() - event.getY());
return super.dispatchTouchEvent(event);
}
#Override
public void run() {
invalidate();
handler.postDelayed(this, 30);
}
}
That class is working well, but only when implementing the Runnable interface and by calling invalidate() every few milliseconds.
If I only rotate the canvas without invalidating the view, the changes of the child views are not drawn.
Now I'm wondering what's the reason for this behaviour and if theres a way to get it working without the Runnable/Handler implementation.
If I remove the line canvas.rotate(...) the changes of the child views are drawn correctly (this is for example a progressbar which is updating itself frequently.)
I hope someone can help!
Thanks so much.
Chris
It looks normal to me that changing a Gui dynamically would not update the screen. In any Gui framework, they require the programmer to manually request the redraw.
This is because if i change 10 items in my Gui, i don't want to redraw 10 times, but only once at the end. And the framework can't guess when i'm done refactoring the Gui. So i need to explicitely call a refresh method somehow.
Alternatively, you could invalidate you Gui directly when you're done rotating your stuff, and not in a separate thread.

Categories

Resources