DIGITALREP

Update an Android SurfaceView using a ThreadAugust 26, 2016

The Activity

Firstly, you will want to launch the SurfaceView from an Activity:


package easyprogrammingexamples.com.surface;

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

public class TheActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);

        //Use this if you don't want to use XML layout files
        //surfaceView = new TheSurfaceView(this);
        //setContentView(surfaceView);

    }
}

The XML Layout File

If you want to use an XML layout file, you can put your SurfaceView inside a LinearLayout, or RelativeLayout, Frame etc, like this:


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="easyprogrammingexamples.com.surface.TheActivity">

    <easyprogrammingexamples.com.surface.TheSurfaceView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

The SurfaceView

Your SurfaceView will look something like this (before you add the code to draw stuff, anyways):


easyprogrammingexamples.com.surface;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.Arrays;
import java.util.Random;

public class TheSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private Context context;
    private TheThread thread;

    public TheSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        setWillNotDraw(false);
        getHolder().addCallback(this);
    }

    public TheSurfaceView(Context context) {
        super(context);
    }

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

    }

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

            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        thread = new TheThread(surfaceHolder, context, this);
        thread.setRunning(true);
        thread.start();
    }

    public void customDraw(Canvas canvas) {
        
    }
}

Here we have two different constructors and three surfaceholder callback methods.

Inside surfaceCreated, we instantiate our custom thread and start it running.

When surfaceDestroyed is run, the extra thread rejoins with the main thread. At least, it keeps trying until it finally does it, anyway.

As you can see, there is nothing in our custom draw method yet. We’ll fix that later.

 

The thread

The thread will look like this:


package easyprogrammingexamples.com.surface;

import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class TheThread extends Thread {

    boolean run;
    Context context;
    SurfaceHolder surfaceHolder;
    Canvas canvas;
    TheSurfaceView surfaceView;

    public TheThread(SurfaceHolder surfaceHolder, Context context, TheSurfaceView surfaceView) {
        this.surfaceHolder = surfaceHolder;
        this.context = context;
        this.surfaceView = surfaceView;
        run = false;
    }

    public void setRunning(boolean run) {
        this.run = run;
    }

    @Override
    public void run() {
        super.run();
        while(run){
            canvas = surfaceHolder.lockCanvas();
            if (canvas != null) {
                surfaceView.customDraw(canvas);
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }

}

The thread calls the customDraw method of the surfaceView all the time – constantly, in fact; at least, while run is set to true.

At the moment, surfaceView has nothing going on – no drawing is happening.

Let’s draw a background image and a little sprite in front of it onto the Surface View.

Then later on we can capture onTouch events and handle them somehow…

 

The SurfaceView, take two


easyprogrammingexamples.com.surface;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.Arrays;
import java.util.Random;

public class TheSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private Context context;
    private TheThread thread;

    private Bitmap backgroundImage;
    private Bitmap littleSprite;

    public TheSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        setWillNotDraw(false);
        getHolder().addCallback(this);
    }

    public TheSurfaceView(Context context) {
        super(context);
    }

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

    }

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

            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.background);
        littleSprite = BitmapFactory.decodeResource(getResources(), R.drawable.sprite);

        thread = new TheThread(surfaceHolder, context, this);
        thread.setRunning(true);
        thread.start();
    }

    public void customDraw(Canvas canvas) {
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(backgroundImage, 0, 0, null);    
        canvas.drawBitmap(littleSprite, 40, 40, null); 
        //canvas.drawBitmap(littleSprite, posX, posY, null);    
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //To get the touch x and y
        float x = event.getX();
        float y = event.getY();   
    
        //Do something here

        return true;
    }
}

Inside surfaceCreated, we’re grabbing some bitmap drawables from the res folder and setting them up. We’ve also made them class-wide variables. It’s better to load them here rather than every single time the customDraw method is run.

Inside customDraw, we actually draw the background image and sprite. We make everything black first in case later on we want to move littleSprite around the screen…

We also have onTouchEvent. This has to return true.

You could store littleSprite’s position… Make two ints, posX and posY, and use them to draw littleSprite, perhaps. On touch events you could change posX and posY.

 

Hope this helps.

Category: Tutorials
Tags:  

Thoughts onUpdate an Android SurfaceView using a Thread

Leave a Reply

Your email address will not be published. Required fields are marked *