安卓OpenGL:睡眠后疯狂的纵横比

7
如果我按下我的安卓手机的电源按钮(或等待它超时)并且手机进入睡眠状态,唤醒后,屏幕纵横比会出现问题。
截图:
有问题的屏幕截图: http://i.imgur.com/4C6ID.png 正确的屏幕截图: http://i.imgur.com/CNAV0.png 在GameActivity.onCreate()中。
    // Grab display info
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    ACTUAL_SCREEN_WIDTH = (int) dm.widthPixels;
    ACTUAL_SCREEN_HEIGHT = (int) dm.heightPixels;
    ACTUAL_DENSITY = dm.density;

    double scale = ((double) MainMenuActivity.resolutionScale / 100.0);
    SCREEN_WIDTH = (int) (ACTUAL_SCREEN_WIDTH * scale);
    SCREEN_HEIGHT = (int) (ACTUAL_SCREEN_HEIGHT * scale);
    DENSITY = ACTUAL_DENSITY * scale;

    // Setup graphics
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    surface_view = new GLSurfaceView(this);

    if (SCREEN_WIDTH != ACTUAL_SCREEN_WIDTH
            || SCREEN_HEIGHT != ACTUAL_SCREEN_HEIGHT)
        surface_view.getSurfaceHolder().setFixedSize(SCREEN_WIDTH,
                SCREEN_HEIGHT);

我在GLSurfaceView中正确地重新加载了所有内容(例如,返回主屏幕并重新进入游戏会重新加载纹理等)。
我已经在onWindowFocusChanged中打印了方向,结果为landscape(如预期)。
如果我不使用setFixedSize(即resolutionScale为100),则不会出现问题。此外,如果另一个窗口出现在游戏前面(例如首选项窗口),然后返回游戏,则问题消失了。
提前感谢您的帮助。因为研究工作枯竭,我已经随意尝试了大约一个小时。
4个回答

6

我正在处理这个问题,目前看来它似乎与SurfaceHolder.setFixedSize()相关的Android bug有关。

我获取这些日志的方法是:等待游戏加载完成,按下电源键,让屏幕变黑,再次按下电源键(快速),解锁滑块(快速)。顺便提一下,我的手机是HTC Thunderbolt 2.3.4版本。

以下是正常情况下的事件流程(不使用setFixedSize):

----- power button pushed ------
JavaMain: onPause: called, thread: 1
ActivityManager: Config changed: { scale=1.0 imsi=310/12 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=1 layout=34 uiMode=17 seq=1509 skin=default}
JavaMain: onConfigurationChanged, orientation: 1 thread: 1
JavaMain: mSView(old): 800x480
JavaMain: surfaceChanged: width: 480, height:800, thread: 1
JavaMain: onWindowFocusChanged: called, thread: 1
(screen is now off)
------ power button pushed, and slide-lock undone -----
JavaMain: onResume: called, thread: 1
ActivityManager: Config changed: { scale=1.0 imsi=310/12 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=1510 skin=default}
JavaMain: onConfigurationChanged, orientation: 2 thread: 1
JavaMain: mSView(old): 480x800
JavaMain: onWindowFocusChanged: called, thread: 1
JavaMain: surfaceChanged: width: 800, height:480, thread: 1
(back in game)

注意,在onPause之后,我的surface view被重新设置为竖屏,在onResume之后又改回横屏。在清单文件中我将screenOrientation设置为横屏。
现在看看在相同的序列中使用SurfaceHolder.setFixedSize()会发生什么(以减小分辨率为例):
----- power button pushed -------
JavaMain: onPause: called, thread: 1
ActivityManager: Config changed: { scale=1.0 imsi=310/12 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=1 layout=34 uiMode=17 seq=1513 skin=default}
JavaMain: onConfigurationChanged, orientation: 1 thread: 1
JavaMain: mSView(old): 800x480
JavaMain: surfaceChanged: width: 640, height:384, thread: 1
JavaMain: onWindowFocusChanged: called, thread: 1
(screen is now off)
------ power button pushed, and slide-lock undone -----
JavaMain: onResume: called, thread: 1
ActivityManager: Config changed: { scale=1.0 imsi=310/12 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=1514 skin=default}
JavaMain: onConfigurationChanged, orientation: 2 thread: 1
JavaMain: mSView(old): 480x800
JavaMain: onWindowFocusChanged: called, thread: 1
(back in game, with a messed up screen as in OP)

