Android view's post method - android

I wanted to know if View.post(Runnable r) runnable's run() method is executed after View is drawn or after View (in case it is a ViewGroup) and all it's children are drawn?

It will be put into UI message queue, and it depends on internal Android workings when exactly will be executed. If rendering of child views is in one message then it might execute after rendering, if it is split by android into several messages then your message might possibly be executed in between of rendering.
If you want to make sure it will be executed after child redraw, maybe use View.postDelayed with some small delay?
What event is fired after all views are fully drawn?
here is explanation from Romain Guy that it is actuall executed after redraw - if nothing changed (its 4 year old SO), then you might trust this answer.

Related

Why is the post() method in Android attached to a specific View?

I'm wondering why the post() method is a specific to a View and not just a static method. It doesn't seem like the Runnable argument is closely tied to a specific view anyway.
I did find this other question which explains that (as of 4 years ago) the runnable will be run after the view has been drawn, but that doesn't quite answer my question.
What event is fired after all views are fully drawn?
Because it decides which handler to post the event to based on a few different criteria. Check the code at the AOSP. If the view is attached to a window, it uses the one in the attach info. If not, it uses the ViewRoot.getRunQueue and posts it there. So there's a possibility of it posting to different handlers if, for example, you had a view in a different window like a Toast you were calling post on. Thus the need for a non-static function.

Should I use SurfaceView or View for a frequently updated view (15fps)

I am building an application where I need to display signal traces in real time (think the kind you see on cardiac monitors in hospitals). For my line animation to appear smoothly I'd like the frame rate to be around 15 frames per second (I did some experimenting to come to the conclusion this was the lowest acceptable frame rate). It probably does not matter in the context of this post but I potentially have numerous such view in a ListView (~20 is common with about 5 being displayed each time).
Until now I've been doing this using a custom view, extending the View class. I have a thread in the containing fragment that calls invalidate() on the view every ~70ms that redraws the view. This is not causing problems per se as I've optimized my onDraw() function to run in under 2ms most of the time.
Now I have added a Spinner to the fragment and while debugging it I noticed that once I opened the Spinner the adapter was constantly hitting getView() calls, even though I was not touching the Spinner (i.e. open but not scrolling) and also lagging a bit. This led me to realize that my whole fragment was being redrawn every ~70ms which to me sounds bad. Now the questions:
Is there a way to trigger onDraw() on a child view without it causing a redraw of the complete hierachy?
Should I just resort to a SurfaceView? (that would not cause a complete view hierarchy redraw, right?)
I've noticed that the SurfaceView is not HW accelerated. Does that matter if I'm only using basic draw functions (drawLine() and drawText())?
Would GLSurfaceView be any better in my case?
I'll try to answer my question so that it might be useful to other people who might encounter the same.
Containing a bunch of SurfaceViews inside a ListView is a fail. It seems to me since the SurfaceView drawing is not synced with the rest of the UI you will get black lines flickering between views when you scroll (which is probably since the SurfaceView is reassigned to new data source via getView() and displayed before it gets a chance to redraw itself). Triggering a redraw inside getView() was not enough, apparently.
Delyan's first comment to my question was valid even though it might not always apply. If I go through each view in the ListView by hand and call invalidate() on it the redraw will not bubble up through the hierarchy (I did not need the invalidate(l,t,r,b) signature, but it's probably a smart idea to try it out if you're having problems with excessive redraws). So, even though Romain Guy mentions in his talk that invalidate() will bubble up to the root it's apparently not always the case. Delyan's comment about different implementation of HW/SW might be the reason - I'm rendering my views in HW.
Anyhow, to work around the issue I created this method inside my adapter:
public void redrawTraces(ListView lv) {
final int viewCount = lv.getChildCount();
for(int i=0; i < viewCount; i++) {
final View stv = lv.getChildAt(i);
if(stv != null) {
stv.invalidate();
}
}
}
Where the views were not SurfaceViews. Finally, to make it clear what I was doing wrong, I was doing this:
adapter.notifyDataSetChanged();
This call did bubble up and caused a redraw through the entire hierarchy. My assumption was that this was roughly the equivalent of calling listView.invalidate() in terms of updating the whole hierarchy; perhaps that's correct I didn't look into it.

android change UI from background thread - why it's working?

