视图转换后的图像越界

时间:2017-04-14 15:45:04

标签: android view rotation imageview bounds

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

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

enter image description here

它看起来不错,但是当我对其应用旋转时,似乎视图的边界没有变化,图像可能会被截断出屏幕!完全不希望这种情况发生!如何重新缩放图像,以便在旋转90度时图像也适合ImageView。

enter image description here

这是我的XML WITH旋转。

enter image description here

编辑:

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

4 个答案:

答案 0 :(得分:5)

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

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_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:adjustViewBounds="true"
    android:rotation="90"
    android:maxWidth="100dp"
    android:maxHeight="100dp"
    android:scaleType="fitCenter"
    android:src="@drawable/test" />

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

enter image description here

rotation="0"

rotation="45"

rotation="90"

更新:现在尝试调整边界。 wrap_contentmatch_parent之间没有区别(根据图像方面,两者都会增长)。您应该使用maxWidth和/或maxHeight,或者将其放在LinearLayout中,其大小和权重为0。

它也是不可动画的,调整边界虽然动画需要每帧的布局传递,这是非常低效的。有关适用于View.animate()

的版本,请参阅其他答案

答案 1 :(得分:1)

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_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:adjustViewBounds="true"
    android:rotation="90"
    android:scaleType="fitCenter"
    android:src="@drawable/test" />

答案 2 :(得分:0)

根据一项导致Carbon的研究,我想知道@Sarge Borsch的答案是否可以适用于您的情况。

尝试设置

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

如果centerInside不正确,因为您希望显示在中央,可能会尝试将imageview定位在内部而不是图像。

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

答案 3 :(得分:0)

属性android:rotation引用的是ViewView,而不是其内容。

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