视图转换后图像越界
Image out of bounds after transformation on view
我在显示图像时遇到问题。
我有一张图片要全屏显示。所以我有这个带有 match_parent 和 20dp 填充的 Imageview。
它看起来不错,但是当我对其应用旋转时,视图的边界似乎没有改变并且图像可能会被剪裁出屏幕!完全不想发生这种事!如何重新缩放图像,使图像在旋转 90 度时也适合 ImageView。
这是我的XML,里面有旋转。
编辑:
如何固定图像的边界,使文本正好在图像上方对齐?
根据一项导致 this topic 的研究,我想知道@Sarge Borsch 的回答是否适用于您的情况。
尝试设置
android:scaleType="centerInside"
android:adjustViewBounds="true"
如果 centerInside 不正确,因为你想在中心显示,也许尝试定位 imageview 而不是内部图像。
另一个建议:您的图像视图设置在 "wrap_content" 上,我不知道所有内容的确切顺序,但问题可能是因为它在计算尺寸后旋转(因为 wrap_content)。我认为这是可能的,因为您放置的屏幕截图显示图像甚至不适合宽度。 TL;DR :尝试修复图像视图大小(在 activity + match_parent 上填充)而不是包装内容,结合 "adjustViewBounds".
属性 android:rotation
指的是 ImageView 的视图,而不是它的内容。
如果要旋转内容,请将新的位图设置为内容,或者覆盖 onDraw()
并旋转 canvas
测量视图和计算比例时不考虑旋转。一个可能的解决方案是自己做:
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" />
对计算的一个很好的解释,由 提供:
更新:现在正在尝试调整边界。 wrap_content
和 match_parent
之间没有区别(都尽可能增长,基于图像方面)。您应该改为使用 maxWidth
和/或 maxHeight
,或者将其放入尺寸和重量为 0 的 LinearLayout
中。
它也是不可动画的,在动画的同时调整边界需要为每一帧进行布局传递,这是非常低效的。有关可与 View.animate()
一起使用的版本,请参阅其他答案
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" />
我在显示图像时遇到问题。
我有一张图片要全屏显示。所以我有这个带有 match_parent 和 20dp 填充的 Imageview。
它看起来不错,但是当我对其应用旋转时,视图的边界似乎没有改变并且图像可能会被剪裁出屏幕!完全不想发生这种事!如何重新缩放图像,使图像在旋转 90 度时也适合 ImageView。
这是我的XML,里面有旋转。
编辑:
如何固定图像的边界,使文本正好在图像上方对齐?
根据一项导致 this topic 的研究,我想知道@Sarge Borsch 的回答是否适用于您的情况。
尝试设置
android:scaleType="centerInside"
android:adjustViewBounds="true"
如果 centerInside 不正确,因为你想在中心显示,也许尝试定位 imageview 而不是内部图像。
另一个建议:您的图像视图设置在 "wrap_content" 上,我不知道所有内容的确切顺序,但问题可能是因为它在计算尺寸后旋转(因为 wrap_content)。我认为这是可能的,因为您放置的屏幕截图显示图像甚至不适合宽度。 TL;DR :尝试修复图像视图大小(在 activity + match_parent 上填充)而不是包装内容,结合 "adjustViewBounds".
属性 android:rotation
指的是 ImageView 的视图,而不是它的内容。
如果要旋转内容,请将新的位图设置为内容,或者覆盖 onDraw()
并旋转 canvas
测量视图和计算比例时不考虑旋转。一个可能的解决方案是自己做:
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" />
对计算的一个很好的解释,由
更新:现在正在尝试调整边界。 wrap_content
和 match_parent
之间没有区别(都尽可能增长,基于图像方面)。您应该改为使用 maxWidth
和/或 maxHeight
,或者将其放入尺寸和重量为 0 的 LinearLayout
中。
它也是不可动画的,在动画的同时调整边界需要为每一帧进行布局传递,这是非常低效的。有关可与 View.animate()
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" />