Я хочу сделать canvas.drawBitmap(...)
несколько раз, но теперь приложение зависает (ANR), как только я перемещаю свой SurfaceView (вызов surfaceView.setX(gs.moveX); surfaceView.setY(gs.moveY);
в onScroll(...)
) в моем классе SurfaceView с пользовательским GestureDetector. Этот поток SurfaceView контролируется GameThread, в котором я рисую 3 раза canvas.drawBitmap(...)
. Стоит отметить, что когда я комментирую второй или третий canvas.drawBitmap(...)
из drawTmxMaps(...)
, приложение прокручивается почти правильно — почти потому, что оно работает, но прокрутка лагает.
Так как решить мою проблему?
Вот мой код.
Загрузчик.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());
}
}
}
}
РЕДАКТИРОВАТЬ после ответа
Теперь нет ANR, но поверхность, кажется, не обновляется, потому что на экране есть старые позиции холста. Это выглядит так:
Решается простым добавлением canvas.drawARGB(255, 0, 0, 0);
в качестве первого метода в GameThread run().
Похоже, ваш класс MyThread рисует все в основном потоке пользовательского интерфейса. В результате пользовательский интерфейс зависает из-за дополнительной работы.
Лучший способ избежать этого — создать «Runnable», который выполняет рисование и пересылает его «Handler» в своем собственном потоке.
Вот как вы можете изменить свой код, чтобы добиться этого:
Загрузчик.java
public static void drawTmxMaps(Canvas canvas, Context context) {
Canvas[] canvases = differentCanvasesMap.get(0);
Bitmap[] bitmaps = differentFramesMap.get(0);
for (int i = 0; i < canvases.length; i++) {
canvases[i].drawBitmap(bitmaps[i], 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;
}
// Post the drawing to a handler on a separate thread
handler.post(new Runnable() {
@Override
public void run() {
Canvas[] canvases = differentCanvasesMap.get(0);
Bitmap[] bitmaps = differentFramesMap.get(0);
for (int i = 0; i < canvases.length; i++) {
canvases[i].drawBitmap(bitmaps[i], 0, 0, null);
}
}
});
handler.postDelayed(runnable, 1000);
}
};
handler.postDelayed(runnable, 1000);
}
В методе drawTmxMaps() создается цикл для перебора каждого холста и растрового изображения и отрисовки их всех одновременно.
В методе «iterTmxFrames()» мы используем «Handler» для публикации «Runnable», где он разделяет потоки каждые 1000 мс.
«Runnable» выполняет фактическое рисование на холсте, используя растровые изображения из «DifferentFramesMap».
В классе «MyThread» вызовите «Loader.drawTmxMaps()», чтобы нарисовать растровые изображения. После этого «surfaceHolder.unlockCanvasAndPost(canvas)» можно использовать для публикации полученных объектов «Canvas» в «SurfaceHolder».
После этого «SurfaceView» больше не должен зависать или отставать при прокрутке.
На самом деле SurfaceView следует перенести из основного потока пользовательского интерфейса в отдельный поток.
Создайте новый поток для обработки движения SurfaceView.
частный класс MoveSurfaceViewThread расширяет поток { закрытый окончательный SurfaceView SurfaceView; приватный финальный поплавок moveX; приватный финальный поплавок moveY;
public MoveSurfaceViewThread (SurfaceView surfaceView, float moveX, float moveY) { this.surfaceView = SurfaceView; это.переместитьX = переместитьX; this.moveY = двигатьсяY; }
@Override публичный недействительный запуск () { поверхностьView.setX(переместитьX); SurfaceView.setY(переместитьY); } }
В своем пользовательском GestureDetector создайте экземпляр MoveSurfaceViewThread и запустить его при прокрутке
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
float moveX = gameSurface.getX() - distanceX;
float moveY = gameSurface.getY() - distanceY;
// Create a new thread to move the surfaceView
MoveSurfaceViewThread moveSurfaceViewThread = new
MoveSurfaceViewThread(gameSurface, moveX, moveY);
moveSurfaceViewThread.start();
return true;
}
Помещение SurfaceView в отдельный поток сделает приложение более отзывчивым и предотвратит блокировку потока пользовательского интерфейса.
Может быть, мне также следует что-то изменить в MyThread?
Надеюсь, отредактированный ответ поможет вам
так здорово, так что вы помните меня :). Завтра протестирую, но напишите, как именно должен выглядеть MyThread?
Ваш код должен выглядеть так:
Загрузчик.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());
}
}
}
}
}
Спасибо, есть прогресс, хе-хе, ANR нет, но я добавил свой текущий скриншот, когда я перемещаю холст, что тогда происходит.
Спасибо за ваш ответ, но ваш код вообще не рисует эти растровые изображения [] :(. Ни одно из растровых изображений не рисуется