在大分辨率屏幕上旋转Android 3D视图转换

7

我正在为安卓(api > 14)实现3D翻转动画,但在大屏幕平板电脑(> 2048 dpi)上遇到了问题。在问题调查过程中,我发现了以下基本块:

尝试仅使用矩阵和相机的rotateY对视图(简单的ImageView)进行变换,通过一些角度进行旋转,当角度<60和角度>120时,它可以正常工作(变换和显示),但是当角度在60和120之间时,图像会消失(不会显示)。这是我使用的代码:

private void applyTransform(float degree) 
{
     float [] values = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
     float centerX = image1.getMeasuredWidth() / 2.0f;
     float centerY = image1.getMeasuredHeight() / 2.0f;

     Matrix m = new Matrix();
     m.setValues(values);

     Camera camera = new Camera();
     camera.save();

     camera.rotateY(degree);
     camera.getMatrix(m);

     camera.restore();

     m.preTranslate(-centerX, -centerY); // 1 draws fine without these 2 lines
     m.postTranslate(centerX, centerY);  // 2 

    image1.setImageMatrix(m);
}

这是我的布局XML:

   <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout     xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/ImageView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/naponer"
        android:clickable="true"
        android:scaleType="matrix">
    </ImageView>
</FrameLayout>  

以下是我的情况:

  • 在小屏幕800X480、1024x720等上运行时,无论角度和中心点如何,都能正常工作。
  • 在大屏幕设备2048x1536、2560x1600等上,当角度 < 60 和 > 120 时能正常工作。
  • 如果旋转不以中心为轴心(矩阵预处理和后处理的注释掉),则在任何设备上的任何角度都能正常工作。
  • 当在大屏幕设备上旋转中心为轴心且角度在60到120度之间时,会失败(图像消失)。

请告诉我问题出在哪里,并提供一些解决方法...谢谢!!!


没有效果,已测试启用和禁用硬件加速的情况。 - Henrik
3
你尝试过调整摄像机的距离吗?例如 camera.setLocation(0, 0, -20f) - matiash
是的!!!它成功了!!!谢谢你!!!默认值是0、0、-8f...现在正在尝试找到更多关于这个设置的信息...请解释一下它如何影响绘图,以便当从默认相机位置旋转时图像消失? - Henrik
2
@matiash,您可以回答这个问题,提供更多有关它如何影响问题的细节。 - Pablo
请注意,调用setValues是多余的,因为新创建的Matrix默认情况下是一个单位矩阵。http://developer.android.com/reference/android/graphics/Matrix.html#Matrix() - emidander
显示剩余2条评论
3个回答

8
这个问题是由于用来计算转换的相机距离引起的。虽然 Camera 类本身对此问题没有多少说明,但在 View.setCameraDistance() 方法的文档中有更好的解释(强调是我的):

设置相机到该视图沿 Z 轴(垂直于绘制视图的 X/Y 平面)的距离。相机的距离会影响 3D 变换,例如绕 X 和 Y 轴的旋转。(...)

相机与视图平面之间的距离可能会影响视图在绕 x 轴或 y 轴旋转时的透视扭曲。例如,较大的距离将导致较大的视野角度,并且随着视图旋转,视角不会出现太多透视扭曲。而较短的距离可能会导致更多的透视扭曲和一些绘制伪影,如果旋转的视图部分位于相机后面的话(这就是为什么建议使用至少与视图大小相同的距离,如果视图要旋转的话。)

说实话,我以前没见过这种特殊的效果(根本没有绘制),但我怀疑它可能与我遇到过的
透视扭曲相关的问题有关 :) 因此,解决方案是使用 Camera.setLocation() 方法来确保不会出现这种情况。
View.setCameraDistance() 方法相比,单位并不相同,因为 setLocation() 不使用像素。虽然 setCameraDistance() 调整了密度,但 setLocation() 没有。因此,如果您想基于视图的尺寸计算适当的 z 距离,记得要调整密度。例如:
float cameraDistance = Math.max(image1.getMeasuredHeight(), image1.getMeasuredWidth()) * 5;
float densityDpi = getResources().getDisplayMetrics().densityDpi;
camera.setLocation(0, 0, -cameraDistance / densityDpi);

