Android ImageView - 16:9
Asked Answered
D

4

17

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?

Demimondaine answered 8/11, 2016 at 19:7 Comment(0)
O
10

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"/>
Overwhelm answered 8/11, 2016 at 19:13 Comment(0)
P
42

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:

Layout Editor

Politic answered 22/8, 2017 at 15:58 Comment(0)
O
10

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"/>
Overwhelm answered 8/11, 2016 at 19:13 Comment(0)
C
5

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:

The view is set to a 16:9 aspect with the width based on a ratio of the height.

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.

Circlet answered 8/11, 2016 at 19:19 Comment(0)
M
5

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" />

Source

Musgrove answered 5/1, 2017 at 11:27 Comment(1)
Thank you very much, nice example for future custom viewsUncial

© 2022 - 2024 — McMap. All rights reserved.