I have seen a lot of examples of creating custom views and layouts in android. What I've learned from them is that the measure (as said on Android developer's site) method is onMeasure(), which has widthMeasureSpec and heightMeasureSpec as parameters.
What is the actual meaning of those parameters?
What are their initial values?
With what values they are called if the custom view that I am creating is the parent view for my activity?
I am really very confused about these questions.
widthMeasureSpec and heightMeasureSpec are compound variables. Meaning while they are just plain old ints, they actually contain two separate pieces of data.
The first part of data stored in these variables is the available space (in pixels) for the given dimension.
You can extract this data using this convenience method:
int widthPixels = View.MeasureSpec.getSize( widthMeasureSpec );
The second piece of data is the measure mode, it is stored in the higher order bits of the int, and is one of these possible values:
View.MeasureSpec.UNSPECIFIED
View.MeasureSpec.AT_MOST
View.MeasureSpec.EXACTLY
You can extract the value with this convenience method:
int widthMode = View.MeasureSpec.getMode( widthMeasureSpec );
You can do some logic, change one or both of these, and then create a new meassureSpec using the last convenience method:
int newWidthSpec = View.MeasureSpec.makeMeasureSpec( widthPixels, widthMode );
And pass that on down to your children, usually by calling super.onMeasure( widthMeasureSpec, heightMeasureSpec );
In onMeasure() the MeasureSpec pattern serves the purpose of passing in the maximum allowed space your view and it's children are allowed to occupy. It also uses the spec mode as a way of placing some additional constrains on the child views, informing them on how they are allowed to use the available space.
A good example of how this is used is Padding. ViewGroups take the available width and height, and subtract out their padding, and then create a new meassureSpec, thus passing a slightly smaller area to their children.
This article will be helpful for you. I'm answering your question from the context of the article.
1. What is the actual meaning of those parameters?
Answer:
widthMeasureSpec Horizontal space requirements as imposed by the parent view to the child view.
heightMeasureSpec Vertical space requirements as imposed by the parent view to the child view
2. What are their initial values?
Answer:
when MODE_SHIFT = 30 when values are -
MeasureSpec.UNSPECIFIED = 0 << MODE_SHIFT = 0
MeasureSpec.EXACTLY = 1 << MODE_SHIFT = 1073741824
MeasureSpec.AT_MOST = 2 << MODE_SHIFT = 2147483648
3. With what values they are called if the custom view that I am creating is the parent view for my activity?
Answer:
It will depend on what height and width you give in the parent view. You will get a good insight about it in the last part of the article which also shows a chart below I mentioned -
Related
I konw the difference between getWidth() and getMeasuredWidth(),
but I can not understand the difference between MeasureSpec.getSize(widthMeasureSpec) and getWidth(),
which width does MeasureSpec.getSize(widthMeasureSpec) get?
thx~~
MeasureSpec.getSize(widthMeasureSpec) returns the preferred width or the specified witdth of a view you want to create. This may or may not be the same as getWidth() after the view has been created, as views may change size based on the width of other views, screen orientation etc.
Ok lets see.
getMeasuredWidth() returns the width measurement set in onMeasure() via setMeasuredDimension().
getWidth() returns the final width of the view. The Parent view may accept the measurements set in onMeasure (set via setMeasuredDimension()). In which case getWidth and getMeasuredWidth will return the same value. If the Parent view decides that the measurements you set cannot be used, either because it's too big or too small then it will set the final width to something more appropiate. In which case getWidth will have the final valid width and will be different from getMeasuredWidth(). Always use getWidth() in onDraw(). only use getMeasuredWidth() if you want to know if the parent changed the width you previously set in onMeasure().
Finally to explain MeasureSpec.getSize(widthMeasureSpec) you need to understand the following from the official documentation:
If you need finer control over your view's layout parameters,
implement onMeasure(). This method's parameters are View.MeasureSpec
values that tell you how big your view's parent wants your view to be,
and whether that size is a hard maximum or just a suggestion. As an
optimization, these values are stored as packed integers, and you use
the static methods of View.MeasureSpec to unpack the information
stored in each integer. Custom View Components
As you can read from the snippet. WidthMeasureSpec and WeightMeasureSpec are not pixel measurments. To get the actual pixel value use MeasureSpec.getSize():
int width = MeasureSpec.getSize(widthMeasureSpec)
PD:This all applies to the height as well
For more Infomation see my answer here(its not marked as the answer):
MeasureSpec returns a 0 value
I have custom view with child items, which are configureable via xml. However they are can be configurable in runtime via something like a Configuration class. After that I just notify parent view about changes and all is ok.BTW. My question in fact touches measurement: I can change child items size in runtime, but for the first launch I want to set to all of them size (width and height) accordingly to layout params defined in xml.
Maybe some code will add more clarification to you.
protected int getItemWidth() {
if (cell != null) {
int width = cell.getWidth();
return width == 0 ? <layout_width_defined_in_xml> : ScreenUtils.convertToDp(context, width);
} else {
return canvasWidth;
}
}
So, I want to know. Is possible to get layout params before measurement? And how to that?
The size defined in XML isn't always the right size the view should have when it's actually laid out. For example, a child of a LinearLayout may have android:layout_width="0", but might have nonzero width because of android:layout_weight. (There are other examples as well with other kinds of layouts.) Additionally, the values match_parent and wrap_content map to negative integer values in java code, which I don't think is helpful to you here.
If your custom view is interested in measuring and positioning child views, you should be overriding onMeasure() and onLayout(). If you aren't doing that or if you don't need to do that, getWidth() and getHeight() will tell you a view's actual size, and getMeasuredWidth() and getMeasuredHeight() will tell you the measured size (which can differ from the actual size). THe only caveat is that you have to wait for the first measure/layout before calling those 4 methods because otherwise those methods all return zero.
If you do want to inspect the layout parameters of a view, you can do
LayoutParams params = view.getLayoutParams();
int width = params.width;
int height = params.height;
As noted, either or both of those may be negative. You can compare them to LayoutParams.MATCH_PARENT and LayoutParams.WRAP_CONTENT to check for these cases, but again I'm not sure this is helpful to you if you aren't implementing onMeasure() and onLayout().
You can do this in callback ViewTreeObserver.OnGlobalLayoutListener. You can get view dimensions here and set size other views with these dimensions.
I am working on an Android app that is largely driven by code, not xml.
The Adapter for a ListView wants to know the height of the item. But this in turn could depend on the size of UI elements within the item. For example, if the item contains a checkbox, the size of the checkbox could influence the layout of the list item, which in turn could change the height, depending on whether or not a line wrap became necessary.
Question -- is there a way to get the size of an Android UI element without actually creating it? Similar to the way one can use a Paint or TextPaint object to get the size of text before it is drawn.
Without creating it? No, but you could use the MeasureSpec class and measure the View manually after you create it. Why do you need to know the exact size? Can't you just set the LayoutParams to MATCH_PARENT (width) and WRAP_CONTENT (height) for your case?
That said, if you do need to know, you could use the width of your ListView for the width MeasureSpec, and then UNSPECIFIED for the height MeasureSpec:
// Tell the View it should be exactly as wide as the ListView...
int widthSpec = MeasureSpec.makeMeasureSpec(listViewWidth, MeasureSpec.EXACTLY);
// ... and as tall as it wants to be ...
int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
// ... measure it with these constraints ...
item.measure(widthSpec, heightSpec);
// ... and retrieve the measured height.
int itemHeight = item.getMeasuredHeight();
TextView.getMinHeight() appears in API 16. But TextView.setMinHeight() appears in API 1. Where is any possible way to get min height in pre API 16 version without reflection?
Use ViewCompat.getMinimumHeight(view) from support-v4 library.
There's no way to get it without reflection.
The field is called mMaximum. It can hold a value in pixels or in lines of text, that's why it's not called mMaxHeight.
Saving the value in setMaxHeight() is not ideal, because there's also setMaxLines() method, which changes the max mode and effectively clears the max height value. You would have to overload all methods writing mMaximum field.
short answer:
View.getLayoutParams().height
View.getLayoutParams().width
other possibilities :
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().
ways:
using the famous OnGlobalLayoutListener
This is one of the most used mechanisms to get the view dimensions. You attach a Global Layout Listener to the view hierarchy. It helps you actually get the width of all the views in your view heirarchy:
by forcing a measurement of the View
View.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int widht = View.getMeasuredWidth();
int height = View.getMeasuredHeight();
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.