很棒的解决方案...对我有用...但是在使用较小的设备时,这种方法会改变我的分辨率和摄像头角度...有什么建议吗? - H Raval

0
你需要调整翻译坐标。在计算图像的翻译时,您需要考虑图像大小。当执行矩阵计算时,为您的ImageView设置android:scaleType =“matrix”。默认情况下,这将使您的图像对齐在左上角。然后,当您应用预/后翻译时,您的图像可能会超出您的ImageView的边界(特别是如果ImageView相对较大而您的图像相对较小,例如在大屏幕平板电脑中)。
以下转换结果使图像围绕其Y轴中心旋转,并将图像保持对齐到左上角:
m.preTranslate(-imageWidth/2, 0);
m.postTranslate(imageWidth/2, 0);

以下替代方案会使图像围绕其中心Y/X轴旋转,并将图像对齐到ImageView的中心:

m.preTranslate(-imageWidth/2, -imageHeight/2);
m.postTranslate(centerX, centerY);

如果您的图像是位图,您可以使用内在的宽度/高度:

Drawable drawable = image1.getDrawable();
imageHeight = drawable.getIntrinsicHeight();
imageWidth = drawable.getIntrinsicWidth();

实际上我看不出这两个中心点之间有任何区别... 不过我测试了你的建议,结果完全一样 - 图像在同样的范围内消失了... - Henrik
你能提供你的活动布局XML吗?我正在使用简单的布局,其中包含一个ImageView,它被拉伸以填满整个屏幕,效果还不错 :) - stealth
当然,我已经将其添加到原始帖子中...并且还测试了使用ImageView拉伸以填充整个屏幕-但是没有效果...我怀疑您没有在高分辨率设备> 2048x1536上进行测试。 - Henrik

0

不必使用12行代码来创建旋转矩阵,你只需在第一行实现这个http://en.wikipedia.org/wiki/Rotation_matrix

根据你想要的效果,你可能需要将图像居中到你想要绕其旋转的轴上。http://en.wikipedia.org/wiki/Transformation_matrix

嗯,对于图像消失的问题,我猜测可能与内存(内存不足-尽管这会引发异常)或舍入问题有关。也许你可以尝试将精度提高到双精度?

我想到的一件事是,当alpha接近PI/2时,cos(alpha)趋向于0。除此之外,我没有看到任何角度和为什么它不适用于大图像之间的相关性。


确保矩阵计算可以优化为1-2行,但这对我现在来说并不关键...问题是在我上面描述的范围内应用最终矩阵后图像消失了,精度在这里并不起作用...如果它在PI/2度时消失(就像其他设备上一样),我没问题。似乎问题与部分图像超出屏幕边界的范围有关,但我无法弄清楚为什么它必须消失??? - Henrik
每次都确切地在60度和120度消失吗?还是有点随机,有时在62度,有时在58度? - Jani
另外一个需要注意的是前/后置翻译。你说没有这些行也能工作?问题可能是在旋转之前没有将图像居中。或许可以尝试先进行平移(居中)-> 旋转 -> 再平移回去。建议使用Camera.translate(x,y,z)函数。http://developer.android.com/reference/android/graphics/Camera.html - Jani
不是确切的60和120,我是通过每5度进行测试的,它在60时消失,在120时重新出现... - Henrik
  1. 没有预先/后置翻转图像旋转轴不居中,它永远不会超出屏幕边界,这就是为什么它能够正常工作的原因...问题出现在旋转轴被居中的情况下(通过预先/后置翻转或预先计算的矩阵)。
- Henrik

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