sábado, 17 de junho de 2017

CRUD básico com Android e Firebase (parte 5)

Recuperando dados


Sempre lembrando que esta série de postagens é fortemente baseada neste post, sendo esta tradução/expansão autorizada pelo autor original.

Vamos agora recuperar os dados de Artistas, armazenados no Firebase, e exibir em uma lista (ListView).

Como já falamos anteriormente, não é nosso objetivo aqui dar detalhes do desenvolvimento Android, já que nosso foco é nas operações de dados do Firebase. Desta forma, não daremos grandes detalhes ou explicações sobre como utilizar as listas, a menos deste pequeno resumo:

  • cria-se um arquivo de layout, com a definição das Views que formarão cada “item” (linha) da lista;
  • cria-se um “Adapter”, uma classe que recupera o layout acima, “infla” ele (isto é, transforma numa View que será usada em cada item da lista), e preenche com os dados desejados;
  • finalmente, configura-se a lista para utilizar os serviços do Adapter criado acima para exibir os dados.

Começaremos então pela criação do layout do elemento gráfico que será usado como “item” da nossa lista. Crie um novo arquivo de layout (botão-direito na pasta “layout”, new, layout resource file, nome do arquivo layout_artist_item.xml),e utilize o código abaixo:


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textViewName"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <TextView
        android:id="@+id/textViewGenre"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />

</LinearLayout>

Trata-se apenas de dois “TextViews” (id's textViewName e textViewGenre), sendo o primeiro com um tamanho maior.


Vamos agora criar a classe do Adapter, responsável pela criação dos itens da lista.
Botão-direito no pacote (dentro de “Java”), new | Java Class.
Nome da classe: ArtistListAdapter.

Segue o código (comentado) da classe (sem imports e sem o package):


public class ArtistListAdapter extends ArrayAdapter<Artist> {

    private Activity context;
    private List<Artist> artistList;  // lista para armazenar os artitas

    public ArtistListAdapter(Activity context, List<Artist> artistList) {

        super(context, R.layout.layout_artist_item, artistList);
        this.context = context;
        this.artistList = artistList;
    }

    // método que é chamado para fornecer cada item da lista
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // criando um objeto "inflador"
        LayoutInflater inflater = context.getLayoutInflater();

        // usando o inflador para criar uma View a partir do arquivo de layout
        // que fizemos definindo os itens da lista
        View listViewItem = inflater.inflate(R.layout.layout_artist_item, null, true);

        // pegando referências para as views que definimos dentro do item da lista,
        // isto é, os 2 textviews
        TextView textViewName = (TextView) listViewItem.findViewById(R.id.textViewName);
        TextView textViewGenre = (TextView) listViewItem.findViewById(R.id.textViewGenre);

        // a posição do artista na lista (armazenamento) é a mesma na lista (listview)
        // então usamos esse valor (position) para acessar o objeto "Artist" correto
        // dentro da lista artistList
        Artist artist = artistList.get(position);

        // finalmente, colocamos os valores do objeto artista recuperado
        // nas views que formam nosso item da lista
        textViewName.setText(artist.getArtistName());
        textViewGenre.setText(artist.getArtistGenre());

        // a view está pronta! É só devolver para quem pediu
        return listViewItem;
    }
}

Próximo passo, inserir uma ListView no arquivo activity_main.xml.
É trivial, portanto não repetirei o código aqui.
Id desta ListView: listViewArtists.


Vamos agora alterar nossa classe MainActivity, para preencher a lista com os dados armazenados no Firebase.

Em resumo:

  • teremos uma lista de objetos da classe Artist, para armazenar os dados que serão recuperados do Firebase;
  • definiremos um método para responder ao evento “houve alteração de dados no firebase”.
    Ou seja, quando houver alguma alteração de dados no firebase (neste caso, na parte da árvore que aponta para a chave “Artists”), automaticamente este método será chamado.
  • Será neste método que teremos então:
    • o código para receber os dados do Firebase;
    • gerar objetos da classe Artist a partir desses dados;
    • armazenar esses objetos numa lista;
    • criar um adapter, passando esta lista;
    • definir este adapter como o exibidor de dados da ListView que inserimos na tela principal.

Segue abaixo o novo código da classe MainActivity (sem package e sem imports).
Removi os comentários que apareceram numa postagem anterior, e coloquei novos, relativos as mudanças apresentadas aqui.


public class MainActivity extends AppCompatActivity {

    EditText editTextName;
    Button buttonAddArtist;
    Spinner spinnerGenres;

    DatabaseReference databaseArtists;

    // para acessar nossa nova ListView
    ListView listViewArtists;

    // lista para armazenar os objetos "Artist" que leremos do banco de dados
    // obs: por enquanto, só a referência...
    List<Artist> artistList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        databaseArtists = FirebaseDatabase.getInstance().getReference("Artists");

