Проблема с отрисовкой в java на android

137
02 декабря 2020, 10:40

Недавно я с программирования приложений на java для android перешёл на игры. И тут же возникла очень большая проблема: при первом заходе в игру всё нормально, но если нажать кнопку "назад" или "домой", не закрывая окна, а затем снова открыть игру, в ней ничего нет - только пустое окно. Чего я только не перепробовал - ничего не получилось. Я начинающий девелопер, и прошу не судить за мою тупость, если таковая имеет место быть.

Код:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new DrawView(this));
    }
    class DrawView extends SurfaceView implements SurfaceHolder.Callback{
        private DrawThread drawThread;
        private Player player;
        public DrawView(Context context) {
            super(context);
            getHolder().addCallback(this);
            drawThread = new DrawThread(getHolder(),this);
            player = new Player(BitmapFactory.decodeResource(getResources(), R.drawable.player), 10, 10);
            setFocusable(true);
        }
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            drawThread.setRunning(true);
            drawThread.start();
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }
        public void onDraws(Canvas canvas){
            canvas.drawColor(Color.rgb(0, 80, 0));
            player.draws(canvas);
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            boolean retry = true;
            while (retry){
                try {
                    drawThread.join();
                    retry = false;
                } catch (InterruptedException e) {}
            }
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN){
                player.handleActionDown((int) event.getX(), (int) event.getY());
            }
            if (event.getAction() == MotionEvent.ACTION_MOVE){
                if (player.isTouched()){
                    player.setX((int) event.getX());
                    player.setY((int) event.getY());
                }
            }
            if (event.getAction() == MotionEvent.ACTION_UP){
                if (player.isTouched) player.setTouched(false);
            }
            return true;
        }
    }
    class DrawThread extends Thread {
        private boolean running = false;
        private SurfaceHolder surfaceHolder;
        private DrawView drawView;
        public DrawThread(SurfaceHolder surfaceHolder, DrawView drawView){
            this.surfaceHolder = surfaceHolder;
            this.drawView = drawView;
        }
        public void setRunning(boolean running) {
            this.running = running;
        }
        @Override
        public void run() {
            Canvas canvas;
            while (running){
                canvas = null;
                try {
                    canvas = surfaceHolder.lockCanvas(null);
                    synchronized (surfaceHolder) {
                        if (canvas == null) continue;
                        drawView.onDraws(canvas);
                    }
                }finally {
                    if (canvas != null) surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }
    class Player{
        private Bitmap bitmap;
        private int x;
        private int y;
        private boolean isTouched;
        public Player(Bitmap b, int x, int y){
            this.bitmap = b;
            this.y = y;
            this.x = x;
        }
        public void setBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
        }
        public Bitmap getBitmap() {
            return bitmap;
        }
        public void setY(int y) {
            this.y = y;
        }
        public int getY() {
            return y;
        }
        public void setX(int x) {
            this.x = x;
        }
        public int getX() {
            return x;
        }
        public void setTouched(boolean touched) {
            isTouched = touched;
        }
        public boolean isTouched() {
            return isTouched;
        }
        public void draws(Canvas canvas){
            canvas.drawBitmap(bitmap, x - bitmap.getWidth()/2, y - bitmap.getHeight()/2, null);
        }
        public void handleActionDown(int eventX, int eventY){
            if (eventX >= x - bitmap.getWidth() / 2 && eventX <= x + bitmap.getWidth() / 2 &&
                eventY >= y - bitmap.getHeight() / 2 && eventY <= y + bitmap.getHeight() / 2) setTouched(true);
            else setTouched(false);
        }
    }
}
Answer 1

surfaceDestroyed вызывается каждый раз, когда SurfaceView перестаёт быть виден на экране. В нём Вы завершаете поток рисования. Но один экземпляр потока может быть запущен только один раз. Когда он завершился, то запустить его повторно нельзя - нужно создавать новый экземпляр:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    drawThread = new DrawThread(getHolder(),this);
    drawThread.setRunning(true);
    drawThread.start();
}

P.S. Не знаю зачем Вам эта конструкция:

        while (retry){
            try {
                drawThread.join();
                retry = false;
            } catch (InterruptedException e) {}
        }

но останавливать любым способом и сильно нагружать главный поток на Андроиде крайне нежелательно.

P.P.S. А проблема то как раз ещё и в этой конструкции - она блокирует главный поток и ждёт завершения рисующего, но рисующий не останавливается так как ему не вызван drawThread.setRunning(false); и он даже не знает что надо прекратить работу. В результате главный поток не может ни завершить ни восстановить работу активити

Answer 2

Во первых, я бы избавился от анонимности DrawView и перенес бы его в разметку.

 <com.example.DrawView
        android:id="@+id/draw_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

Затем в активити определил бы его.

public class MainActivity extends AppCompatActivity {
private DrawView drawView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    dravView = findViewById(R.id.draw_view);
}
}

Далее внес бы изменения в само DrawView

class DrawView extends SurfaceView implements SurfaceHolder.Callback{
    //ваши поля
    /*без этого конструктора в разметке ваше View не разместить*/
    public DrawView(Context context,@Nullable AttributeSet attrs) {
        super(context,attrs);
    }
    /*этим методом все запускаете*/
    public voyd play(){
        getHolder().addCallback(this);
        drawThread = new DrawThread(getHolder(),this);
        player = new Player(BitmapFactory.decodeResource(getResources(), 
        R.drawable.player), 10, 10);
        setFocusable(true);
    {
    //остальной код
    }

В MainActivity переопределяем onStart()

@Override
protected void onStart() {
    super.onStart();
    drawView.play();
}

Вроде так. На машине не тестил, но должно помочь. Хотя в принципе, при нажатии кнопки Back то таких нюансов не должно было быть..

READ ALSO
Как с помощью JS определить ip пользователя?

Как с помощью JS определить ip пользователя?

Хочу сделать так что бы JS определял Ip пользователя при запуске скрипта, но не знаю как это сделатьПодскажете?

118
Импортировать картинку в поле image из canvas, Яндекс шаринг

Импортировать картинку в поле image из canvas, Яндекс шаринг

Столкнулась с проблемой, что изображение, получаемое из canvas, не добавляется в поле imageКод следующий (VueJS v

125