1

SurfaceViewを使用して画面の一部に描画しています。これにより、背景スレッドで描画を更新します。surfaceviewは、SurfaceView.callbackを実装します。

onDrawメソッドでは、canvas.drawpont(x、y、paint)を使用して、キャンバスのほぼ全体をピクセルごとに描画するため、大画面ではかなり集中的に使用できます。とにかく、ランドスケープとリバースランドスケープの間で電話をすばやく回転させると、illegalArgumentExceptionが発生します。次のように:

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を呼び出す前にキャンバスをロックしました。

このメソッドは、すべての方向にゆっくりと回転させると問題なく機能しますが、2つのランドスケープ状態の間ですばやく180度回転すると、このillegalArgumentExceptionがトリガーされます。surfaceChangedで幅と高さを監視していると、すばやく回転すると、横向きの幅と縦向きの高さが対になっていることに気付きました。たとえば、私の電話(Motorola atrix)では、縦向きの場合は540x307(WidthxHeight)、横向きの場合は960x172のSurfaceViewのサイズです。これは、横向きと逆向きの横向きの間をすばやく回転するとどうなるかです。

ゆっくりと横向きに回転します。

960x172

すばやく回転して風景を反転させます。変更されたサーフェスは2回呼び出されます。

960x307 - 1st
960x172 - 2nd

onMeasureをオーバーライドしようとしましたが、幅と高さはSurfaceChangedのものと同じです。ですから、準備が整っていないキャンバスに描画しようとしているようですが、ホルダーにロックできるように十分に準備ができています。お時間をいただきありがとうございます。

さらに、破壊された表面では、次のようにスレッドを処理しています。

boolean retry = true;

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

これにより、スレッドはonDrawを実行できなくなります。

4

2 に答える 2

1

ライブ壁紙の作業中に同じ問題が発生していました。キャンバスを強制的に画面サイズにすることでこれを解決しました。onSurfaceChanged で、キャンバスにペイントするために使用するグローバルな四角形を作成します。これを行わないと、元の作成者と同じ問題が発生します。

それを解決したコード:

変更された表面では、描画に使用しているすべての変数を初期化します。_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 メソッドによって報告されるサイズと同じサイズになります。

于 2012-07-17T01:09:37.613 に答える
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 は、私のサーフェス ビューではグローバル ブール値です。実験から、SurfaceChanged() は SurfaceCreated の前に呼び出されることがわかりました。これは、上記の変更に合わせて、スレッドと対話しようとするコードを簡単に変更できることを意味します。

そのため、その背景描画スレッドに参加または開始しようとしていた他の場所では、サーフェスが不正であるかどうかを最初に確認する必要がありました。例えば

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();
                }
            }


        }

とにかく、これはうまくいくようです。おそらく他の誰かがもっと良いことを知っています。しかし、同じ問題を抱えている他の人のために、ここに私の答えを載せます。ありがとう。

于 2012-06-18T09:17:29.293 に答える