What is the onMeasure method in android?

mohamad wael
5 min readJun 10, 2021

--

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.

--

--

No responses yet