package com.arcao.trackables.ui.widget.util; import android.view.View.MeasureSpec; /** * This class is a helper to measure views that require a specific aspect ratio.<br /> * <br /> * The measurement calculation is differing depending on whether the height and width * are fixed (match_parent or a dimension) or not (wrap_content) * * <pre> * | Width fixed | Width dynamic | * ---------------+-------------+---------------| * Height fixed | 1 | 2 | * ---------------+-------------+---------------| * Height dynamic | 3 | 4 | * </pre> * Everything is measured according to a specific aspect ratio.<br /> * <br /> * <ul> * <li>1: Both width and height fixed: Fixed (Aspect ratio isn't respected)</li> * <li>2: Width dynamic, height fixed: Set width depending on height</li> * <li>3: Width fixed, height dynamic: Set height depending on width</li> * <li>4: Both width and height dynamic: Largest size possible</li> * </ul> * * @author Jesper Borgstrup */ public class ViewAspectRatioMeasurer { private double aspectRatio; /** * Create a ViewAspectRatioMeasurer instance.<br/> * <br/> * Note: Don't construct a new instance everytime your <tt>View.onMeasure()</tt> method * is called.<br /> * Instead, create one instance when your <tt>View</tt> is constructed, and * use this instance's <tt>measure()</tt> methods in the <tt>onMeasure()</tt> method. * @param aspectRatio */ public ViewAspectRatioMeasurer(double aspectRatio) { this.aspectRatio = aspectRatio; } /** * Measure with the aspect ratio given at construction.<br /> * <br /> * After measuring, get the width and height with the {@link #getMeasuredWidth()} * and {@link #getMeasuredHeight()} methods, respectively. * @param widthMeasureSpec The width <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method * @param heightMeasureSpec The height <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method */ public void measure(int widthMeasureSpec, int heightMeasureSpec) { measure(widthMeasureSpec, heightMeasureSpec, this.aspectRatio); } /** * Measure with a specific aspect ratio<br /> * <br /> * After measuring, get the width and height with the {@link #getMeasuredWidth()} * and {@link #getMeasuredHeight()} methods, respectively. * @param widthMeasureSpec The width <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method * @param heightMeasureSpec The height <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method * @param aspectRatio The aspect ratio to calculate measurements in respect to */ public void measure(int widthMeasureSpec, int heightMeasureSpec, double aspectRatio) { int widthMode = MeasureSpec.getMode( widthMeasureSpec ); int widthSize = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( widthMeasureSpec ); int heightMode = MeasureSpec.getMode( heightMeasureSpec ); int heightSize = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( heightMeasureSpec ); if ( heightMode == MeasureSpec.EXACTLY && widthMode == MeasureSpec.EXACTLY ) { /* * Possibility 1: Both width and height fixed */ measuredWidth = widthSize; measuredHeight = heightSize; } else if ( heightMode == MeasureSpec.EXACTLY ) { /* * Possibility 2: Width dynamic, height fixed */ measuredWidth = (int) Math.min( widthSize, heightSize * aspectRatio ); measuredHeight = (int) (measuredWidth / aspectRatio); } else if ( widthMode == MeasureSpec.EXACTLY ) { /* * Possibility 3: Width fixed, height dynamic */ measuredHeight = (int) Math.min( heightSize, widthSize / aspectRatio ); measuredWidth = (int) (measuredHeight * aspectRatio); } else { /* * Possibility 4: Both width and height dynamic */ if ( widthSize > heightSize * aspectRatio ) { measuredHeight = heightSize; measuredWidth = (int)( measuredHeight * aspectRatio ); } else { measuredWidth = widthSize; measuredHeight = (int) (measuredWidth / aspectRatio); } } } private Integer measuredWidth = null; /** * Get the width measured in the latest call to <tt>measure()</tt>. */ public int getMeasuredWidth() { if ( measuredWidth == null ) { throw new IllegalStateException( "You need to run measure() before trying to get measured dimensions" ); } return measuredWidth; } private Integer measuredHeight = null; /** * Get the height measured in the latest call to <tt>measure()</tt>. */ public int getMeasuredHeight() { if ( measuredHeight == null ) { throw new IllegalStateException( "You need to run measure() before trying to get measured dimensions" ); } return measuredHeight; } }