-
Star
(164)
You must be signed in to star a gist -
Fork
(36)
You must be signed in to fork a gist
-
-
Save JakeWharton/2856179 to your computer and use it in GitHub Desktop.
// Copyright 2012 Square, Inc. | |
package com.squareup.widgets; | |
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.util.AttributeSet; | |
import android.widget.ImageView; | |
/** Maintains an aspect ratio based on either width or height. Disabled by default. */ | |
public class AspectRatioImageView extends ImageView { | |
// NOTE: These must be kept in sync with the AspectRatioImageView attributes in attrs.xml. | |
public static final int MEASUREMENT_WIDTH = 0; | |
public static final int MEASUREMENT_HEIGHT = 1; | |
private static final float DEFAULT_ASPECT_RATIO = 1f; | |
private static final boolean DEFAULT_ASPECT_RATIO_ENABLED = false; | |
private static final int DEFAULT_DOMINANT_MEASUREMENT = MEASUREMENT_WIDTH; | |
private float aspectRatio; | |
private boolean aspectRatioEnabled; | |
private int dominantMeasurement; | |
public AspectRatioImageView(Context context) { | |
this(context, null); | |
} | |
public AspectRatioImageView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AspectRatioImageView); | |
aspectRatio = a.getFloat(R.styleable.AspectRatioImageView_aspectRatio, DEFAULT_ASPECT_RATIO); | |
aspectRatioEnabled = a.getBoolean(R.styleable.AspectRatioImageView_aspectRatioEnabled, | |
DEFAULT_ASPECT_RATIO_ENABLED); | |
dominantMeasurement = a.getInt(R.styleable.AspectRatioImageView_dominantMeasurement, | |
DEFAULT_DOMINANT_MEASUREMENT); | |
a.recycle(); | |
} | |
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | |
if (!aspectRatioEnabled) return; | |
int newWidth; | |
int newHeight; | |
switch (dominantMeasurement) { | |
case MEASUREMENT_WIDTH: | |
newWidth = getMeasuredWidth(); | |
newHeight = (int) (newWidth * aspectRatio); | |
break; | |
case MEASUREMENT_HEIGHT: | |
newHeight = getMeasuredHeight(); | |
newWidth = (int) (newHeight * aspectRatio); | |
break; | |
default: | |
throw new IllegalStateException("Unknown measurement with ID " + dominantMeasurement); | |
} | |
setMeasuredDimension(newWidth, newHeight); | |
} | |
/** Get the aspect ratio for this image view. */ | |
public float getAspectRatio() { | |
return aspectRatio; | |
} | |
/** Set the aspect ratio for this image view. This will update the view instantly. */ | |
public void setAspectRatio(float aspectRatio) { | |
this.aspectRatio = aspectRatio; | |
if (aspectRatioEnabled) { | |
requestLayout(); | |
} | |
} | |
/** Get whether or not forcing the aspect ratio is enabled. */ | |
public boolean getAspectRatioEnabled() { | |
return aspectRatioEnabled; | |
} | |
/** set whether or not forcing the aspect ratio is enabled. This will re-layout the view. */ | |
public void setAspectRatioEnabled(boolean aspectRatioEnabled) { | |
this.aspectRatioEnabled = aspectRatioEnabled; | |
requestLayout(); | |
} | |
/** Get the dominant measurement for the aspect ratio. */ | |
public int getDominantMeasurement() { | |
return dominantMeasurement; | |
} | |
/** | |
* Set the dominant measurement for the aspect ratio. | |
* | |
* @see #MEASUREMENT_WIDTH | |
* @see #MEASUREMENT_HEIGHT | |
*/ | |
public void setDominantMeasurement(int dominantMeasurement) { | |
if (dominantMeasurement != MEASUREMENT_HEIGHT && dominantMeasurement != MEASUREMENT_WIDTH) { | |
throw new IllegalArgumentException("Invalid measurement type."); | |
} | |
this.dominantMeasurement = dominantMeasurement; | |
requestLayout(); | |
} | |
} |
<declare-styleable name="AspectRatioImageView"> | |
<attr name="aspectRatio" format="float" /> | |
<attr name="aspectRatioEnabled" format="boolean" /> | |
<attr name="dominantMeasurement"> | |
<enum name="width" value="0"/> | |
<enum name="height" value="1"/> | |
</attr> | |
</declare-styleable> |
@kennywyland your right. The following calculation for a 16:9 TV with 1920x1080 resolution makes it more understandable:
aspectRatio = 16/9 = 1.78
newWidth = getMeasuredWidth() = 1920
newHeight = (int) (newWidth * aspectRatio) = (int) (1920 * 1.78) = 3413
But newHeight should be 1080 to fit the aspect ratio.
newHeight = (int) (newWidth / aspectRatio) = (int) (1920 / 1.78) = 1080
In line 48 therefore has to be multiplied rather than divided.
@kennywyland @Servus7
I guess you are not right.
First of all you should choose measurement (dominant) you want to measure (width or height)
Suppose you choose width as dominant.
In this case you can set aspectRatio >1 to make height>width and vice versa
With introduction of ConstraintLayout
, this solution seems a bit clumsy. Now to keep your ImageView
in 16:9 ratio, you simply need to build your ImageView
into ConstraintLayout
:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
android:layout_marginTop="0dp"
app:srcCompat="@mipmap/ic_launcher"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Don't forget to add constraint-layout
dependency to your module's build.gradle
file
implementation "com.android.support.constraint:constraint-layout:1.0.2"
Or you can directly edit your layout in Layout Editor in this way:
Is there a solution for variable height of the parent object with a ConstraintLayout. @eugenebrusov's solution is working just fine for parent views of static height like "match_parent" or "20dp", but when I try to use a parent view with a height of "wrap_content" it is not working because the parent object will have a height of 0. Am I doing something wrong?
I think line 48 should be dividing by the aspectRatio, not multiplying. It should be:
The aspectRatio is (desiredWidth/desiredHeight), so:
aspectRatio = (newWidth/newHeight)
multiply both sides by newHeight...
aspectRatio * newHeight = newWidth
divide both sides by aspectRatio...
newHeight = (newWidth / aspectRatio)