I want to do canvas.drawBitmap(...)
multiple times but now app freezes (ANR) once I move my SurfaceView (call surfaceView.setX(gs.moveX); surfaceView.setY(gs.moveY);
in onScroll(...)
) in my SurfaceView class with custom GestureDetector. This SurfaceView flow is controlled by GameThread in which I draw 3 times canvas.drawBitmap(...)
. What is worth noticing is that when I comment out second or third canvas.drawBitmap(...)
from drawTmxMaps(...)
then app scrolls almost correctly - almost because it works but scrolling is laggy.
So how to solve my problem?
Here is my code.
Loader.java
private static Map<Integer, Bitmap[]> differentFramesMap = new HashMap<>();
private static Map<Integer, Canvas[]> differentCanvasesMap = new HashMap<>();
(...)
public static void drawTmxMaps(Canvas canvas, Context context) {
canvas.drawBitmap(differentFramesMap.get(0)[framesFirstAnimIter], 0, 0, PaintTemplates.getInstance(context).pMap);
canvas.drawBitmap(differentFramesMap.get(1)[framesSecondAnimIter], 0, 0, PaintTemplates.getInstance(context).pMap);
canvas.drawBitmap(differentFramesMap.get(2)[framesThirdAnimIter], 0, 0, PaintTemplates.getInstance(context).pMap);
if (!isIterStarted) {
iterTmxFrames();
isIterStarted = true;
}
}
private static void iterTmxFrames() {
runnable = () -> {
framesFirstAnimIter++;
framesSecondAnimIter++;
framesThirdAnimIter++;
if (framesFirstAnimIter == 3)
framesFirstAnimIter = 0;
if (framesSecondAnimIter == 2)
framesSecondAnimIter = 0;
if (framesThirdAnimIter == 2)
framesThirdAnimIter = 0;
handler.postDelayed(runnable, 1000);
};
handler.postDelayed(runnable, 1000);
}
MyThread.java
@Override
public void run() {
while (isRunning) {
startTime = SystemClock.uptimeMillis();
Canvas canvas = surfaceHolder.lockCanvas(null);
if (canvas != null) {
synchronized (surfaceHolder) {
TmxLoader.drawTmxMaps(canvas, gameSurface.getContext());
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
loopTime = SystemClock.uptimeMillis() - startTime;
if (loopTime < delay) {
try {
Thread.sleep(delay - loopTime);
} catch (InterruptedException e) {
Log.e("Interupted ex", e.getMessage());
}
}
}
}
EDIT after answer
Now there is no ANR but the surface seems not to refresh because thre are old positions of canvas on the screen. It looks like this:
Solved by simply adding canvas.drawARGB(255, 0, 0, 0);
as a first method in GameThread run().
Your code should look like this:
Loader.java:
public class Loader {
private static Map<Integer, Bitmap[]> differentFramesMap = new HashMap<>();
private static Map<Integer, Canvas[]> differentCanvasesMap = new HashMap<>();
private static boolean isIterStarted = false;
private static int framesFirstAnimIter = 0;
private static int framesSecondAnimIter = 0;
private static int framesThirdAnimIter = 0;
private static Handler handler = new Handler(Looper.getMainLooper());
private static Runnable runnable;
public static void drawTmxMaps(Canvas canvas, Context context) {
canvas.drawBitmap(differentFramesMap.get(0)[framesFirstAnimIter], 0, 0, PaintTemplates.getInstance(context).pMap);
canvas.drawBitmap(differentFramesMap.get(1)[framesSecondAnimIter], 0, 0, PaintTemplates.getInstance(context).pMap);
canvas.drawBitmap(differentFramesMap.get(2)[framesThirdAnimIter], 0, 0, PaintTemplates.getInstance(context).pMap);
if (!isIterStarted) {
iterTmxFrames();
isIterStarted = true;
}
}
private static void iterTmxFrames() {
runnable = new Runnable() {
@Override
public void run() {
framesFirstAnimIter++;
framesSecondAnimIter++;
framesThirdAnimIter++;
if (framesFirstAnimIter == 3)
framesFirstAnimIter = 0;
if (framesSecondAnimIter == 2)
framesSecondAnimIter = 0;
if (framesThirdAnimIter == 2)
framesThirdAnimIter = 0;
handler.postDelayed(runnable, 1000);
}
};
handler.postDelayed(runnable, 1000);
}
}
MyThread.java:
public class MyThread extends Thread {
private final SurfaceHolder surfaceHolder;
private final GameSurface gameSurface;
private boolean isRunning;
private long delay = 33; // 30 FPS
private long startTime;
private long loopTime;
private float scrollX;
private float scrollY;
public MyThread(SurfaceHolder surfaceHolder, GameSurface gameSurface) {
this.surfaceHolder = surfaceHolder;
this.gameSurface = gameSurface;
}
public void setRunning(boolean running) {
isRunning = running;
}
public void setScroll(float scrollX, float scrollY) {
this.scrollX = scrollX;
this.scrollY = scrollY;
}
@Override
public void run() {
while (isRunning) {
startTime = SystemClock.uptimeMillis();
Canvas canvas = surfaceHolder.lockCanvas(null);
if (canvas != null) {
synchronized (surfaceHolder) {
canvas.translate(-scrollX, -scrollY);
Loader.drawTmxMaps(canvas, gameSurface.getContext());
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
loopTime = SystemClock.uptimeMillis() - startTime;
if (loopTime < delay) {
try {
Thread.sleep(delay - loopTime);
} catch (InterruptedException e) {
Log.e("Interrupted ex", e.getMessage());
}
}
}
}
}