        editTextName = (EditText) findViewById(R.id.editTextName);
        buttonAddArtist = (Button) findViewById(R.id.buttonAddArtist);
        spinnerGenres = (Spinner) findViewById(R.id.spinnerGenres);

        // acessando nossa nova ListView no arquivo de layout...
        listViewArtists = (ListView) findViewById(R.id.listViewArtists);

        // efetivamente criando a lista que vai armazenar os artistas
        // que leremos do banco de dados
        artistList = new ArrayList<>();

        buttonAddArtist.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                addArtist();
            }
        });
    }

    // lembrando o ciclo de vida de um app android, o primeiro método
    // que é executado é o onCreate, geralmente para carregar os layouts
    // e inicializar as coisas.
    //
    // logo a seguir, é chamado o método onStart. Este método também é chamado
    // quando o app estava em background e volta a ter foco. Logo, colocaremos aqui
    // o código para recuperar os dados do firebase e colocar na lista,
    // pois pode ter havido alteração nos dados quando o app estava em background
    // (por outra cópia em execução do nosso app, por exemplo).
    @Override
    protected void onStart() {

        super.onStart();

        // criando um tratador de eventos relacionado com nosso
        // banco de dados Firebase
        databaseArtists.addValueEventListener( new ValueEventListener() {

            // método chamado automaticamente quando houver mudança nos dados
            // armazenados no firebase
            // lembre-se!! databaseArtist "aponta" para a chave "Artists" no
            // JSON dos dados no firebase. Então, se algo mudar "ali dentro",
            // (isto é, dados de artistas), este método será chamado.
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                // se entrou aqui, é porque mudou alguma coisa nos artistas
                // que estão no banco de dados. Então, vamos limpar a lista
                // que armazena esses artistas, para recuperar esses dados
                // novamente (já que não sabemos exatamente o que mudou)
                artistList.clear();

                // Recebemos um objeto DataSnapshot, que tem os dados
                // apontados por nossa referencia no firebase, isto é,
                // os dados que estão "debaixo" da chave "Artists".
                // Vamos então "varrer" esse objeto, pegando os dados lá dentro
                // e criando objetos da nossa classe Artist, para colocar na lista
                for(DataSnapshot artistSnapshot : dataSnapshot.getChildren()) {

                    // artistSnapshot tem um dos "filhos" de "Artists", isto é,
                    // tem os dados de um artista.
                    // Vamos então criar um objeto artista, a partir desses dados
                    //
                    // ... getValue(Artist.class)
                    //     pegue os dados, e a partir deles crie um objeto da
                    //     classe Artist.
                    Artist artist = artistSnapshot.getValue(Artist.class);

                    // enfim, colocamos o objeto artista criado a partir dos dados lidos
                    // na nossa lista de artistas
                    artistList.add(artist);
                }

                // agora que temos nossa lista de artistas atualizada,
                // podemos criar o adapter que vai ser responsável por
                // colocar esses dados no ListView,
                // passando nossa lista para este adapter
                ArtistListAdapter adapter = new ArtistListAdapter(MainActivity.this, artistList);

                // finalmente, informamos ao ListView quem é o adapter que vai
                // exibir os dados
                listViewArtists.setAdapter(adapter);
            }

            // método chamado no caso de algum erro no banco de dados
            // (ainda dentro do listener).
            // Não definiremos nada neste momento.
            @Override
            public void onCancelled(DatabaseError databaseError) {
            }

        });
    }

    // método para adicionar um artista no banco de dados

    private void addArtist() {

        String name = editTextName.getText().toString().trim();
        String genre = spinnerGenres.getSelectedItem().toString();

        if( !TextUtils.isEmpty(name) ) {

            String id = databaseArtists.push().getKey();
            Artist artist = new Artist(id, name, genre);
            databaseArtists.child(id).setValue(artist);

            Toast.makeText(this, "Artista adicionado", Toast.LENGTH_LONG).show();
        }
        else {

            Toast.makeText(this, "Você deve digitar um nome", Toast.LENGTH_LONG).show();
        }
    }
}

Lembrando como está nosso banco de dados...


Executando o app...


Inserindo dados...




Os novos dados são exibidos automaticamente!


Funciona também se inserir dados diretamente no "console" do Firebase (simulando, por exemplo, que foi outra cópia do app, em outro celular, que inseriu os dados):



Um último detalhe, sobre o método getValue, conforme nos diz a documentação:

“Este método é usado para organizar os dados contidos em um snapshot para dentro de uma classe da sua escolha.
Esta classe deve respeitar 2 requisitos:

  • A classe deve ter um construtor default que não receba nenhum argumento.
  • A classe deve definir getters públicos para os atributos que serão utilizados.”

Dêem uma conferida na classe Artist, para ver que esses requisitos são preenchidos (já falamos isso anteriormente).


Segue para parte 6...



Nenhum comentário: