I have created a custom view to handle my bottom sheet implementation. I have tried to retrieve the bottomsheet behavior using BottomSheetBehavior.from( this ) but since I need the behavior in the constructor, this is not yet initalized.
For now, I've managed a workaround using postDelayed runnable at 1ms to get the behavior and do what I need but that is clearly a hack.
I have been unable to find any other ways to get the behavior.
[EDIT]
After looking a bit deeper, this not being initialized does not seem to be the problem. The problem is that the LayoutParams are not an instance of CoordinatorLayout.LayoutParams when BottomSheetBehavior.from is called directly from the constructor but they are CoordinatorLayout.LayoutParams when called from the runnable created in the constructor.
After looking through the View documentation a bit more, I moved the calls from the view's constructor to its onAttachedToWindow method.
I find this to be much cleaner than using a runnable.
Related
I'm not sure about the usage of onAttachedToWindow.
My question is basically about the documentation.
Which mentions:
[...] it may be called any time before the first onDraw --
including before or after onMeasure(int, int)
I what to know:
when it is called before onMeasure and when is it called after.
The Story behind:
I am adding OnGlobalLayoutListener in onAttachedToWindow and remove it in onDetachedFromWindow. Because is somehow logic to me to handle the layouting when the view is added to the window.
But I am concerned that the first onGlobalLayout calls get lost, if the Listener is not added yet. (because onMeasure usually happens during the layouting)
If someone got a better approach for my problem, feel free to give me a hint.
Docs are correct, and you should not rely on onWindowAttach/Detach being in sync with onMeasure or onLayout pass.
If your View class is interested in parent hierarchy changes, I'd advice against such a design. The Parent UI should notify sub-views of hierarchy changes. So, OnGlobalLayoutListener better be used by an enclosing UI class.
Also, View class has onSizeChanged() that you can override to detect when it has been measured up.
We know ViewTreeObserver is used to register listeners that can be notified of global changes in the view tree. There are two method defined in this class are
addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) and addOnPreDrawListener(ViewTreeObserver.OnPreDrawListener listener). But the the purpose of these methods are not clearly understandable to me which described in the developer site. I tried to understand it and also searched in google but was unable to find clear picture about this topic. Any help is greatly handful for me.
OnPreDrawListener
Gets called just before onDraw() method gets invoked. At this point, all views in the tree have been measured and given a frame. Therefore you can properly manipulate view in this callback
OnGlobalLayoutListener
This listener gets called:
- when visibility state changes. In example when view has been drawn it becomes visible and this gets called.
- when you addView state of view tree changes
I am populating a linear layout dynamically. Depending upon response, I have to clear the past child views and create new views. I have read the document, but still be confused with the couple methods, they all look the same function. Which function I should use.
As Scott Biggs points out, the difference is not a big one. The only difference is that removeAllViews() calls requestLayout() and invalidate() afterwards. The key to why this difference is here is to understand the naming of removeAllViewInLayout(). Confusingly, its meaning isn't "remove all views within this view layout."
If we look at the similar method, removeViewInLayout(), we can understand what it's supposed to mean:
Removes a view during layout. This is useful if in your onLayout() method, you need to remove more views.
So removeAllViewsInLayout() actually means "remove all views, and we're calling this method during a layout pass (i.e. onLayout())". That's why removeAllViewsInLayout() doesn't call through to requestLayout(), as it's assumed that you're already currently inside a layout pass, so requesting another layout pass is unneeded.
If you use removeAllViewsInLayout(), then it's your responsibility to ensure that you're calling this during a layout pass, or to properly call requestLayout() and invalidate() as needed.
removeAllViews() : Call this method to remove all child views from the ViewGroup.
removeAllViewsInLayout() : Called by a ViewGroup subclass to remove child views from itself, when it must first know its size on screen before it can calculate how many child views it will render.
Well, looking at the source, there isn't much difference:
public void removeAllViews() {
removeAllViewsInLayout(); // Details implemented here
requestLayout();
invalidate(true);
}
So unless you want to call invalidate() at a time of your choosing, you might as well use removeAllViews() and save yourself a bit of typing.
EDIT
For a more detailed explanation, see David Lui's answer. To sum it up, use removeAllViews() unless you're in the process of constructing a View--in which case you'd call removeAllViewsInLayout().
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.
I have a ListView with custom Adapter. To be honest, I have many of them at the same time on screen, and my Tegra 3 device started to lag, what made me really confused... I found than in each ListView's Adapter the getView() method is called for all visible rows every time any animations runs on screen. That gives me like few hundreds of calls per second! Digging more, most of these calls are due to measure() and onMeasure() calls of ListViews' parents, and - this is tke key - they are useless, because all the layouts of my ListViews
have const size.
So my question is: how to eliminate these calls? Of course I want to leave proper calls alone (caused by adding items to Adapter and notifyDataSetChanged() ).
I've tried almost anything, but either the whole list doesn't draw itself (when I overriden it's onMeasure() and forced to returned const size without calling super.onMeasure()) or stops updating at some time.
How you implemented the getView() method? If you implement it in the correct way there should be nearly no lagging.
Check out this really really good video:
http://www.youtube.com/watch?v=wDBM6wVEO70
Slides: http://dl.google.com/googleio/2010/android-world-of-listview-android.pdf
As Romain said, work with it not against it. Best is to leave measure() alone and focus on your adapter.
Thats how ListView is implemented.. I don't think that will cause a performance Overhead.. Provided you do things properly there..
For example..
Don't instanciate LayoutInflator inside GetView Method, Do it at class level..
And Inflate View Only if the convertView==null or else just return convertView.. Inflating view is a costly process....
Well like you said these calls are due to measure() and onMeasure() calls of ListViews parents and I'm sure you are using height=wrap_content also with wrap_content on height your ListView will check without stop if your height has changed.
So the solution is to put the height=fill_parent.
I hope this helped you.
The underlying reason for this is that ListView.onMeasure() calls AbsListView.obtainView(), which will request a view from your list adapter. So if your view is being remeasured through animations, your performance will be very poor.