I need to set the Bitmap into ImageView to keep ratio 16:9. Do you have any advice? The main solution is probably in the override method onMeasure of custom ImageView but how?
EDIT 01/03/2019: After all this time, I strongly recommend using ConstraintLayout
that @Eugene Brusov introduced below. I personally would never use my image view that does Math by overriding onMeasure.
EDIT 03/29/2018: My answer below may be simple, but may be too much of raw answer. If you want to utilize what's already given by Google's ConstraintLayout
, follow this answer or scroll down and see @Eugene Brusov's answer.
Old answer:
public class CustomImageView extends ImageView {
// some other necessary things
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
//force a 16:9 aspect ratio
int height = Math.round(width * .5625f);
setMeasuredDimension(width, height);
}
}
Then in your xml
<path.to.package.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/img"/>
Since PercentFrameLayout
and PercentRelativeLayout
were deprecated in API level 26.0.0, I'd suggest you to consider using of ConstraintLayout
to keep ratio 16:9 for your ImageView
. ConstraintLayout
is really powerful tool to build Responsive UI for Android platform, you can find more details here Build a Responsive UI with ConstraintLayout.
Here is the example how to build your ImageView into ConstraintLayout to keep 16:9 ratio:
<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="H,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 instead of editing your XML file, edit your layout directly in Layout Editor:
EDIT 01/03/2019: After all this time, I strongly recommend using ConstraintLayout
that @Eugene Brusov introduced below. I personally would never use my image view that does Math by overriding onMeasure.
EDIT 03/29/2018: My answer below may be simple, but may be too much of raw answer. If you want to utilize what's already given by Google's ConstraintLayout
, follow this answer or scroll down and see @Eugene Brusov's answer.
Old answer:
public class CustomImageView extends ImageView {
// some other necessary things
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
//force a 16:9 aspect ratio
int height = Math.round(width * .5625f);
setMeasuredDimension(width, height);
}
}
Then in your xml
<path.to.package.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/img"/>
EDIT: Google has officially deprecated the Percent support library starting API 26 and you should move away from it as soon as possible.
Set size as a ratio You can set the view size to a ratio such as 16:9 if at least one of the view dimensions is set to "match constraints" (0dp). To enable the ratio, click Toggle Aspect Ratio Constraint (callout 1 in figure 10), and then enter the width:height ratio in the input that appears.
If both the width and height are set to match constraints, you can click Toggle Aspect Ratio Constraint to select which dimension is based on a ratio of the other. The view inspector indicates which is set as a ratio by connecting the corresponding edges with a solid line.
For example, if you set both sides to "match constraints", click Toggle Aspect Ratio Constraint twice to set the width be a ratio of the height. Now the entire size is dictated by the height of the view (which can be defined in any way) as shown in figure below:
More information here: https://developer.android.com/training/constraint-layout/index.html#adjust-the-view-size
There is something called PercentFrameLayout
and PercentRelativeLayout
in the Support library.
<android.support.percent.PercentFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
app:layout_widthPercent="100%"
app:layout_aspectRatio="178%"
android:scaleType="centerCrop"
android:src="@drawable/header_background"/>
<!-- The rest of your layout -->
</android.support.percent.PercentRelativeLayout>
If you take a closer look at the above layout, you'll see that the ImageView
in question (that needs to be fixed at 16:9) is wrapped around a PercentFrameLayout
and there's two attributes set on the ImageView
that you might not have seen before:
app:layout_widthPercent="100%"
app:layout_aspectRatio="178%"
So (1) you need to define one of the dimensions (either width or height) to be the leading dimension for our ImageView
in question. In this case the ImageView
will expand vertically, to it's maximum width (like android:layout_width="match_parent"
)
And (2) you need to set the aspect ratio in percentage (hence the name of the library) which is in this case 178% (16/9 = 1.77777777778 or more simply put 1.78:1 or 178%).
Read more about the Percent Support Library here.
Above answers didn't work for me, I found this and worked:
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();
}
}
Note: One simbol *
is changed to /
(read the comments in source)
And this in your /values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<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>
</resources>
Then you can use this way in your layout (aspectRatio = width/height):
<com.yourpackage.ui.classes.AspectRatioImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:aspectRatio="1.78"
app:aspectRatioEnabled="true"
app:dominantMeasurement="width" />
© 2022 - 2024 — McMap. All rights reserved.