My answer to this question was just accepted but I started to wonder when exactly one needs to invalidate() a View and when it is not necessary?
After a bit of thinking I came to realization that it should work more or less like this:
actual drawing of "everything" occurs after onResume()
in "free" time parts of the screen can be redrawn but only those that were invalidated (and everything underneath)
Therefore, it would seem, if I change something after onResume() (e.g. as a response to a button click, I should invalidate() the changed View).
However, from what scana in this question says, it must be more complex then that and it depends somethimes on what method one uses.
E.g. on whether one uses
lastClicked.setImageBitmap();
or
lastClicked.setImageResource();
So, when it's necessary to execute invalidate() on a View and how does it really work ?
(Do consider accepting some answers)
Generally, invalidate() means 'redraw on screen' and results to a call of the view's onDraw() method. So if something changes and it needs to be reflected on screen, you need to call invalidate(). However, for built-in widgets you rarely, if ever, need to call it yourself. When you change the state of a widget, internal code will call invalidate() as necessary and your change will be reflected on screen. For example, if you call TextView.setText(), after doing a lot of internal processing (will the text fit on screen, does it need to be ellipsised, etc.), TextView will call invalidate() before setText() returns. Similarly for other widgets.
If you implement a custom view, you will need to call invalidate() whenever the backing model changes and you need to redraw your view. It can also be used to create simple animations, where you change state, then call invalidate(), change state again, etc.
Usually, the system handles resizing, hiding, showing and a ton of other things for your widgets automatically but it sometimes has issues if the underlying buffer for drawn pixels or backing data has changed or is stale (you swap the image resource on a View or the raw dataset changes). This occurs because there is no way that the OS can know that the data changed in the specific manner that it did.
In these cases where you are dealing with drawing, you have to tell the system that its underlying data is not in a good state with Widget.invalidate() and the re-drawing gets queued on the main thread just as you mentioned. Depending on the system implementation and Android version what is tracked for changes by the system varies but what I normally do is assume that system resources (byte arrays, char arrays, resource indexes, manual drawing on the context) are not tracked and need an invalidate and everything else will be handled by the system.
Please remember that drawing on the screen is frequent process, whenever you update a view, that change should be propogated and redrawn to notify such change right. invalidate() is a trigger method,that signals force reDrawing of any view you wish to show changes for.
I had this problem when I wanted to draw a textPaint!
My code was
canvas.drawPaint(textPaintNumber)
canvas.drawText("MyText", 30F, 63F, textPaintNumber)
I cleared the first lint and the problem was solved
canvas.drawText("MyText", 30F, 63F, textPaintNumber)
Related
From Activity lifecycle we know, that in onResume the UI is visible. But if I set a breakpoint on onResume, I still don't see it, only after that. I heard there's some method which can check it, but I can't remind how it calls. We used it to make better animation. So how can I be fully sured that UI is ready?
Add global layout listener on view you want to check. Also please make sure that you remove listener once your work is done else it will keep getting called multiple times.
#onik shared a good link that should solve your problem
This ensures lay-outing. Drawing happens continuously and frame are refreshed according to sys clock.
I recommend watching: https://www.youtube.com/watch?v=Q8m9sHdyXnE
This will give you good idea on android drawing and layouting
I wish to sometimes (when a flag is on) update a few UI components in an Android activity every drawn frame (i.e., not while app is not visible, and not more than once per frame). How do I do this?
The question specifically states "every drawn frame", which suggests that overriding onDraw() in whatever it is that's being drawn will solve your problem exactly.
If that's not quite what you're going for, you should take a look at Choreographer, which invokes a callback once per display refresh. You must renew the callback every time it is invoked. To stop the callbacks when the Activity is in the background, establish the callback in onResume(), and set a flag in onPause(); if the callback sees the flag set, it doesn't renew itself.
The documentation for Choreographer notes higher-level API features that do common things. If one of those fits your needs, prefer that over the use of the lower-level API.
It seems that multiple invalidate will be coalesced for performance issues, but i'm encountering a case where this optimization will cause problems.
Say i have a custom view and it captures keydown and keyup event. in keydown, the view is in blue and in keyup it becomes red. But sometimes during the down and up processing, the onDraw will only be called once, it is likely because the two invalidate are coalsced. The result is that the view never becomes blue.
Anyone knows how to fix this?
Layout should happen very fast, even if it is said at some point in the future in the documentation.
Therefore, missing some event as you said implies that the redraw happens so late after being requested that the user see it, likely 1 second.
This hypothesis is only possible if you make huge work on Ui Thread, or something in your app slows the Ui Thread abnormaly.
So these considerations aren't to be taken into account in my opinion.
You should track the current color of your view using a boolean and when the view is actually drawn in the future, just draw it in the current color.
Changes being missed this way, should indicate they happenned so fast that the user cannot see them, which seems hard because the state of your view is based on user input.
I'm curious guys,
What are the exact cases doTraversal -> performTraversals is called? Since I have pretty heavy Activity, I want the application to call onDraw or the sort. performTraversals is pretty heavy, it is trying/measuring if it should resize views, stuff like that I guess. And I don't need it when I'm making some view, that no other view is dependent on, GONE, but I guess I can't skip that. So before digging into the source of Android, I want to ask. If you know it, please feel free to share :)
Thanks,
Danail
performTraversals() has many jobs but its three main roles are:
Measure views
Layout views
Draw views
Every time Android needs to redraw a window, performTraversals() is invoked. It does not mean however that measure/layout happens every time performTraversals() executes. You cannot skip performTraversals() if you are using a standard views (only SurfaceView lets you bypass this when drawing.)
I'm reading up on SurfaceView and how to use it, and I've come across some information that states that a SurfaceView has View#willNotDraw() set to false by default, and that it's up to you to call SurfaceView#onDraw(). I also read that RomainGuy said that this is done by default because it is more efficient. My question now is, when should you handle calling SurfaceView#onDraw() in a separate thread, and when should you just set View#willNotDraw() to true, and just call SurfaceView#invalidate(). Is there a difference between the two, and does one improve performance more than the other?
See:
http://developer.android.com/reference/android/view/View.html#setWillNotDraw(boolean)
I'm not sure where you got your information, but at least the javadoc says that most users will set this to false to get Android to send it onDraw events itself. As for your question about when you should do this, I would say it comes down to why you're using a SurfaceView.
If your view is displaying something dynamic (e.g. for a game or something that has a tight event loop), you'll want to be controlling exactly when updates happen, especially if you'll have the information to use one of the more detailed forms of invalidate to save redrawing the entire View. You won't want Android to call invalidate for you, and that's why the flag is there.
If, on the other hand, you are simply drawing something static, it makes sense to let Android's UI stack control the invalidations.
By the way, invalidate only posts a request to re-draw the View, so be aware of this if you intend to use the event-loop style (onDraw will be called sometime after you call it).
Edit: some clarifications.
Using SurfaceView.onDraw() and SurfaceView.invalidate() will make SurfaceView behave like a normal View and you will pay for the extra overhead associated with SurfaceView. If you want to draw from the UI thread, use a regular View instead. It's easier and cheaper.