SurfaceView回调在Android中调用surfaceChanged时尺寸不一致

时间:2012-06-18 05:38:37

标签: android surfaceview android-canvas surfaceholder

我使用SurfaceView绘制到屏幕的一部分,我用后台线程更新绘图。 surfaceview实现了SurfaceView.callback。

在onDraw方法中我使用canvas.drawpont(x,y,paint)并逐像素地绘制到整个画布,因此在大屏幕上它可能非常密集。无论如何,如果我在横向和反向横向之间快速旋转手机,我会收到非法的违规行为。如下:

06-18 15:03:05.853: E/AndroidRuntime(29969): FATAL EXCEPTION: Thread-45
06-18 15:03:05.853: E/AndroidRuntime(29969): java.lang.IllegalArgumentException
06-18 15:03:05.853: E/AndroidRuntime(29969):    at Android.view.Surface.unlockCanvasAndPost(Native Method)
06-18 15:03:05.853: E/AndroidRuntime(29969):    at android.view.SurfaceView$3.unlockCanvasAndPost(SurfaceView.java:793)
06-18 15:03:05.853: E/AndroidRuntime(29969):    at com.enhancement.SpecGuageUpdate.run(SpecGuageUpdate.java:313)

在313的SpecGugaeupdate上,我有:

mHolder.unlockCanvasAndPost(canvas);

mHolder是我的SurfaceHolder。当然,我也在调用onDraw之前锁定了画布。

如果我在每个方向上缓慢旋转,此方法可以完美地工作,但是,当我在两个横向状态之间快速旋转180度时,它会触发此非法抛出异常。 在surfaceChanged中监视宽度和高度时,我注意到当我快速旋转时,景观的宽度与肖像的高度配对。例如,在我的手机(Motorola atrix)上,SurfaceView for Portrait的尺寸为540x307(WidthxHeight),而横向尺寸为960x172。当我在横向和反向景观之间快速旋转时会发生这种情况:

慢慢旋转:

960x172

快速旋转以反转横向,表面更改会被调用两次。

960x307 - 1st
960x172 - 2nd

我试过覆盖onMeasure,宽度和高度与SurfaceChanged相同。因此,我似乎试图画一幅尚未准备好的画布,但已准备好足以将其锁定到我的持有人。感谢您的时间,任何想法都会很棒。

另外,在我的surfacedestroyed中,我正在处理我的线程:

boolean retry = true;

            thread.setRunning(false);
              while (retry) {
                    try {
                        specupdate.join();
                        retry = false;
                    } catch (InterruptedException e) {
                    }
                }

阻止线程执行onDraw。

2 个答案:

答案 0 :(得分:1)

我在制作动态壁纸时遇到同样的问题。我通过强制我的画布成为屏幕大小来解决这个问题。在我的onSurfaceChanged上,我创建了一个全局矩形,用于在画布上画画。如果我不这样做,我会遇到与原作者相同的问题。

解决它的代码:

On surface Changed我初始化我用于绘图的所有变量。请注意我创建_screenRectangle。这些变量是全局的,并在整个应用程序中使用。

@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    super.onSurfaceChanged(holder, format, width, height);

    System.out.println("===============================================");
    System.out.println("SURFACE CHANGED");
    System.out.println("===============================================");

    //Get Screen Sizes
    _centerX = width/2.0f;
    _centerY = height/2.0f;

    _screenRectangle = new Rect(0, 0, width, height);
    _screenOrient = getResources().getConfiguration().orientation; 

    System.out.println("===============================================");
    System.out.println("WIDTH: " + width + " HEIGHT: " + height + " ORIEN: " + _screenOrient);
    System.out.println("===============================================");

    _resetSurface = true;
    _redrawStatic = true; 
} 

然后在我的绘画方法中,我使用背景颜色绘制整个屏幕:

try
{            
    canvas = holder.lockCanvas(_screenRectangle);


    System.out.println("===============================================");
    System.out.println("PAINT BACKGROUND: " + canvas.getWidth() + " " + canvas.getHeight());
    System.out.println("===============================================");

    if (canvas != null) 
    {
        canvas.drawColor(PreferencesHelper.ScreenColor);
        _resetSurface = false;
    }
}
finally 
{
    System.out.println("===============================================");
    System.out.println("BACKGROUND PAINTED");
    System.out.println("===============================================");
    if (canvas != null) holder.unlockCanvasAndPost(canvas);
}

在使用_screenRectangle锁定画布之前,在随机时间,它将无法绘制整个屏幕。调试窗口将显示画布大小与当前屏幕不匹配,但是会遇到与OP指出的相同的问题。这似乎解决了这个问题。这会强制锁定的画布与onSurfaceChanged方法报告的大小相同。

答案 1 :(得分:0)

在我的深夜故障排除中,我设法整理了一个解决方法。结果是没有任何东西被绘制,直到surfacechanged已经稳定到它的最后状态,所以用户不知道发生了什么。

基本上我检查返回的高度是否格式不正确,如果是我什么都不做,那么当我想到的时候我会画到表面。我需要的是一个参考值,我的高度和宽度应该是什么,我通过在我的主要活动中的SetContentView()之前放置以下代码来做到这一点:

WindowManager w = getWindowManager();
   Display d = w.getDefaultDisplay();
   int Meausredwidth = d.getWidth();
   int Measuredheight = d.getHeight(); 

根据我测量的高度和宽度,我计算了我需要SurfaceView的多少,并将这些值作为全局变量传递给我的SurfaceView(现在这些是我想要的尺寸)。确保我在主活动中的onconfigurationchanged()中调用上面的代码,以便每次重新计算这些值。在我实现SurfaceHolder.Callback的SurfaceView上,我将surfacechanged()更改为如下所示:

@Override
public void surfaceChanged(SurfaceHolder mholder, int format, int width,
        int height) {

    if (width != mWidth) {
        MalFormed = true;
        thread.setRunning(false);
        count++;

    } else if (width == mWidth) {

        if (MalFormed) {
            MalFormed = false;

            Log.d("ROTATE", "ReDraw");

            thread = new DrawingThread(this, holder, mHandler);
            thread.setRunning(true);
            thread.start();

        }

    }

}

其中mWidth是所需的宽度。 MalFormed是我的surfaceview中的全局布尔值。 从实验中我发现SurfaceChanged()在SurfaceCreated之前被调用,这意味着我们可以轻松地修改任何试图与线程交互的代码以适应我们的上述更改。

所以在我尝试加入或启动背景绘制线程的任何地方,我需要首先检查表面是否为MalFormed。 E.g。

if (thread.getState() == State.TERMINATED){

            if(!MalFormed){
               holder.removeCallback(this);
               holder = getHolder();
               holder.addCallback(this);


               thread = new DrawingThread(this, holder,mHandler);
               thread.setRunning(true);

               thread.start();
            }



        }else{
            if(!MalFormed){
                thread.setRunning(true);

                try {
                    thread.join();
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }


        }

无论如何,这似乎可以解决问题,或许别人知道更好的事情。但是我把这个问题放在这里给其他有同样问题的人。感谢。