I have some methods in my View that modify some of the shapes that are drawn when called. In Java in order to make sure the component is updated I would call repaint(). Is there something that will make sure my view is updated correctly?
I had read somewhere that calling invalidate() in the onDraw() method would keep things up to date and therefore I wouldn't need to have something like repaint() in my methods that modify that shapes that are drawn.
Is this correct, or is there something else I have to do?
EDIT
To add in an example, a method I call in my view is:
public void setLineThickness(int thickness) {
aLineThickness = thickness;
if(aLineThicness > 1)
//repaint(); - Okay in Java but not in Android
}
Calling invalidate() will tell the view it needs to redraw itself (call onDraw) sometime in the future. So if you change something in your view, like the line thickness, call invalidate() after it. That way you know your view will eventually be updated.
All your drawing code should be implemented in onDraw() and your other methods should just change the state of your view, which will then be used to draw it, after you call invalidate().
Related
My project is based on surfaceView and up until now I've had all of my rendering in onDraw which I am overriding. All seemed to be OK.
However, I've just updated my SDK and now it gives me an error telling me:
Suspicious method call; should probably call "draw" rather than "onDraw"
Could someone please explain the difference between these two?
I've read some similar questions around the net but I've not found an explanation that I understand.
Thanks
I tried cleaning my project and it did solve the problem. Try it.
SurfaceView.draw() basically calls View.draw(); If you want to implement your drawing, you should do it in View.onDraw() which is for you to implement which even says in the source code comments.
This method is called by ViewGroup.drawChild() to have each child view
draw itself. This draw() method is an implementation detail and is not
intended to be overridden or to be called from anywhere else other
than ViewGroup.drawChild().
As for difference between them:
draw():
13416 /*
13417 * Draw traversal performs several drawing steps which must be executed
13418 * in the appropriate order:
13419 *
13420 * 1. Draw the background
13421 * 2. If necessary, save the canvas' layers to prepare for fading
13422 * 3. Draw view's content
13423 * 4. Draw children
13424 * 5. If necessary, draw the fading edges and restore layers
13425 * 6. Draw decorations (scrollbars for instance)
13426 */
onDraw() is empty. Its for you to implement.
I have the problem since ever.
I handle it like this:
1) Declare a method like the following.
#SuppressLint("WrongCall")
public void drawTheView() {
theCanvas = null;
try{
theCanvas = getHolder().lockCanvas();
if(theCanvas != null) {
onDraw(theCanvas);
}
} finally {
getHolder().unlockCanvasAndPost(theCanvas);
}
}
2) Now you can modify the onDraw() Method:
#Override
public void onDraw(Canvas canvas) {
//Do some drawing
}
You can call the drawTheView() method from everywhere you want and call the onDraw() method this way without getting the error...
I think this is a practical way.
Note that in the case of drawing, overriding draw() and calling super.draw is often used when a ViewGroup wants to draw content over its child views. Content drawn in onDraw will appear under children.
As friiky said, #SuppressLint("WrongCall") fixed my problem. However it must be in front of the method name, not the above.
What I did is put mouse over the error code, right click and select Add #SuppressLint("WrongCall")
onDraw gives you a canvas to draw to the screen.
draw() allows you to manually draw a canvas to the screen (you have to make the canvas yourself).
I want to know how a View is created in Android. On a View object creation which functions are called?Initially constructors will be called.Next what? And so on.
If anybody know the sequence of functions called after object creation,Please reply me.
Thanks
This might help you better I guess.
1) Constructor : to initialize variables
2) onMeansure : to prepare canvas , set Width and height
3)OnDraw() : to draw actual view .
now onDraw() will continuously be called after a particular interval which depends upon display/processor and UI eventsenter code here or on calling invalidate()
In my application I have an infinite loop on one of my View's onMeasure() overrides. Debugging the source code starting from a break point in my onMeasure, I am able to trace myself all the way up the stack trace up to the PhoneWindow$DecorView's measure() (top most class in my View Hierarchy), which gets called by ViewRoot.performTraversals(). Now from here if I keep stepping over, I eventually get the PhoneWindow$DecorView's measure() called again by a message in the Looper.loop() class. I'm guessing something has queued up a message that it needs to remeasure, like an invalidate.
My question is, what triggers that a measure call needs to occur on a View?
From my understanding of the layout/measure/draw process, this will only occur when the invalidate() method is called on a specific view, and that will trickle down and perform a layout/measure/draw pass for that view invalidated and all of its children. I would assume that somehow my top most View in my View Hierarchy is getting invalidated.
However, I've explicitly put a break point on every single invalidate call I have, and am not calling invalidate in some infinite manner. So I do not think that is the case. Is there another way to trigger a measure pass? Could something internally be triggering this? I'm kind of out of ideas after seeing that nothing is infinity invalidating.
In order to trigger a measure pass for a custom View you must call the requestLayout() method.
For example, if you are implementing a custom view that extends View and it will behave like a TextView, you could write a setText method like this:
/**
* Sets the string value of the view.
* #param text the string to write
*/
public void setText(String text) {
this.text = text;
//calculates the new text width
textWidth = mTextPaint.measureText(text);
//force re-calculating the layout dimension and the redraw of the view
requestLayout();
invalidate();
}
Well, If you are changing a View's content, it will eventually have to call invalidate(). For example, you have a TextView with a text called "Text 1". Now, you change the text of the same TextView to "Text 2". Here aslo, invalidate will be called.
So basically, when something changes on the view, more often than not, you would expect the invalidate method to be called, and a corresponding call to the measure().
Look at the source code for TextView, for example.
http://www.google.com/codesearch#uX1GffpyOZk/core/java/android/widget/TextView.java&q=TextView%20package:android&type=cs
Count the number of invalidate calls. There are quite a few.
I'm trying to move a ball on canvas. a and b are similar to x,y coordinate positions. Any way from my code im trying to get different values dynamically. The a,b are global variables. But it seems that "invalidate()" or the refreshing of screen only happens afer end of the whole loop. Do you know why?. And if i have to build this on another thread please suggest me with some simple codes.
private void shootBall(){
while (a>b){
a = getPositionX();
b = getPositionY();
invalidate();
}
}
}
I think it's more correct to say that you can call invalidate() from within a loop, but that that invalidation will not be handled (the canvas won't be redrawn) until after your loop is complete. The problem is that you are calling invalidate on the same thread (the UI toolkit thread) as the one that would call your onDraw() method. So unless/until you hand control back to the toolkit, it cannot possibly do the rendering. So your invalidate() call does actually invalidate the view ... but the view won't be redrawn until after your loop completes and your function returns.
It is more correct to change the position in some function that is called via some timer (which is essentially what the animation classes do). In that function, you would change the values, invalidate() appropriately, and return. Then the toolkit re-renders the scene and your function will get future callbacks and update the position accordingly.
do it like this, and use postInvalidate() instead:
private void shootBall(){
new Thread(new Runnable() {
public void run() {
while (a>b){
a = getPositionX();
b = getPositionY();
postInvalidate();
}
}
}).start();
}
edit: but as mentioned before, don't assume that the invalidate redraws the screen, it marks it as to-be-redrawn and the UI thread will get around to it.
You can put invalidate() at the end of onDraw() like in this example: How can I use the animation framework inside the canvas?
However this works well on some devices while bottlenecks and slows down on other.
To use a thread and SurfaceView go through all of these tutorials: http://www.droidnova.com/playing-with-graphics-in-android-part-i,147.html
UI cant be modified form any new thread..you should use invalidate() in the same thread where your view
I have a complex custom view - photo collage.
What is observed is whenever any UI interaction happens, the view is redrawn.
How can I avoid complete redrawing (for example, use a cached UI) of the view specially when I click the "back" button to go back to previous activity because that also causes redrawing of the view.
While exploring the API and web, I found a method - getDrawingCache() - but don't know how to use it effectively.
How do I use it effectively?
I've had other issues with Custom Views that I outline here.
I found a better way than using getDrawingCache.
In the method onDraw, apart from drawing in the natural canvas, I also draw on an memory-only canvas.
Bitmap cacheBmp = Bitmap.Create(....);
Canvas cacheCanvas = new Canvas(cacheBmp);
void onDraw(Canvas c)
{
if(updateDueToInteraction)
{
c.drawXXX(...);
cacheCanvas.drawXXX(...);
} else
{
c.drawBitmap(cacheBmp, 0, 0);
}
}
First of all you will have to use the setDrawingCacheEnabled(true) method, so that you're View is cache-enabled. Then, you can use the getDrawingCache(boolean) method which returns a Bitmap representing the View. Then, you can draw that bitmap manually.
If you don't enable caching by calling the setDrawingCacheEnabled(true) method, you will have to call buildDrawingCache() before (and call destroyDrawingCache() when you're done).
Bye!