Android SurfaceView not showing onDraw

nhaarman picture nhaarman · Apr 12, 2011 · Viewed 9k times · Source

For simpleness of the question, I'm drawing an integer on a SurfaceView which increases by 1 every draw. The increasing actually happens, as I can see on the System.out. The text on the screen stays on '0'. Who can tell me what I'm doing wrong?

SurfaceViewTest.java

package com.niek.surfaceviewtest;

import android.app.Activity;
import android.os.Bundle;

public class SurfaceViewTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

StatusView.java

package com.niek.surfaceviewtest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class StatusView extends SurfaceView implements SurfaceHolder.Callback {

    private int tmp;
    private DrawThread drawThread;

    public StatusView(Context context, AttributeSet attrs) {
        super(context, attrs);
        getHolder().addCallback(this);
        setFocusable(true);
        drawThread = new DrawThread(getHolder());
    }

    @Override
    public void onDraw(Canvas c) {
            c.drawColor(Color.BLACK);
        Paint p = new Paint();
        p.setColor(Color.RED);
        c.drawText(tmp + "", 10, 10, p);
        tmp++;

        System.out.println(tmp);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // TODO Auto-generated method stub

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        drawThread.setRunning(true);
        drawThread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        drawThread.setRunning(false);
        while (retry) {
            try {
                drawThread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }

    protected class DrawThread extends Thread {
        private SurfaceHolder surfaceHolder;
        private boolean isRunning;

        public DrawThread(SurfaceHolder surfaceHolder) {
            this.surfaceHolder = surfaceHolder;
            isRunning = false;
        }

        public void setRunning(boolean run) {
            isRunning = run;
        }

        public void run() {
            Canvas c;
            while (isRunning) {
                c = null;
                try {
                    c = surfaceHolder.lockCanvas(null);
                    synchronized (surfaceHolder) {
                        onDraw(c);
                    }
                } finally {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null) {
                        surfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }
    }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF">

    <com.niek.surfaceviewtest.StatusView
        android:id="@+id/statusview1"
        android:layout_width="fill_parent"
        android:layout_height="30dip"
        android:background="#000000"
 />
</LinearLayout>

Answer

Steve Blackwell picture Steve Blackwell · Apr 12, 2011

My only guess is that painting isn't being done on the surface because the view isn't invalidated. You're supposed to call invalidate() in order to draw, and then let the framework call onDraw(). Maybe that's why tmp is being incremented, but the paint operation only reaches the surface the first time.

It might be worth experimenting: maybe make Canvas c a member of StatusView, and then replace

synchronized (surfaceHolder) {
  onDraw(c);
}

with

synchronized (surfaceHolder) {
  invalidate();
}

Does that work?