views:

39

answers:

1

I get the fault "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."

The problem commes from the onDraw call in TutorialThread. The code works if the call to startAnimation in onDraw is removed. Why does the code work if startAnimation is removed? Why is this call different than the call to drawRect?

Below is the code:

public class GamePanel extends Activity { private Panel mPanel;

public void onCreate(Bundle savedInstanceState) {       
    super.onCreate(savedInstanceState);
    mPanel = new Panel(this);
    setContentView(mPanel);
    mPanel.requestFocus(); 
}

}

class Panel extends SurfaceView implements SurfaceHolder.Callback{ private TutorialThread _thread; private Paint mPaint; Context context; private final GamePanel game;

public Panel(Context context) {
    super(context); 
    this.game = (GamePanel) context;
    getHolder().addCallback(this);
    _thread = new TutorialThread(getHolder(),this);

    setFocusable(true);
    mPaint = new Paint();
}  
@Override
public void onDraw(Canvas canvas) {             
    canvas.drawARGB(255, 0, 0, 0);              
    mPaint.setColor(Color.WHITE);
    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawRect(100, 100, 150, 150, mPaint);
    startAnimation(AnimationUtils.loadAnimation(game,R.anim.shake));
}               
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){

}
@Override
public void surfaceCreated(SurfaceHolder holder){
    _thread.setRunning(true);
    _thread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder){
    boolean retry = true;
    _thread.setRunning(false);
    while(retry) {
        try {
            _thread.join();
            retry = false;
        } catch(InterruptedException e) {

        }
    }
}

class TutorialThread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private Panel _panel;
    private boolean _run = false;

    public TutorialThread(SurfaceHolder surfaceHolder,Panel panel){
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    }
    public void setRunning(boolean run){
        _run = run;
    }
    @Override
    public void run(){
        Canvas c;
        while(_run){
            c=null;
            try{
                c = _surfaceHolder.lockCanvas(null);
                synchronized(_surfaceHolder){                       
                    _panel.onDraw(c);
                }
            }finally{
                if(c!=null){
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
            try
            {
                Thread.sleep(500);
            }
                catch(InterruptedException Ie) {
            }                   
        }           
    }       
}

}

A: 

Instead of your custom Thread class you should use AsyncTask. AsyncTask enables proper and easy use of the UI thread.

Peter Knego