Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

holder.getSurface() sempre fica null

Estou tentando renderizar o passáro e não aparece nada na tela, passando no debugger eu reparei que o retorno de holder.getSurface() é sempre null, de forma que nunca é chamado o método de desenhar o passáro na tela, não ocorre nenhum erro nos logs, já tentei alterar a ordem e o escopos das variáveis e nada.

Também tentei adicionar um callback para o holder, conforme eu vi em alguns sites, também nada feito. Não sei se eu esqueci de adicionar alguma linha de inicialização... segue a classe Game do meu projeto

package br.com.alura.jumper.engine;

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

import br.com.alura.jumper.elementos.Passaro;

public class Game extends SurfaceView implements Runnable {
    private boolean isRunning = true;
    private SurfaceHolder holder = getHolder();
    private Passaro passaro;

    public Game(Context context) {
        super(context);
        IniciaElementos();
    }

    private void IniciaElementos(){
        passaro = new Passaro(100);
    }

    @Override
    public void run() {
        while(isRunning){
            if(!this.holder.getSurface().isValid()) continue;
            Canvas canvas = holder.lockCanvas();

            passaro.DesenhaNo(canvas);

            holder.unlockCanvasAndPost(canvas);
        }
    }

    public void inicia() {
        isRunning = true;
    }

    public void pausa() {
        isRunning = false;
    }
}

O problema está nessa linha, que sempre retorna true e entra no "continue"

if(!this.holder.getSurface().isValid()) continue;
3 respostas

Ricardo, tudo bem ?

Segundo a documentação do Android :

The Surface may not always be available -- for example when using a SurfaceView the holder's Surface is not created until the view has been attached to the window manager and performed a layout in order to determine the dimensions and screen position of the Surface

Pode ser isso que está acontecendo !

Seu código está desta forma :

    @Override
    public void run() {
        while(isRunning){
            if(!this.holder.getSurface().isValid()){ 
                    continue;
                }
            Canvas canvas = holder.lockCanvas();

            passaro.DesenhaNo(canvas);

            holder.unlockCanvasAndPost(canvas);
        }
    }

É esse o comportamento que você deseja ?

Oi Matheus,

Eu segui exatamente o código que foi passado na aula, eu fiz alguns testes, mas ele sempre é inválido. Eu entendo que na documentação, o holder não está pronto enquanto a view não é adicionado no layout, isso até é explicado pelo instrutor na aula, mesmo deixando a aplicação rodando por vários minutos, dando tempo da view ser anexada (assim eu espero), o holder sempre é null.

O comportamento que eu espero é que o holder seja criado enquanto o while for válido e que o pássaro seja desenhado na tela.

Vou deixar também o código do Main e do Layout, onde são definidos os elementos do qual é adicionado este SurfaceView:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


</FrameLayout>
public class MainActivity extends Activity{

    Game game;
    FrameLayout container;

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

        container = (FrameLayout) findViewById(R.id.container);

        game = new Game(this);
        container.addView(game);

    }

    @Override
    protected void onResume() {
        super.onResume();
        game.inicia();
        new Thread(game).run();
    }

    @Override
    protected void onPause() {
        super.onPause();
        game.pausa();
    }
}

Fiz alguns testes no debugger e saiu este resultado, tanto na primeira execução do laço até depois de alguns minutos o resultado é o mesmo:

Já estou sem idéias para testes...

solução!

Eu decidi refazer o projeto desde o inicio e consegui descobrir o problema, fica aí como aprendizado.

Não sei por qual motivo, mas eu adicionei o inicio da Thread com o método "run"

   @Override
    protected void onResume() {
        super.onResume();
        game.inicia();
        new Thread(game).run();
    }

O correto, que é o mesmo que está na explicação do instrutor é "start"

@Override
    protected void onResume() {
        super.onResume();
        game.inicia();
        new Thread(game).start();
    }

O mais bizarro que não ocorre nenhum erro, não sei qual a diferença entre os dois, mas talvez o start() deve iniciar alguns objetos que o run() não faz. Isso causa este comportamento peculiar.