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 View
s 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.
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()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With