ImageView如何成为一个动态宽度的正方形?

76

我有一个GridView,里面有ImageViews,每行都有3个。我可以使用WRAP_CONTENT和scaleType = CENTER_CROP正确设置宽度,但我不知道如何将ImageView的大小设置为正方形。到目前为止,我所做的事情是这样的,除了高度是“静态”的以外,其他都好像还可以:

imageView = new ImageView(context);     
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(GridView.LayoutParams.WRAP_CONTENT, 300));

我正在适配器内执行它。

10个回答

170

最好的选择是自己继承ImageView,并覆盖度量流程:

public class SquareImageView  extends ImageView {

  ...

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int width = getMeasuredWidth();
    setMeasuredDimension(width, width);
  }

  ...

}

1
太棒了!非常感谢,我没有想到这种可能性。 - Andrea
如果我设置了填充//边距,就会出现白色边框。有什么办法可以解决这个问题吗? - Waza_Be
2
android:adjustViewBounds="true" android:scaleType="centerCrop" - Kaloyan Roussev
2
难道不应该设置为min(宽度,高度)吗? - android developer
1
这是一个微小的优化,但检查宽度和高度是否不同可以防止另一次测量。@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); // 优化,除非需要,否则不进行两次测量 if (width != height) { setMeasuredDimension(width, width); } } - Chantell Osejo
显示剩余2条评论

102

另一个答案可以正常工作。这只是bertucci的解决方案的扩展,以使得ImageView相对于xml膨胀布局具有正方形的宽度和高度。

创建一个类,比如说一个SquareImageView继承ImageView,像这样:

public class SquareImageView extends ImageView {

    public SquareImageView(Context context) {
        super(context);
    }

    public SquareImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = getMeasuredWidth();
        setMeasuredDimension(width, width);
    }

}
现在,在您的xml中执行以下操作,
        <com.packagepath.tothis.SquareImageView
            android:id="@+id/Imageview"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
如果您希望 ImageView 不在程序中动态创建,而是在 xml 中固定,则此实现将会有所帮助。

如果您需要一个 ImageView 不被动态创建在程序中,相反地,固定在XML中,那么这个实现会很有帮助。


1
太棒了,谢谢Andro。这正是我在寻找的。再次感谢您抽出时间提供完整的答案。 - Taliadon
1
这应该被标记为正确的完整解决方案。 - Jarrod Robins
还可以参考一个使用这种技术的好教程:https://www.raywenderlich.com/2945946-glide-tutorial-for-android-getting-started - Mr-IDE

27

更加简单的是:

public class SquareImageView extends ImageView {
    ...
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
    }    
}

不要忘记 SDK 版本 >= 21@TargetApi(Build.VERSION_CODES.LOLLIPOP) public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } - Jan Rabe

11

之前的答案已经足够了。我只是在@Andro Selva和@a.bertucci的解决方案上添加了一个小优化:

这只是一个微小的优化,但检查宽度和高度是否不同可以避免另一次测量。

public class SquareImageView extends ImageView {

    public SquareImageView(Context context) {
        super(context);
    }

    public SquareImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();

        // Optimization so we don't measure twice unless we need to
        if (width != height) {
            setMeasuredDimension(width, width);
        }
    }

}

3

对于寻求 Kotlin 解决方案的人:

class SquareImageView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
) : ImageView(context, attrs, defStyleAttr, defStyleRes){
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) = super.onMeasure(widthMeasureSpec, widthMeasureSpec)
}

3

指定宽度的正方形ImageView:

public class SquareImageViewByWidth extends AppCompatImageView {

    public SquareImageViewByWidth(Context context) {
        super(context);
    }

    public SquareImageViewByWidth(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareImageViewByWidth(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width= getMeasuredWidth();
        setMeasuredDimension(width, width);
    }

     ...
}

指定高度的正方形ImageView:
    public class SquareImageViewByHeight extends AppCompatImageView {

        public SquareImageViewByHeight(Context context) {
            super(context);
        }

        public SquareImageViewByHeight(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public SquareImageViewByHeight(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            int height = getMeasuredHeight();
            setMeasuredDimension(height, height);
        }

        ...
    }

一个最小尺寸为正方形的ImageView:

public class SquareImageViewByMin extends AppCompatImageView {

        public SquareImageViewByHeight(Context context) {
            super(context);
        }

        public SquareImageViewByHeight(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public SquareImageViewByHeight(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            int minSize = Math.min(width, height);
            setMeasuredDimension(minSize, minSize);
        }

       ...
    }

在这个答案中使用 AppCompatImageView 是一个重要的点。 - Richard Le Mesurier

1
如果有人希望视图不是正方形,而是按比例调整高度(例如16/9或1/3),可以这样做:
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth()/3, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

0

对于 Kotlin 的快速解决方案(Material.ShapeableImageView 可以更改为 ImageView)

class SquareImageView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ShapeableImageView(context, attrs, defStyleAttr) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(measuredWidth, measuredWidth)
    }
}

0

单行解决方案 -

layout.setMinimumHeight(layout.getWidth());

-1

这里所有的都不必要调用其超类来执行onMeasure。这是我的实现:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    int size = Math.min(width, height);
    setMeasuredDimension(size, size);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接