I’m making a solitaire game for Android and so far I’ve managed to figure everything necessary out, except for the Canvas. The Canvas is being locked, drawn to, unlocked and posted, but it is NOT updating the screen.
The whole screen will draw initially, cards and all, but will never update again except until the thread throws an IllegalThreadStateException. (by putting the app into the background, by hitting the home button, or the recent apps button.)
A lot of my code was copied from tutorials in an attempt to get it working in a state I can test it in.
I am a complete beginner to Android API and so far, I absolutely hate it. Please tell me if I did anything stupid, even at all.
Target API is at 34, minimum is set to 21. My device is a physical device; TCL 10 Pro, 6.47″, API 30. Doesn’t throw anything about API, though.
Here is my custom Thread source:
public class MainThread extends Thread {
private final SurfaceHolder surfaceHolder;
private final GameBoard boardView;
private boolean running;
public static Canvas canvas;
public MainThread(SurfaceHolder surfaceHolder, GameBoard boardView) {
super();
this.surfaceHolder = surfaceHolder;
this.boardView = boardView;
}
public void setRunning(boolean isRunning) {
running = isRunning;
}
@Override
public void run() {
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized(surfaceHolder) {
this.boardView.update();
this.boardView.draw(canvas);
}
} catch (Exception e) {
System.out.println("CATCH");
} finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
Here is my (shortened) custom SurfaceView source:
public class GameBoard extends SurfaceView implements SurfaceHolder.Callback {
BaseSingleDeckGameObject gameObject;
private final MainThread thread;
// shorter constructors were here
public GameBoard(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle); //shorter constructors set these to (null, 0)
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
gameObject = new KlondikeGameObject(0, context);
}
public void update() {
gameObject.updateGame();
}
private void init(AttributeSet attrs, int defStyle) {
this.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
);
}
public boolean onTouchEvent(MotionEvent motion) {
gameObject.doTouchEvent(motion); //this just prints out the information from MotionEvent to console, and sets 2 variables.
//performClick
return true;
}
// performClick is here
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (canvas != null) {
gameObject.drawGame(canvas);
}
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
retry = false;
}
}
}
Here is my custom AppCompatActivity source:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
this.setContentView(R.layout.game_screen);
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy())
.detectLeakedClosableObjects()
.build());
}
@Override
protected void onResume() {
super.onResume();
}
}
The only 2 problems I’m really having, is getting the screen to update, and the Thread errors every time the Surface is destroyed, and then created.
Here is the error thrown:
Exception configuring surface
java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:872)
at com.severinghams.homebrewsolitaire.GameBoard.surfaceCreated(GameBoard.java:113)
at android.view.SurfaceView.updateSurface(SurfaceView.java:1208)
at android.view.SurfaceView.setWindowStopped(SurfaceView.java:294)
at android.view.SurfaceView.surfaceCreated(SurfaceView.java:1699)
at android.view.ViewRootImpl.notifySurfaceCreated(ViewRootImpl.java:1796)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2798)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2008)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8343)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1136)
at android.view.Choreographer.doCallbacks(Choreographer.java:958)
at android.view.Choreographer.doFrame(Choreographer.java:882)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1121)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:7904)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
I cannot find any documentation/examples on the standard use of drawing to a Canvas on a SurfaceView, so I’m stumped. I cannot figure out what I did wrong, but I know it isn’t anything in the custom class, because it DOES draw everything, the Thread is executing code in the SurfaceView’s draw() method, the Thread does not error, or throw, or cause any problems when it unlocks and posts, and the temporary cursor does show up. Screenshot
onTouchEvent calls a method in my game class, which sets position variables inside the game class. Then the draw() method calls drawGame() inside my game class, which should draw a square at the location provided, but it doesnt update the screen.
The white square only moves after the thread exception is thrown, but only once. It will update again when it tries to create again.
My apologies for my incredibly stilted and meandering explanation of the problem, and clear lack of brevity. In my attempts to truncate my explanation, it just becomes more disjointed.
Any help would be appreciated! I am truly stumped!
SeveringHams is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.