Custom View onMeasure vs onSizeChanged vs onLayout - android

I have implemented some custom views in Android and one of the most frequent problems I have is to adapt the layout to the screen, weather by matching the parent's content, MATCH_PARENT, or wrapping its own content, WRAP_CONTENT. The ConstraintLayout, for instance, does not support WRAP_CONTENT.
The trouble starts when I have to adapt the children size, which initially I was doing on onLayout. One use case of this is when I want to have children which width and height respect a certain aspect ratio; and the width grow as much as possible.
So, in my onLayout, if changed was true I had reset constraints reset. However, it was causing a loop, since it changes the layout again.
What would be the proper place to size the custom widget children?
onSizeChanged or onMeasure?

Related

How does Android achieve the attribute "wrap_content"?

These days I am learning how to customize the view on Android.I know if we set the minimum width, then the system will compare the min width we set and the measured width and choose the proper one to fit the view.What I am confusing is that how does android system change the size of the view according to the content of the view as the word "wrap_content" means.I want to know more details about how to achieve "wrap_content". Thanks a lot.
This is what Official Documentation says (I am adding just a part of it for a quick read)
When a View object's measure() method returns, its getMeasuredWidth() and getMeasuredHeight() values must be set, along with those for all of that View object's descendants. A View object's measured width and measured height values must respect the constraints imposed by the View object's parents. This guarantees that at the end of the measure pass, all parents accept all of their children's measurements. A parent View may call measure() more than once on its children. For example, the parent may measure each child once with unspecified dimensions to find out how big they want to be, then call measure() on them again with actual numbers if the sum of all the children's unconstrained sizes is too big or too small (that is, if the children don't agree among themselves as to how much space they each get, the parent will intervene and set the rules on the second pass).
The measure pass uses two classes to communicate dimensions. The ViewGroup.LayoutParams class is used by View objects to tell their parents how they want to be measured and positioned. The base ViewGroup.LayoutParams class just describes how big the View wants to be for both width and height. For each dimension, it can specify one of:
MATCH_PARENT, which means the View wants to be as big as its parent (minus padding)
WRAP_CONTENT, which means that the View wants to be just big enough to enclose its content (plus padding).
MeasureSpec objects are used to push requirements down the tree from parent to child. A MeasureSpec can be in one of three modes:
UNSPECIFIED: This is used by a parent to determine the desired dimension of a child View. For example, a LinearLayout may call measure() on its child with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how tall the child View wants to be given a width of 240 pixels.
EXACTLY: This is used by the parent to impose an exact size on the child. The child must use this size, and guarantee that all of its descendants will fit within this size.
AT MOST: This is used by the parent to impose a maximum size on the child. The child must guarantee that it and all of its descendants will fit within this size.

Create custom View group(Diagonal Layout)

I want to create a custom diagonal layout like androids predefined linear layout..so i started with a class extending ViewGroup and overrided OnLayout and OnMeasure methods..However I am finding it difficult to understand where to write the logic for alligning all the Views such that they will be placed diagonally
I read few blogs on it How to Create Custom Layout in Android by Extending ViewGroup Class
Also followed the google I/O video
But still not clear about how to get started with creating a diagonal layout..can someone suggest any additional resources in this regard..???
The logic to measure children should be in onMeasure. In here, you want the sum of all children height to be as big as the height given in the heightMeasureSpec. Same goes for the width.
To do so, you can divide the width and height from measureSpec by the number of children, and call the child views measure method with those computed values.
The logic to position the children is in onLayout. In here, you call the child views layout method one after another, passing it positions (left, top, ...) incremented after layouting each child.
You must take care of child views margins in both methods, as well as your container padding.
Lucas Rocha has a very good article (with lot of reverse engineering) for custom views.

Propagate change of View dimensions to children / parents

In my Android app I want to change the height of a RelativeLayout in an animation. The layout contains a hierarchy of children, which are all the same size as the parent.
If I change the height of the parent view by adjusting its LayoutParams, the children keep their sizes. Changing the inner child's size also doesn't change the parent's size, and changing the size of all of the View at the same time also doesn't seem to work.
How do I increase the height of several nested views at the same time?

Android onMeasure and draw()

I Have some really strange problems.
First problem is as follow:
I read how to override onMeasure(). I did it. I was expecting that when I set width/height in onMeasure that they will be exactly the same in onDraw, but apparently this is not true.
Also I except that measuredWidth will be same as width in draw phase, but again it isn't true.
onMeasure could be called many times. On each call I invoke setMeasuredDimension with calculated width and height. So for example, first time my view should be 480 x 245, on second call I recalculate it again, based on parent of course, and it should be 90 x 245.
For my great surprise android somehow is just ignoring everything but the first call. So in this case my final view size is 480 x 245.
Second issue which is as follow:
My view height is match_parent, my parent height is 0. So how am I supposed to set right height on onMeasure when I don't know it ?
Any Idea how to make android not to ignore my setMeasureDimensions calls and how to set match_parent ?
The space that's allocated to your view doesn't depend only on the size you measured, here's a snapshot of the process :
View.onMeasure gets called during the measurement of your parent. You got your MeasureSpec that is essentially how much space at most you can take (very summarized).
Given these specs, you determine the size of yourself, logically measuring your own children and calling setMeasuredDimension
A while after, your parent assigns you concrete dimensions, based on what you measured (but this also means it can be different). Then as these will be your dimensions, that's the one you have to use. The callback called at this point is onLayout, and you shall layout your children in the process, based on the dimensions that were affected to you.
After all this, your View will be drawn, that is View.dispatchDraw being called and resulting for simple views to onDraw. Drawing yourself also means drawing your children if you're a ViewGroup.
When drawing, the system passes a Canvas whose dimensions are these of the screen, and using translation and clipping, the canvas is passed along views so that they draw themselves. This avoids allocation during draw. For this reason, if your want to know what space is dedicated to you, you should not use Canvas.getWidth or Canvas.getHeight which are the dimensions of the screen, but rather retrieve the clipped dimensions (Canvas.getClipBounds())
And finally, when you correctly retrieve the clip bounds, they should usually be the same as your width and height (View.getWidth or View.getHeight), but they might be different, for example if the canvas was scaled.
So to summarize :
onMeasure purpose, is to determine the size of children, so that ViewGroup can compute they're dezired size.
onLayout purpose is to affect a width and height to each view, propagating to children.
onDraw purpose is to render your view.

What is an Android view's measured state?

I'm having a hard time finding example usage, or explanations, of the Android View concept of "measured state".
To be clear, this is different from measured width and measured height. See in Android docs.
The general idea is that a View has a desired width and height, which is constrained by two factors.
The first constraint is the parent View to which the View is attached. This will describe the maximum width and size of the View. If the View has asked to be wider or taller than the parent view, then it's measured height and measured width will be constrained such that the values are no greater than the parent's. These values, which can be MATCH_PARENT, WRAP_CONTENT or a dp value, get converted to measured widths and heights once the parent View's width and height constraints are included. This guarantees that no child is larger than it's parent.
The second constraint comes from the View's siblings, or in other words, from other Views attached to the same layout. The ViewGroup (Layout) will resolve a View's width and height such that it displays correctly. These resolved width's and height's are the values you get from getWidth() and getHeight().
There is a lot of good documentation on the Android development portal. In particular look at the Layout section of the View class: http://developer.android.com/reference/android/view/View.html#Layout
Edit: Sorry for misunderstanding your question. I've taken a look at the concept of measured state for a view, and I can only find a single documented state: MEASURED_STATE_TOO_SMALL. This leads me to believe that it's use may be very limited and it's existence is primarily for the purpose of future functionality or to be made use of by custom Views/ViewGroups.
The documentation for MEASURED_STATE_TOO_SMALL states the following:
Bit of getMeasuredWidthAndState() and getMeasuredWidthAndState() that
indicates the measured size is smaller that the space the view would
like to have.
This leads me to believe that the bit will be set whenever a fixed dp/px value is given to the View which is larger than the parent's width and height and therefore the View's measured width and height will have been scaled down.
If you manage to find more states or additional information as to where it is used, please to update the question. All the best!

Categories

Resources