ImageView按比例缩放并固定高度,但裁剪多余的宽度

13
我希望我的 ImageView 以一种特定的方式进行缩放:
  • 使图像的高度始终适合 ImageView 的高度。
  • 裁剪任何多余的宽度。
图片更能说明问题,下面是我想要实现的ImageView的效果。假设它的高度是100dp固定的,并且宽度为match_parent

enter image description here

请注意:
  • 在手机布局上,图像高度被拉伸,但两侧被裁剪,类似于CROP_CENTER
  • 在平板电脑布局上,图像也被拉伸以适应ImageView的高度,呈FIT_CENTER行为
我猜测需要使用scaleType:matrix,但之后我就不知道该怎么办了。那如何确保图像适合Y轴,但会裁剪X轴呢?

你能提供一张你想要实现的屏幕截图吗? - user957654
1
嗯...你不明白我的绘画创作吗? - Maarten
抱歉,我没有。请提供您的源代码。 - user957654
你的绘画创作已经很不错了,但这一幅真的太棒了! - Carlos Robles
@Amrola 你在搞笑! - Maarten
显示剩余2条评论
5个回答

27

6

在我的朋友Carlos Robles和pskink的帮助下,我设计出了以下自定义ImageView

public class FitYCropXImageView extends ImageView {
    boolean done = false;

    @SuppressWarnings("UnusedDeclaration")
    public FitYCropXImageView(Context context) {
        super(context);
        setScaleType(ScaleType.MATRIX);
    }

    @SuppressWarnings("UnusedDeclaration")
    public FitYCropXImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setScaleType(ScaleType.MATRIX);
    }

    @SuppressWarnings("UnusedDeclaration")
    public FitYCropXImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setScaleType(ScaleType.MATRIX);
    }

    private final RectF drawableRect = new RectF(0, 0, 0,0);
    private final RectF viewRect = new RectF(0, 0, 0,0);
    private final Matrix m = new Matrix();
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (done) {
            return;//Already fixed drawable scale
        }
        final Drawable d = getDrawable();
        if (d == null) {
            return;//No drawable to correct for
        }
        int viewHeight = getMeasuredHeight();
        int viewWidth = getMeasuredWidth();
        int drawableWidth = d.getIntrinsicWidth();
        int drawableHeight = d.getIntrinsicHeight();
        drawableRect.set(0, 0, drawableWidth, drawableHeight);//Represents the original image
        //Compute the left and right bounds for the scaled image
        float viewHalfWidth = viewWidth / 2;
        float scale = (float) viewHeight / (float) drawableHeight;
        float scaledWidth = drawableWidth * scale;
        float scaledHalfWidth = scaledWidth / 2;
        viewRect.set(viewHalfWidth - scaledHalfWidth, 0, viewHalfWidth + scaledHalfWidth, viewHeight);

        m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER /* This constant doesn't matter? */);
        setImageMatrix(m);

        done = true;

        requestLayout();
    }
}

我不认为 Matrix.ScaleToFit.CENTER 会起作用,它真的有效吗? - pskink
对我来说可以... 我对 setRectToRect 方法没有深入的了解,但在我的设备上看起来很好!(STRETCH 也是如此。) - Maarten
1
确实,如果两个矩形具有相同的宽高比,则任何ScaleToFit都可以使用。 - pskink
1
太好了!它也适用于FitXCropYImageView,可以在填充宽度的同时裁剪高度。 - Julio_oa
1
请问我需要做哪些更改才能获得FitXCropYImageView? - quad

1
如果您使用scaleType:matrix,则需要创建自己的矩阵,并通过setImageMatrix(Matrix)将其分配给视图,或在自定义ImageView的onMeasure方法中手动修改矩阵。
public class MyImageView extends ImageView   {

 boolean done=false;

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

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (done)
            return;

        final Drawable d = getDrawable();
        final int drawableW = d.getIntrinsicWidth();
        final int drawableH = d.getIntrinsicHeight();
        float ratio =  drawableW / drawableH;

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

        float scale=height/drawableH;

          Matrix m = getImageMatrix();

          float[] f = new float[9];
          m.getValues(f);

          f[Matrix.MSCALE_X]=scale;
          f[Matrix.MSCALE_Y]=scale;

          m.setValues(f);  

        done = true;

        requestLayout();

    }

}

是的,这就是为什么你要在onmeasure方法中调整高度。另外,你要改变宽度以保持比例,并且由于scaletype是center_crop,如果宽度过大,它将被裁剪。 - Carlos Robles
如果你给我一点时间,我会解释如何做到这一点。 - Carlos Robles
最终,我花时间编写了一个使用矩阵的解决方案。我认为它在任何情况下都能很好地工作。 - Carlos Robles
但是如果在测量时没有设置可绘制对象怎么办? - Maarten
1
你可以在 if (done) 后检查,如果没有可绘制的内容就返回。系统会一直调用 onMeasure 方法。同时,你也可以将这段代码迁移到你的活动中,在分配可绘制对象后进行所有数学计算。当然,函数可能会有所不同,但逻辑是相同的。 - Carlos Robles
显示剩余2条评论

1
    LinearLayout ll = new LinearLayout(this);
    ll.setOrientation(LinearLayout.VERTICAL);
    LayoutParams params;

    final ImageView iv0 = new ImageView(this);
    //iv0.setBackgroundColor(0xffff0000);
    params = new LayoutParams(LayoutParams.MATCH_PARENT, 100);
    ll.addView(iv0, params);

    final ImageView iv1 = new ImageView(this);
    //iv1.setBackgroundColor(0xff00ff00);
    params = new LayoutParams(60, 100);
    ll.addView(iv1, params);

    setContentView(ll);

    Runnable action = new Runnable() {
        @Override
        public void run() {
            Drawable d = getResources().getDrawable(R.drawable.layer0);
            int dw = d.getIntrinsicWidth();
            int dh = d.getIntrinsicHeight();
            RectF src = new RectF(0, 0, dw, dh);

            ImageView[] iviews = {iv0, iv1};
            for (int i = 0; i < iviews.length; i++) {
                ImageView iv = iviews[i];
                iv.setImageDrawable(d);
                iv.setScaleType(ScaleType.MATRIX);

                float h = iv.getHeight();
                float w = iv.getWidth();
                float cx = w / 2;
                float scale = h / dh;
                float deltaw = dw * scale / 2;
                RectF dst = new RectF(cx - deltaw, 0, cx + deltaw, h);
                Matrix m = new Matrix();
                m.setRectToRect(src, dst, ScaleToFit.FILL);
                iv.setImageMatrix(m);
            }
        }
    };
    iv1.post(action);

可能需要一些解释吗?如果在“ImageView”消息队列结束时尚未设置可绘制对象怎么办? - Maarten
“如果drawable尚未设置”是什么意思? - pskink
您在Runnable中执行 iv.setImageDrawable(d);。如果可绘制的图像是通过异步方式从互联网获取的,并且您无法保证在获取它时已经发生了布局,反之亦然,布局发生时是否有可绘制的图像呢? - Maarten
我仅使用Runnable,因为我需要确保ImageView已经布局并具有非零宽度和高度,只要ImageView的大小不为零,您可以随时分配图像Drawable(您也可以通过扩展ImageView并覆盖onSizeChanged来实现)。 - pskink

0
如果你想要显示图像的中心,请使用:
android:scaleType="centerCrop"
android:adjustViewBounds="true"

如果您想显示图像的边缘而不是中心,请使用以下代码:
android:scaleType="matrix"
android:adjustViewBounds="true"

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