现在请注意,在 onPause 之后,surfaceChanged 反映了我指定的固定大小,但是在 onResume 时,surface 视图处于纵向方向(有些出乎意料)。但真正缺少的是带有我的固定大小参数的 surfaceChanged 调用。在你提问之前,请注意,在 onConfigurationChanged() 中执行 .setFixedSize() 没有任何效果。

更新和解决方案:

实际上,解决方案归结为恢复(实际上是不允许任何更改)我的 surface 视图。这可以通过在活动的 onConfigurationChanged 超级调用之后添加以下两行来完成。 (假设您设置了以下清单设置)。
android:configChanges="orientation"
android:screenOrientation="landscape"

这有效防止表面视图尺寸被设置为其他任何尺寸(如纵向)。顺便提一下,这就是我在指定screenOrientation = landscape时想要的结果!!!(我在这里看着你,Google)

public void onConfigurationChanged(Configuration newConfig) {       
    super.onConfigurationChanged(newConfig);

    // -- stomp any attempts by the OS to resize my view.
    // -- Hardcoded values for illustration only. In real life
    // -- you'll need to save these when the surface view is originally created
    mSView.getLayoutParams().width = 800;
    mSView.getLayoutParams().height = 480;
}

2
谢谢!自从我第一次发布这个问题以来已经过了很长时间。但是看到解决方案还是很好的! - coda
这实际上不是解决方案。首先,它是一个肮脏的黑客,当您的GLSurfaceView不是全屏时,或者当它是复杂视图层次结构的一部分时,它将无法完成工作。相反,正如fabspro已经指出的那样:确保每次调用onSurfaceChanged()时都调用glViewport()。 - Ariel Malka
好的,这是为全屏视图而设计的,所以在需要时完成了工作(回到'12年)。我不再参与相关项目,很遗憾无法测试您的建议。 - gadget

1

您需要在GL10.Renderer的onSurfaceChanged方法中调用gl.glViewport(0, 0, width, height)或类似的函数。我的假设是当设备从休眠中唤醒时,某些内容必须会更改视口,然而,视口在您的应用程序首次启动时已经初始化,这就是为什么它在休眠之前可以正常工作的原因。


0

OpenGL状态和暂停/恢复活动

您还应该意识到,当您的活动被暂停/恢复时,您的OpenGL上下文(所有当前状态,如当前颜色、绑定纹理等)将会丢失
因此,您可能面临上下文丢失问题。
如果您正在使用GLSurfaceView.Renderer类,您应该在onSurfaceCreated(..)方法中重新设置您的状态。 应该更新您的问题并附上一些代码。

更新 仅供参考,您可以参考以下评论:http://www.youtube.com/watch?v=cdvaPyq_eBU (38:14 min)以及setFixedSize(..)的用法。

http://developer.android.com/reference/android/view/SurfaceHolder.html#setFixedSize%28int,%20int%29
指出您必须从运行SurfaceView窗口的相同线程调用方法setFixedSize(SCREEN_WIDTH,SCREEN_HEIGHT)。看起来您正在从UI线程而不是渲染线程中进行设置。


我解决了这个问题!奇怪的是,问题是因为我没有使用具有2次幂维度的纹理!自从更改后,我再也没有遇到这个问题了。 - coda
算了,问题又出现了。顺便说一下,我正在正确地处理所有的线程。 - coda
我有一个相同的问题,你找到导致问题的原因了吗? - fabspro

0

你需要做三个步骤:

  1. AndroidManifest.xml - android:configChanges="keyboard|keyboardHidden|screenSize|orientation"
  2. 在活动中使用FrameLayout来布局
  3. 在Java代码中

    public void onSurfaceChanged(GL10 gl, int w, int h) { // 你需要测试w > h,然后再进行图形初始化 ... }


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