Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override a RelativeLayout's onMeasure() to constrain its aspect ratio

I have an extended RelativeLayout which, when programmatically positioned and sized using RelativeLayout.LayoutParams, needs to restrict itself to a given aspect ratio. Typically, I would wish for it to constrain itself to 1:1, so that if the RelativeLayout.LayoutParams contain a width of 200 and a height of 100, the custom RelativeLayout would constrain itself to 100 x 100.

I am already used to overriding onMeasure() in ordinary custom Views in order to achieve similar aims. For example, I have created my own SVG image converter, and the custom View that renders the SVG image has an overridden onMeasure() that ensures that the call to setMeasuredDimension() contains dimensions that (a) fit within the original measurement specifications, and (b) match the aspect ratio of the original SVG image.

Going back to my custom RelativeLayout which I wish to constrain itself in a similar way, I've tried overriding onMeasure() but I haven't had much success. Knowing that RelativeLayout's onMeasure() performs all of the child View placement, what I'm generally trying to do at the moment, but without the desired results, is to override onMeasure() such that I initially modify the dimension specifications first (i.e. apply my desired constraints) and then call super.onMeasure(). Like this:

@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);      
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    // Restrict the aspect ratio to 1:1, fitting within original specified dimensions
    int chosenDimension = Math.min(chosenWidth, chosenHeight);
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

What actually happens when I do this is that, bizarrely, the height is properly restricted as I intended, but the width is not. To illustrate:

  • Specifying a height of 200 and width of 100 in the RelativeLayout.LayoutParams results in my custom RelativeLayout having a height of 100 and width of 100. -> Correct.

  • Specifying a height of 100 and width of 200 in the RelativeLayout.LayoutParams results in my custom RelativeLayout having a height of 100 and width of 200. -> Not correct.

I realise that I could instead apply my aspect ratio constraint logic within the calling class that's placing the RelativeLayout in the first place (and in the meantime I may well do that to get around this), but really this is an implementation detail that I want the RelativeLayout itself to perform.

Clarification: The resultant width and height values I'm reading back are from using getWidth() and getHeight(). These values are read back some time in the future, after the layout process has been performed again.

like image 974
Trevor Avatar asked Dec 04 '22 05:12

Trevor


1 Answers

I have got around this now by also setting the width and height of the LayoutParams currently held by the RelativeLayout in the onMeasure().

@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);      
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    // Restrict the aspect ratio to 1:1, fitting within original specified dimensions
    int chosenDimension = Math.min(widthSize, heightSize);
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);

    getLayoutParams().height = chosenDimension;
    getLayoutParams().width = chosenDimension;
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    
    }

This now works as desired: The size of the RelativeLayout (and subsequent calls to getWidth() and getHeight()) now agree with size restrictions applied in my overridden onMeasure().

like image 99
Trevor Avatar answered Jan 25 '23 23:01

Trevor