What is the onMeasure method in android?
Ohhhhh, you know, I have heard about this onMeasure
method, but I have no idea how it works! Hopefully the folks at android, did provide some solid documentation. Wink, Wink Android :)
hmmm, let us first ask, what is onMeasure
? onMeasure
is a method, which is part of a View
lifecycle, and in this method, a View
is given the chance, of informing its parent, about its width and its height requirements, and the parent is given the chance, to inform the View
back, about the parenting requirements for this View
, with regards to width and height.
So, what you are saying, is that me, as a View
, I must in the onMeasure
method, call the setMeasuredDimension
method, which reports back, to my parent view, the width and height that I have measured.
Yehhh, says the parent, and if you do not call the setMeasuredDimension
method, in the onMeasure
method, this will cause a java.lang.IllegalStateException
.
Fair enough, the child says, anything else should I know?
Mmm, well I will provide you with two parameters, better, let me just tell you about the signature of the onMeasure
method.
void
onMeasure
(int widthMeasureSpec , int heightMeasureSpec )
So, what’s with these weird parameter names, widthMeasureSpec
, and heightMeasureSpec
, says the child?
Mmmm look! Simply stated, the parent says, I want you to provide me with measurement, but sometimes I have some requirements of my own. For example, I might require, that you have a fixed width, or a fixed height, or I might just tell you, look there is a max limit, on the width or height, or better, I might tell you, just do whatever pleases you.
Okay, that is a deal, the child says.
So, in other words, the parent continues, I need to pass on more than two parameters, I need to pass you the mode, which is the limitation that we have talked about, additionally I need to provide you with any width or height, that I might require.
Okay, okay, interesting, the child says.
Mmm, shifting …? Mmm, let us just forget about it. Okay, tell you what, with each of the two parameters, that I handed down to you, use the function MeasureSpec.getMode
, to know what limitation I require, and MeasureSpec.getSize
to get any width or height values.
Okay!! Okay!! Any example?
@Override
protected void
onMeasure
(int widthMeasureSpec , int heightMeasureSpec ){
int width , height;
//get any width or height
width = MeasureSpec .getSize (widthMeasureSpec );
height = MeasureSpec .getSize (heightMeasureSpec );
switch (MeasureSpec .getMode (widthMeasureSpec ) ){
case MeasureSpec .EXACTLY:
break; //width must be exactly the passed width
case MeasureSpec .AT_MOST:
break; //width can be at most the past width
case MeasureSpec .UNSPECIFIED:
width = 200; //Liberty of setting width to anything
break; }
switch (MeasureSpec .getMode(heightMeasureSpec )){
case MeasureSpec .EXACTLY:
break; //height must be exactly the passed height
case MeasureSpec .AT_MOST:
break; //height can be at most the past height
case MeasureSpec .UNSPECIFIED:
height = 200; //Liberty of setting height to anything
break; }
setMeasuredDimension (width, height ); }
As you notice, the parent making some eyebrow moves, we first got any passed width or height, using the MeasureSpec.getSize
function, later on we switched the mode, for both width and height, to check the passed requirements.
MeasureSpec.EXACTLY
my dear, means you need to do exactly, what I am telling you, so the dimension, must be set to the dimension that I have passed on.
MeasureSpec.AT_MOST
my dear, means you have some freedom, in setting the dimensions, to what pleases you, but there is an upper limit, which is the dimensions passed on.
MeasureSpec.UNSPECIFIED
my dear, means you are mature enough, so just tell me the sizing that suits you.
So, what if me, as a View
, I do not want to implement this onMeasure
method, the child says? Is there a default onMeasure
methods which I can use?
Mmm , Mmm, well, well, what do you know? It seems that I have a default onMeasure
method, implemented for me, which I can use, and which implementation is as follows:
void
onMeasure
(int widthMeasureSpec , int heightMeasureSpec ){
setMeasuredDimension
(getDefaultSize
(getSuggestedMinimumWidth ( ) , widthMeasureSpec),
getDefaultSize
(getSuggestedMinimumHeight ( ) , heightMeasureSpec ));}
So let me understand this default method, it seems to call the getDefaultSize
function, which if there are no spec mode limitations, will set the size to the passed size, which is in this case, gotten by calling the getSuggestedMinimumWidth
, and getSuggestedMinimumHeight
functions, which return respectively, the max of the View
minimum width and the background minimum width, and the max of the View
minimum height and the background minimum height , and If there is any mode limitation, the getDefaultSize
function, will set the size, in accordance with the passed on limitations, which is just the passed on dimension.
Yeh, Yeh! So where is this size used, the child asks?
Mmmm, why not in the onDraw
method, the parent replies, as in this example:
import android .content .Context;
import android .graphics .Canvas;
import android .graphics .Color;
import android .graphics .Paint;
import android .view .View;public class
Example
extends View{public
Example
(Context context ){
super(context); } @Override
protected void
onMeasure
(int widthMeasureSpec, int heightMeasureSpec ){ int width , height; //get any width or height
width = MeasureSpec .getSize (widthMeasureSpec );
height = MeasureSpec .getSize (heightMeasureSpec ); switch (MeasureSpec .getMode (widthMeasureSpec ) ){
case MeasureSpec .EXACTLY:
break; //width must be exactly the passed width
case MeasureSpec .AT_MOST:
break; //width can be at most the past width
case MeasureSpec .UNSPECIFIED:
width = 200; //Liberty of setting width to anything
break; } switch (MeasureSpec .getMode(heightMeasureSpec )){
case MeasureSpec .EXACTLY:
break; //height must be exactly the passed height
case MeasureSpec .AT_MOST:
break; //height can be at most the past height
case MeasureSpec .UNSPECIFIED:
width = 200; //Liberty of setting height to anything
break; }
setMeasuredDimension (width, height ); } @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); //set the color of the paint object to GREEN
Paint paint = new Paint ( );
paint .setColor (Color .GREEN ); // draw a rectangle ,
// it is positioning is
// left, top, right, bottom .
canvas .drawRect (20.0f , 20.0f , getWidth ( ) - 20.0f , getHeight ( ) - 20.0f , paint ); } }
What if I am a ViewGroup
, the View
says!? For example, what if I am a GridLayout
? Mmm, well in such a case, I have children, so I still have to implement the onMeasure
method, to report my dimensions to my parent, but additionally, my children onMeasure
method is called, in order for them to report their size to me. This reported size, additionally to any padding I made, I am going to use for calculating my final dimensions.
It might happen that onMeasure
might be called multiple times, with different mode requirements, as in for example, first not setting a constraint on the children, in order to figure out their requirements, and later, it might be, that there is some space left, so maybe just divide it between children, so in other words ,call the onMeasure
method once more, passing the determined mandatory size to the child.