Question

ok in the android cube example, it shows the draw() function wrapped in the runnable run(), and handler callbacks, etc are used.

public class CubeWallpaper1 extends WallpaperService {

private final Handler mHandler = new Handler();

@Override
public void onCreate() {
    super.onCreate();
}

@Override
public void onDestroy() {
    super.onDestroy();
}

@Override
public Engine onCreateEngine() {
    return new CubeEngine();
}

class CubeEngine extends Engine {

    private final Paint mPaint = new Paint();
    private float mOffset;
    private float mTouchX = -1;
    private float mTouchY = -1;
    private long mStartTime;
    private float mCenterX;
    private float mCenterY;

    private final Runnable mDrawCube = new Runnable() {
        public void run() {
            drawFrame();
        }
    };
    private boolean mVisible;

    CubeEngine() {
        // Create a Paint to draw the lines for our cube
        final Paint paint = mPaint;
        paint.setColor(0xffffffff);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStyle(Paint.Style.STROKE);

        mStartTime = SystemClock.elapsedRealtime();
    }

    @Override
    public void onCreate(SurfaceHolder surfaceHolder) {
        super.onCreate(surfaceHolder);

        // By default we don't get touch events, so enable them.
        setTouchEventsEnabled(true);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mDrawCube);
    }

    @Override
    public void onVisibilityChanged(boolean visible) {
        mVisible = visible;
        if (visible) {
            drawFrame();
        } else {
            mHandler.removeCallbacks(mDrawCube);
        }
    }

    @Override
    public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        super.onSurfaceChanged(holder, format, width, height);
        // store the center of the surface, so we can draw the cube in the right spot
        mCenterX = width/2.0f;
        mCenterY = height/2.0f;
        drawFrame();
    }

    @Override
    public void onSurfaceCreated(SurfaceHolder holder) {
        super.onSurfaceCreated(holder);
    }

    @Override
    public void onSurfaceDestroyed(SurfaceHolder holder) {
        super.onSurfaceDestroyed(holder);
        mVisible = false;
        mHandler.removeCallbacks(mDrawCube);
    }

    @Override
    public void onOffsetsChanged(float xOffset, float yOffset,
            float xStep, float yStep, int xPixels, int yPixels) {
        mOffset = xOffset;
        drawFrame();
    }

    /*
     * Store the position of the touch event so we can use it for drawing later
     */
    @Override
    public void onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            mTouchX = event.getX();
            mTouchY = event.getY();
        } else {
            mTouchX = -1;
            mTouchY = -1;
        }
        super.onTouchEvent(event);
    }

    /*
     * Draw one frame of the animation. This method gets called repeatedly
     * by posting a delayed Runnable. You can do any drawing you want in
     * here. This example draws a wireframe cube.
     */
    void drawFrame() {
        final SurfaceHolder holder = getSurfaceHolder();

        Canvas c = null;
        try {
            c = holder.lockCanvas();
            if (c != null) {
                // draw something
                drawCube(c);
                drawTouchPoint(c);
            }
        } finally {
            if (c != null) holder.unlockCanvasAndPost(c);
        }

        // Reschedule the next redraw
        mHandler.removeCallbacks(mDrawCube);
        if (mVisible) {
            mHandler.postDelayed(mDrawCube, 1000 / 25);
        }
    }

    /*
     * Draw a wireframe cube by drawing 12 3 dimensional lines between
     * adjacent corners of the cube
     */
    void drawCube(Canvas c) {
        c.save();
        c.translate(mCenterX, mCenterY);
        c.drawColor(0xff000000);
        drawLine(c, -400, -400, -400,  400, -400, -400);
        drawLine(c,  400, -400, -400,  400,  400, -400);
        drawLine(c,  400,  400, -400, -400,  400, -400);
        drawLine(c, -400,  400, -400, -400, -400, -400);

        drawLine(c, -400, -400,  400,  400, -400,  400);
        drawLine(c,  400, -400,  400,  400,  400,  400);
        drawLine(c,  400,  400,  400, -400,  400,  400);
        drawLine(c, -400,  400,  400, -400, -400,  400);

        drawLine(c, -400, -400,  400, -400, -400, -400);
        drawLine(c,  400, -400,  400,  400, -400, -400);
        drawLine(c,  400,  400,  400,  400,  400, -400);
        drawLine(c, -400,  400,  400, -400,  400, -400);
        c.restore();
    }

    /*
     * Draw a 3 dimensional line on to the screen
     */
    void drawLine(Canvas c, int x1, int y1, int z1, int x2, int y2, int z2) {
        long now = SystemClock.elapsedRealtime();
        float xrot = ((float)(now - mStartTime)) / 1000;
        float yrot = (0.5f - mOffset) * 2.0f;
        float zrot = 0;

        // 3D transformations

        // rotation around X-axis
        float newy1 = (float)(Math.sin(xrot) * z1 + Math.cos(xrot) * y1);
        float newy2 = (float)(Math.sin(xrot) * z2 + Math.cos(xrot) * y2);
        float newz1 = (float)(Math.cos(xrot) * z1 - Math.sin(xrot) * y1);
        float newz2 = (float)(Math.cos(xrot) * z2 - Math.sin(xrot) * y2);

        // rotation around Y-axis
        float newx1 = (float)(Math.sin(yrot) * newz1 + Math.cos(yrot) * x1);
        float newx2 = (float)(Math.sin(yrot) * newz2 + Math.cos(yrot) * x2);
        newz1 = (float)(Math.cos(yrot) * newz1 - Math.sin(yrot) * x1);
        newz2 = (float)(Math.cos(yrot) * newz2 - Math.sin(yrot) * x2);

        // 3D-to-2D projection
        float startX = newx1 / (4 - newz1 / 400);
        float startY = newy1 / (4 - newz1 / 400);
        float stopX =  newx2 / (4 - newz2 / 400);
        float stopY =  newy2 / (4 - newz2 / 400);

        c.drawLine(startX, startY, stopX, stopY, mPaint);
    }

    /*
     * Draw a circle around the current touch point, if any.
     */
    void drawTouchPoint(Canvas c) {
        if (mTouchX >=0 && mTouchY >= 0) {
            c.drawCircle(mTouchX, mTouchY, 80, mPaint);
        }
    }

}

}

but should not the draw() method be replaced in areas like onVisibilityChanged with

handler.post(mDrawCube);

because it seems to me that the cube is being written outside of the thread. i'm sure i'm wrong but would like verification : ). is the only function of the thread to cover the destruction of the app, and not for calling in onVisibilityChanged, onSurfaceChanged, onOffsetsChanged?

Was it helpful?

Solution

ah! now i get it. the thread is also run under the draw() method itself. i totally missed that. so whenever drawFrame is called the thread is called through the handler. crazy it seems very loopy.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top