Android ImageView - 16:9

Android ImageView - 16:9

我需要将位图设置到 ImageView 中以保持比例 16:9。你有什么建议吗?主要的解决办法应该是在自定义ImageView的onMeasure的override方法中,但是如何呢?

编辑 01/03/2019: 经过这么长时间,我强烈建议使用下面介绍的 @Eugene Brusov 的 ConstraintLayout。我个人永远不会使用通过覆盖 onMeasure 来执行数学运算的图像视图。

编辑 03/29/2018: 我在下面的回答可能很简单,但可能过于原始。如果您想利用 Google 的 ConstraintLayout 已经给出的内容,请关注 或向下滚动并查看@Eugene Brusov 的回答。

旧答案:

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);
    }
}

然后在你的 xml

<path.to.package.CustomImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/img"/>

编辑:Google 已正式弃用从 API 26 开始的百分比支持库,您应该尽快离开它。

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:

这里有更多信息:https://developer.android.com/training/constraint-layout/index.html#adjust-the-view-size


支持库中有一个叫做 PercentFrameLayoutPercentRelativeLayout 的东西。

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

如果仔细查看上面的布局,您会发现有问题的 ImageView(需要固定在 16:9)周围环绕着 PercentFrameLayout 并且在 ImageView 上设置了两个您以前可能没有见过的属性:

app:layout_widthPercent="100%"
app:layout_aspectRatio="178%"

因此 (1) 您需要将其中一个维度(宽度或高度)定义为我们所讨论的 ImageViewleading 维度。在这种情况下,ImageView 将垂直扩展到最大宽度(如 android:layout_width="match_parent") 并且 (2) 您需要以百分比(因此库的名称)设置纵横比,在本例中为 178%(16/9 = 1.77777777778 或更简单地说 1.78:1 或 178%)。

阅读有关百分比支持库的更多信息here

以上答案对我不起作用,我找到了这个并起作用了:

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();
  }
}

注意:一个符号 * 改为 / (阅读源代码中的注释) 这在你的 /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>

然后你可以在你的布局中使用这种方式(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

由于 PercentFrameLayoutPercentRelativeLayout 在 API 级别 26.0.0 中被弃用,我建议您考虑使用 ConstraintLayout 来保持比率 16:9 为您 ImageViewConstraintLayout 是为 Android 平台构建响应式 UI 的强大工具,您可以在此处找到更多详细信息 Build a Responsive UI with ConstraintLayout.

下面是如何将 ImageView 构建到 ConstraintLayout 中以保持 16:9 比率的示例:

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

不要忘记将 constraint-layout 依赖项添加到模块的 build.gradle 文件

implementation "com.android.support.constraint:constraint-layout:1.0.2"

或者不编辑 XML 文件,直接在布局编辑器中编辑布局: