在视图上转换后图像超出范围

Posted

技术标签:

【中文标题】在视图上转换后图像超出范围【英文标题】:Image out of bounds after transformation on view 【发布时间】:2017-04-14 15:45:03 【问题描述】:

我在显示图片时遇到问题。

我有一张想要全屏显示的图片。所以我有这个带有 match_parent 和 20dp 填充的 Imageview。

看起来不错,但是当我对其应用旋转时,视图的边界似乎没有改变,并且图像可能会从屏幕上剪掉!完全不希望这样的事情发生!如何重新缩放图像,以便图像在旋转 90 度时也适合 ImageView。

这是我的带有旋转的 XML。

编辑:

如何修复图像的边界,使文本在图像上方对齐?

【问题讨论】:

【参考方案1】:

在测量视图和计算比例时不考虑旋转。一个可能的解决方案是自己做:

public class RotatedImageView extends ImageView 

    ...
    constructors
    ...


    private double mRotatedWidth;
    private double mRotatedHeight;

    private boolean update() 
        Drawable d = getDrawable();

        if (d == null) 
            return false;
        

        int drawableWidth = d.getIntrinsicWidth();
        int drawableHeight = d.getIntrinsicHeight();

        if (drawableWidth <= 0 || drawableHeight <= 0) 
            return false;
        

        double rotationRad = getRotation() / 180 * Math.PI;

        // calculate intrinsic rotated size
        // see diagram

        mRotatedWidth = (Math.abs(Math.sin(rotationRad)) * drawableHeight
                + Math.abs(Math.cos(rotationRad)) * drawableWidth);
        mRotatedHeight = (Math.abs(Math.cos(rotationRad)) * drawableHeight
                + Math.abs(Math.sin(rotationRad)) * drawableWidth);

        return true;
    


    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 

        if (update()) 
            double ratio = mRotatedWidth / mRotatedHeight;

            int wMax = Math.min(getDefaultSize(Integer.MAX_VALUE, widthMeasureSpec), getMaxWidth());
            int hMax = Math.min(getDefaultSize(Integer.MAX_VALUE, heightMeasureSpec), getMaxHeight());

            int w = (int) Math.min(wMax, hMax * ratio);
            int h = (int) Math.min(hMax, wMax / ratio);

            setMeasuredDimension(w, h);
         else 
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        

    

    private final float[] values = new float[9];

    protected void onDraw(Canvas canvas) 

        if (update()) 
            int availableWidth = getMeasuredWidth();
            int availableHeight = getMeasuredHeight();

            float scale = (float) Math.min(availableWidth / mRotatedWidth, availableHeight / mRotatedHeight);

            getImageMatrix().getValues(values);

            setScaleX(scale / values[Matrix.MSCALE_X]);
            setScaleY(scale / values[Matrix.MSCALE_Y]);
        

        super.onDraw(canvas);
    

    @Override
    public void setRotation(float rotation) 
        super.setRotation(rotation);
        requestLayout();
    

adjustViewBounds 必须为真:

<com.mypackage.RotatedImageView
    android:layout_
    android:layout_
    android:layout_margin="20dp"
    android:adjustViewBounds="true"
    android:rotation="90"
    android:maxWidth="100dp"
    android:maxHeight="100dp"
    android:scaleType="fitCenter"
    android:src="@drawable/test" />

一个很好的计算解释,由Cheticamp提供:

更新:现在尝试调整界限。 wrap_contentmatch_parent 之间没有区别(两者都尽可能地增长,基于图像方面)。您应该改用maxWidth 和/或maxHeight,或者将其放入大小和重量为0 的LinearLayout

它也不是可动画的,在制作动画时调整边界需要为每一帧进行布局传递,这非常低效。请参阅其他答案以获取可与 View.animate() 一起使用的版本

【讨论】:

看起来不错,让我明天试试这个!会及时通知您! 你能说出这是什么公式吗? 旋转后的宽度(高度)取决于旋转前的宽度和高度,例如当旋转 = 0 : sin = 0, cos = 1 之后宽度 = 之前的宽度(显然),当旋转 = 90 : sin = 1, cos = 0 之后宽度 = 之前的高度。明天我会尝试添加更完整的解释 @azizbekian 这是一个非常聪明的答案,我坐下来弄清楚发生了什么。这是我计算它的价值的链接。 i.imgur.com/zZUFpul.jpg @DennisAnderson 我终于设法制作了一个可与animate() 一起使用的版本。这很 hackish,所以我将它添加为另一个答案,并保持原样。我不确定哪个是最好的【参考方案2】:

