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.
Related
I want to arrange a set of custom widgets inside a GridView. There are predefined templates which describe a count of elements and arrangement config. Max width and heigh for each of them have to be provided in order to avoid scrolling. How can I get a size of the displayed GridView when computing items size in adapter's "GetView" method?
You have access to few functions in order to get a GridView Height and Width as any other View. Look at the documentation here.
The size of a view is expressed with a width and a height. A view
actually possess two pairs of width and height values.
The first pair is known as measured width and measured height. These
dimensions define how big a view wants to be within its parent (see
Layout for more details.) The measured dimensions can be obtained by
calling getMeasuredWidth() and getMeasuredHeight().
The second pair is simply known as width and height, or sometimes
drawing width and drawing height. These dimensions define the actual
size of the view on screen, at drawing time and after layout. These
values may, but do not have to, be different from the measured width
and height. The width and height can be obtained by calling getWidth()
and getHeight().
Try doing a quick search on their doc next time, even if it's Xamarin, the Android doc is pretty reliable and you only need some adjustment to make it work.
(e.g.) : grivView.Width; instead of gridView.getWidth(); one being a property on Xamarin to follow C# standards, the other is a function.
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!
I am attempting to experiment in creating my own Views by subclassing "View." I want to simulate a new Button type class that will be drawn with the onDraw where I simply draw a rectangle from the canvas being passed in. I then add that custom View into the layout XML file, but I cannot control its size. For example, I set the width to "fill_parent" and the height to "wrap_content" but I don't know what the content of my View is. (BTW, the LinearLayout that my view is in is oriented to "vertical")
Can anyone give me some pointers in how to control the size of my View? For example, how does my custom view know what parameters were set in the XML file?
Android cannot detect the contents of your Canvas, so it assumes the contents are 0x0.
To do what you're looking for, you have to override onLayout or onMeasure (onLayout is specified by Android developers as a way to lay out children instead). You will also need a way to calculate the size of your contents.
First, start with a member variable called Point mSize = new Point(0, 0);. In a method other than onDraw, but called just as often, you will want to replace this with the size of your contents. If you draw a circle that has a radius of 25, do: mSize.x = 50; mSize.y = 50;.
Next, I'll introduce you to this answer (for onMeasure). It shows how to set the size of an item based on its parent's dimensions (divided by 2, in this case). Then, you can pull the size from mSize.x and mSize.y and apply those dimensions.
Finally, calling invalidate() after you measure your new mSize should force the View to lay itself out again, and adjust to your new parameters. This is the one part I'm unsure of in this regard.
NB: Also, you could call setLayoutParams using a fixed width and height once you calculate the size of your Canvas contents. Not sure of the performance implications for this, though.
The Android Documentation says that there is two sizes for a view, the measured dimensions and the drawing dimensions. The measured dimension is the one computed in the measure pass (the onMeasure method), while the drawing dimensions are the actual size on screen. Particularly, the documentation says that:
These values may, but do not have to, be different from the measured width and height.
So, my question is: what could make the drawing dimension be different of the measured dimension? If the onMeasure(int,int) method respects the layout requirements (given as the parameters widthMeasureSpec and heightMeasureSpec, how could the SDK decides that the view should have a different drawing size?
Additionally, how/where in the Android Source Code the measured width/height is used to compute the drawing width/height? I tryed to look into the View source code, but I can't figure out how the measuredWidth/Height is used to compute the final width/height. Maybe it has something to do with the padding, but I'm not sure.
As the name suggests the measuredWidth/height is used during measuring and layoutting phase.
Let me give an example,
A widget is asked to measure itself, The widget says that it wants to be 200px by 200px. This is measuredWidth/height.
During the layout phase, i.e. in onLayout method. The method can use the measuredWidth/height of its children or assign a new width/height by calling layout method of the view.
lets say the onLayout method calls childview.layout(0,0,150,150) now the width/height of the view is different than the measured width/height.
I would suggest not to use the measuredWidth/height outside onLayout method.
to summarize .
onMeasure -> sets up measuredWidth/measuredHeight
onLayout -> sets up the width/height of the widget.
additionallly
public void View.layout(int l, int t, int r, int b)
seems to be place where the assignment of position and size happens.
I'm creating a custom component, where can I know the dimensions this can assume on it's parent? In Layout it's defined with fill_parent x fill_parent.
I tried getWidth() x getHeight() on the constructor, on the onMeasure method and onFinishInflate, in all cases this return 0, I wish to know the size it's have to draw some components in independent screen size.
You can override onMeasure() and get values from there.
http://developer.android.com/reference/android/view/View.html#onMeasure(int, int)
In onMeasure you can actually set your own dimensions for the component and build component accordingly to sizes after that.
Use View.MeasureSpec.getSize(one of the parameters methods takes in. Either width, or height) to get dimensions.
getWidth() and getHeight() will only be populated after the Layout Phase, that is after onLayout() has been called.
You should not use the values within onMeasure() or getMeasuredWidth()/getMeasuredHeight(), since those are only the dimensions the View is initially offered or would like to have, not necessarily the ones it actually ends up getting.
See http://developer.android.com/guide/topics/ui/how-android-draws.html for more information.