Will ever crash an application that changes text, background resource and inflate some views in it in a background thread, if the view's visibility is GONE ? I've done some tests with two buttons, one visible and one gone and if I changed text of the visible one in background thread, it crashed and when I've changed the text of the button with GONE visibility, it worked without a crash. Can someone explain this?
Ok, I have a something for you.
Event Handling and Threading
The basic cycle of a view is as follows:
An event comes in and is dispatched to the appropriate view. The
view handles the event and notifies any listeners.
If in the course of processing the event, the view's bounds may need
to be changed, the view will call requestLayout().
Similarly, if in the course of processing the event the view's
appearance may need to be changed, the view will call invalidate().
If either requestLayout() or invalidate() were called, the framework
will take care of measuring, laying out, and drawing the tree as
appropriate.
Note: The entire view tree is single threaded. You must always be on the UI thread when calling any method on any view. If you are doing work on other threads and want to update the state of a view from that thread, you should use a Handler.
You can find much more here http://developer.android.com/reference/android/view/View.html .
In your case Your View is already GONE so I think its not attached to View Tree.
You have to create an Handler!
In that handler put your code to update your UI and in the thread call the Handler!
You should never update the UI from an thread directly in none of langages! it's a big error in programming!
Example to create your handler:
static Handler updater1 = new Handler() {
#Override
public void handleMessage(Message msg) {
//your code to update the UI
}
};
Example to call your handler from the thread:
updater1.sendEmptyMessage(0);
The behaviors about to change the text from a thread can be different in all langages! but you should never do that it's a big error!Maybe when you tried to update a text on a button with visibility "GONE" it just not worked! but you can't see it!

View.post(), and when Runnables are executed

My original problem was needing to know the height and width of my root View so that I could make programmatic layout changes. For my purposes, I don't necessarily need to determine this information during onCreate(); it's quite sufficient for me to programmatically add my child Views after layout of the root has completed, so therefore I'm happy to use onWindowFocusChanged() as a hook to determine when calling the root View's getWidth() and getHeight() will return valid results.
However, I see quite a few people have asked how to determine the root View's size during onCreate(). I'm guessing people want to do this for better user experience - perhaps so that users don't see the layout being built in stages (if anyone can clarify the reasons, I'd appreciate it). Now, the answer I have commonly seen given to this, such as the one here, is to post a Runnable to the root View within onCreate(). Inside that Runnable we perform the getWidth(), etc. The reason why this works is because, apparently, Runnable objects posted to a View are executed when the View has layout and / or is attached to the window.
This brings me to my question. In the API documentation for View, it doesn't seem to describe this behavior. So, can anyone tell me where it is defined and documented? Or, is it a matter of inspecting the source? Is it an absolutely and rigidly defined feature that Runnables will stay in a View's queue and only be handled at a certain point after the layout process?
Further clarification: On reading Kerry's answer and thinking it over a bit more, my basic question can be clarified as follows: In the answer given here and also in this CodeProject entry, I understand that we get around the problem of the root View's dimensions not being available during the onCreate() by posting a Runnable to the View. As Kerry points out, the guarantee is that this message Runnable cannot be executed until onCreate() has executed. I understand that layout occurs some point after onCreate, but I still can't grasp at the moment why this Runnable should execute when the View's dimensions are known.
I had a look at the View.post(Runnable) method and it seems to me that by calling that method you are just adding a message to the UI thread queue. The only thing that is going to be 'guaranteed' is that the Runnable will be executed sometime AFTER the method which has called post(Runnable) has completed. This assumes you are calling post(Runnable) from the UI thread but as the docs say:
This method can be invoked from outside of the UI thread only when this View is attached to a window.
I think to be sure that both your Root view and Child view both have size, you would need to call post(Runnable) from the child onSizeChanged() method because if the child has size I think it follows that the Parent i.e. Root view therefore must have size too.
Apologies if this is a bit of a rambling answer. I kind of understand what you're wanting to achieve but not 100% sure. Let me know if you want me to clarify anything.
Personally whenever I've needed the size of a View I do everything in onSizeChanged() which I do believe is the 'correct' way of doing it and it has always worked.

Why use the word "invalidate" to request a view to be redrawn?

In GUI, when a view is required to be redraw. We usually do this as follows:
View v = View ();
v.invalidate()
I have no idea that the operation "invalidate()" is such named. In English, "invalidate" means to make something not valid. But "invalidate a view" in GUI programming is meant to make the view to be redrawn. I am not a native English speaker. Please give me hints. Tks.
To "invalidate a view" means that any data that may already be drawn is no longer valid. The view of the data is invalid, hence invalidate() to mark it as such. It's less that the function is making it invalid so much as it is already invalid, but no-one except the caller to invalidate() knows that yet. The purpose of the function is to tell the rest of the code that the views data is now invalid.
Invalidate is semantically different from redrawing in that it usually only marks something to be redrawn later, as opposed to redrawing at call-time. The idea is that when several possibly overlapping areas are invalidated, the amount of redrawing at a later time can then be made equal to only the sum of non-overlapping areas. That is, we may redraw only once for several subsequent invalidations. Hence the distinction between redrawing and invalidation exists.

Categories

Resources