根据导致this topic 的研究,我想知道@Sarge Borsch 的答案是否适用于您的情况。

尝试设置

android:scaleType="centerInside"
android:adjustViewBounds="true"

如果 centerInside 不正确,因为你想显示在中心,也许尝试定位 imageview 而不是里面的图像。

另一个建议:您的图像视图设置在“wrap_content”上,我不知道所有内容的确切顺序,但问题可能是因为它在计算尺寸后旋转(因为 wrap_content)。我认为这是一种可能性,因为您放置的屏幕截图显示图像甚至不适合宽度。 TL;DR : 结合“adjustViewBounds”尝试修复 imageview 大小(activity + match_parent 上的填充)而不是包装内容。

【讨论】:

【参考方案3】:

RotatedImageView 的另一个版本,可以使用ViewPropertyAnimator 为旋转设置动画。思路是一样的,只是缩放是在onDraw()而不是onMeasure()中完成的,所以不需要每次都布局一遍。 为了使动画工作,我不得不劫持更新侦听器。如果你想使用自己的监听器,别忘了invalidate()onAnimationUpdate()中的视图。

public class RotatedImageView2 extends ImageView 

    ...
    constructors
    ...

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

        int beforeWidth = getMeasuredWidth();
        int beforeHeight = getMeasuredHeight();
        int max = Math.max(beforeWidth, beforeHeight);

        // try to grow
        setMeasuredDimension(getDefaultSize(max, widthMeasureSpec), getDefaultSize(max, heightMeasureSpec));
    


    private final float[] values = new float[9];

    @Override
    protected void onDraw(Canvas canvas) 

        Drawable d = getDrawable();

        if (d == null) 
            return;
        

        int drawableWidth = d.getIntrinsicWidth();
        int drawableHeight = d.getIntrinsicHeight();

        if (drawableWidth <= 0 || drawableHeight <= 0) 
            return;
        

        double rotationRad = getRotation() / 180 * Math.PI;

        double rotatedWidth = (Math.abs(Math.sin(rotationRad)) * drawableHeight
                + Math.abs(Math.cos(rotationRad)) * drawableWidth);
        double rotatedHeight = (Math.abs(Math.cos(rotationRad)) * drawableHeight
                + Math.abs(Math.sin(rotationRad)) * drawableWidth);

        int availableWidth = getMeasuredWidth();
        int availableHeight = getMeasuredHeight();

        float scale = (float) Math.min(availableWidth / rotatedWidth, availableHeight / rotatedHeight);

        getImageMatrix().getValues(values);

        setScaleX(scale / values[Matrix.MSCALE_X]);
        setScaleY(scale / values[Matrix.MSCALE_Y]);

        super.onDraw(canvas);
    

    @Override
    public void setRotation(float rotation) 
        super.setRotation(rotation);
        // force redraw
        invalidate();
    

    @Override
    public ViewPropertyAnimator animate() 
        // force redraw on each frame
        // (a ViewPropertyAnimator does not use setRotation())
        return super.animate().setUpdateListener(new ValueAnimator.AnimatorUpdateListener() 
            @Override
            public void onAnimationUpdate(ValueAnimator animation) 
                invalidate();
            
        );
    

使用示例:

<com.mypackage.RotatedImageView2
    android:layout_
    android:layout_
    android:layout_margin="20dp"
    android:adjustViewBounds="true"
    android:rotation="90"
    android:scaleType="fitCenter"
    android:src="@drawable/test" />

【讨论】:

老兄!我喜欢你为回答我的问题而付出的努力!我很快就要试试这个了! :D 谢谢人!会告诉你它是否有效!【参考方案4】:

android:rotation 属性指的是作为 ImageView 的 View,而不是它的内容。

如果要旋转内容,要么设置新的 BItmap 作为内容,要么覆盖 onDraw() 并旋转画布

【讨论】:

如果它指的是视图,它是如何发生的,View 在“显示布局边界”模式下查看时不会旋转?

以上是关于在视图上转换后图像超出范围的主要内容,如果未能解决你的问题,请参考以下文章

表视图中的索引超出范围:在表视图中批量删除后的canEditRowAt

UIView 中的 UIImageView 并且不显示超出范围的图像部分

让超出父视图范围的子视图响应事件,在UIView范围外响应点击

当我使用单元格时索引超出范围(Swift)

无法识别 UILabel 上超出父视图范围的点击

尝试过滤转换后的日期时间值时超出范围的值