Tutorial Android #7 – Menus e Mensagens

Padrão

Olá pessoal! Continuando com a nossa série sobre Android, hoje vamos ver como adicionar itens de menu (acessíveis pelo botão físico de menu do dispositivo) e mensagens informativas (anotações) para os itens listados na nossa Lista de Restaurantes.

O primeiro passo, então, é adicionarmos em nossa classe de modelo o campo anotações, do tipo String. Após os o campo mais o setter e getter, a nossa classe Restaurante ficará assim:

package net.rafaeltoledo.restaurante.model;

public class Restaurante {

	private String nome = "";
	private String endereco = "";
	private String tipo = "";
	private String anotacoes = "";

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getEndereco() {
		return endereco;
	}

	public void setEndereco(String endereco) {
		this.endereco = endereco;
	}

	public String getTipo() {
		return tipo;
	}

	public void setTipo(String tipo) {
		this.tipo = tipo;
	}

	public String getAnotacoes() {
		return anotacoes;
	}

	public void setAnotacoes(String anotacoes) {
		this.anotacoes = anotacoes;
	}

	@Override
	public String toString() {
		return getNome();
	}
}

Continuando, vamos agora adicionar o campo para as anotações em nosso arquivo main.xml. Antes do nó Button, adicione o campo:

<TableRow>
	<TextView android:text="Anotações:"/>
	<EditText android:id="@+id/anotacoes"
		android:singleLine="false"
		android:gravity="top"
		android:lines="2"
		android:scrollHorizontally="false"
		android:maxLines="2"
		android:maxWidth="200sp"/>
</TableRow>

Caso execute o aplicativo, o formulário terá essa aparência:

O próximo passo é criarmos o menu, e configurá-lo para ser exibido quando o usuário pressionar a tecla Menu do celular. Para isso, vamos criar um novo arquivo XML, chamado opcao.xml, colocando-o em res/menu. O arquivo terá o seguinte conteúdo:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:id="@+id/anotacao"
	    android:title="Exibir Anotação"
	    android:icon="@drawable/anotacao"/>
</menu>

O ícone anotacao (anotacao.png) pode ser baixado no final do post, junto com o projeto.

Continuando, agora adicione o seguinte método a nossa classe ListaRestaurantes:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
	new MenuInflater(this).inflate(R.menu.opcao, menu);
	return super.onCreateOptionsMenu(menu);
}

Agora, ao pressionar a tecla Menu do celular, a opção definida será exibida a opção de menu que definimos.

Agora, vamos adicionar o controle sobre o botão, para que a anotação seja exibida quando o usuário selecionar o botão Exibir Anotação. A questão principal, é que para isso precisamos saber qual anotação devemos exibir. Para isso, vamos criar um controle para saber qual restaurante está selecionado.

Para começar, vamos adicionar um novo atributo à nossa classe ListaRestaurantes, chamado atual, que será inicializado em null.

Restaurante atual = null;

Após isso, nos métodos onSave() e onListClick(), em vez de utilizar a variável local r, vamos utilizar o atributo atual. Além disso, lembre-se de adicionar as referências também ao novo atributo (anotacoes) nestes métodos, e crie um atributo para ele, juntamente com nome, endereco e tipos.

Por fim, adicione a seguinte implementação do método onOptionsItemSelected(), lembrando do atalho Ctrl + Shift + O para corrigir as importações.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
	if (item.getItemId() == R.id.anotacao) {
		String mensagem = "Nenhum restaurante selecionado";

		if (atual != null) {
			mensagem = atual.getAnotacoes();
		}

		Toast.makeText(this, mensagem, Toast.LENGTH_LONG).show();

		return true;
	}

	return super.onOptionsItemSelected(item);
}

Agora, ao cadastrar um restaurante e ir para a listagem, experimente pressionar a tecla menu para ver o que acontece.

Pra quem perdeu alguma parte ou não conseguiu acompanhar, segue a listagem da classe ListaRestaurantes:

package net.rafaeltoledo.restaurante;

import java.util.ArrayList;
import java.util.List;

import net.rafaeltoledo.restaurante.model.Restaurante;
import android.app.TabActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.Toast;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;

public class ListaRestaurantes extends TabActivity {

	List<Restaurante> listaRestaurantes = new ArrayList<Restaurante>();
	AdaptadorRestaurante adaptador = null;
	Restaurante atual = null;

	EditText nome = null;
	EditText endereco = null;
	EditText anotacoes = null;
	RadioGroup tipos = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		nome = (EditText) findViewById(R.id.nome);
		endereco = (EditText) findViewById(R.id.end);
		tipos = (RadioGroup) findViewById(R.id.tipos);

		Button salvar = (Button) findViewById(R.id.salvar);
		salvar.setOnClickListener(onSave);

		ListView lista = (ListView) findViewById(R.id.restaurantes);
		adaptador = new AdaptadorRestaurante();
		lista.setAdapter(adaptador);
		lista.setOnItemClickListener(onListClick);

		TabSpec descritor = getTabHost().newTabSpec("tag1");
		descritor.setContent(R.id.restaurantes);
		descritor.setIndicator("Lista", getResources().getDrawable(R.drawable.lista));
		getTabHost().addTab(descritor);

		descritor = getTabHost().newTabSpec("tag2");
		descritor.setContent(R.id.detalhes);
		descritor.setIndicator("Detalhes", getResources().getDrawable(R.drawable.restaurante));
		getTabHost().addTab(descritor);

		getTabHost().setCurrentTab(0);
	}

	private OnItemClickListener onListClick = new OnItemClickListener() {
		public void onItemClick(AdapterView<?> parent, View view, int position,
				long id) {
			atual = listaRestaurantes.get(position);
			nome.setText(atual.getNome());
			endereco.setText(atual.getEndereco());
			anotacoes.setText(atual.getAnotacoes());

			if (atual.getTipo().equals("rodizio")) {
				tipos.check(R.id.rodizio);
			} else if (atual.getTipo().equals("fast_food")) {
				tipos.check(R.id.fast_food);
			} else {
				tipos.check(R.id.a_domicilio);
			}

			getTabHost().setCurrentTab(1);
		}
	};

	private OnClickListener onSave = new OnClickListener() {

		public void onClick(View arg0) {
			atual = new Restaurante();
			EditText nome = (EditText) findViewById(R.id.nome);
			EditText endereco = (EditText) findViewById(R.id.end);
			EditText anotacoes = (EditText) findViewById(R.id.anotacoes);

			atual.setNome(nome.getText().toString());
			atual.setEndereco(endereco.getText().toString());
			atual.setAnotacoes(anotacoes.getText().toString());

			RadioGroup tipos = (RadioGroup) findViewById(R.id.tipos);

			switch (tipos.getCheckedRadioButtonId()) {
			case R.id.rodizio:
				atual.setTipo("rodizio");
				break;
			case R.id.fast_food:
				atual.setTipo("fast_food");
				break;
			case R.id.a_domicilio:
				atual.setTipo("a_domicilio");
				break;
			}

			adaptador.add(atual);
		}
	};

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		new MenuInflater(this).inflate(R.menu.opcao, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (item.getItemId() == R.id.anotacao) {
			String mensagem = "Nenhum restaurante selecionado";

			if (atual != null) {
				mensagem = atual.getAnotacoes();
			}

			Toast.makeText(this, mensagem, Toast.LENGTH_LONG).show();

			return true;
		}

		return super.onOptionsItemSelected(item);
	}

	class AdaptadorRestaurante extends ArrayAdapter<Restaurante> {
		AdaptadorRestaurante() {
			super(ListaRestaurantes.this, R.layout.linha,
					listaRestaurantes);
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			View linha = convertView;
			ArmazenadorRestaurante armazenador = null;

			if (linha == null) {
				LayoutInflater inflater = getLayoutInflater();
				linha = inflater.inflate(R.layout.linha, parent, false);
				armazenador = new ArmazenadorRestaurante(linha);
				linha.setTag(armazenador);
			} else {
				armazenador = (ArmazenadorRestaurante) linha.getTag();
			}

			armazenador.popularFormulario(listaRestaurantes.get(position));

			return linha;
		}
	}

	static class ArmazenadorRestaurante {
		private TextView nome = null;
		private TextView endereco = null;
		private ImageView icone = null;

		ArmazenadorRestaurante(View linha) {
			nome = (TextView) linha.findViewById(R.id.titulo);
			endereco = (TextView) linha.findViewById(R.id.endereco);
			icone = (ImageView) linha.findViewById(R.id.icone);
		}

		void popularFormulario(Restaurante r) {
			nome.setText(r.getNome());
			endereco.setText(r.getEndereco());

			if (r.getTipo().equals("rodizio")) {
				icone.setImageResource(R.drawable.rodizio);
			} else if (r.getTipo().equals("fast_food")) {
				icone.setImageResource(R.drawable.fast_food);
			} else {
				icone.setImageResource(R.drawable.entrega);
			}
		}
	}
}

E se quiser, pode baixar o projeto aqui.

Até a próxima!

10 comentários sobre “Tutorial Android #7 – Menus e Mensagens

  1. lucas rosseti

    Rafael muito bom teu tutorial ta me ajudando muito mas tive um problema que não consegui resolver (também por que não identifiquei a fonte) quando terminei de acompanhar o código com o menu tentei rodar mas quando seleciono um restaurante da lista da erro e o programa fecha pelo log do eclipse a unica coisa que consegui descobrir foi isso,

    03-28 17:00:02.922: E/AndroidRuntime(599): java.lang.NullPointerException
    03-28 17:00:02.922: E/AndroidRuntime(599): at com.android.listaderestaurantes.ListaRestaurantes$1.onItemClick(ListaRestaurantes.java:75)

    que me fez pensar que tinha deixado alguma coisa faltando em alguma parte do código então copiei todo o código (Ctrl+c / Ctrl+v dessa vez) para ter certeza que não tinha faltado nada e… mais uma vez o mesmo erro o que sera que é isso?
    Pode me ajudar (mais do que ja esta ajudando)?
    Grato por sua atenção e mais ainda pelas aulas…

    • Olá Lucas!

      Dê uma olhada em qual instrução está na linha 75, indicada no erro. A exceção NullPointerException indica que foi chamado algum método de algum objeto não instanciado. Outra coisa, é tentar baixar o zip do projeto e ver se tem alguma discrepância em relação ao conteúdo do post (às vezes eu esqueci alguma coisa na hora de passar do projeto pro post). Se achar algo, me avise que eu corrijo! 🙂

      • lucas rosseti

        A instrução da linha 75 é
        anotacoes.setText(atual.getAnotacoes());

        a unica chamada dessa linha é ao setAnotacoes() e esse método esta implementado em Restaurante.java…

        E como você sugeriu tentei baixar o zip do projeto e deu esse erro…
        [2012-03-28 17:59:17 – ListaDeRestaurantes2] Unable to resolve target ‘Google Inc.:Google APIs:8’

        Mais uma vez grato pela atenção

      • lucas rosseti

        Aha…
        No armazenador restaurante não tem nada tomando o conteúdo do campo anotacoes mas quando se clica em um item da lista ele tenta mandar algo para EditText la não sera esse o problema? Foi a unica coisa que eu encontrei…

        • Verifique no final do onCreate() do ListaRestaurantes se está havendo o carregamento correto do objeto. Deve ter uma coisa mais ou menos assim.

          idRestaurante = getIntent().getStringExtra(ListaRestaurantes._ID);		
          	
          if (idRestaurante != null) {
          	carregar();
          }

          Quanto ao fato de rodar os apps, tem que corrigir as opções da SDK e da sua JVM na seção Java Build Path das propriedades do projeto.

  2. Vívian

    Rafael,
    Também tentei executar este projeto e tenho o mesmo erro NullPointerException na linha 75. No projeto não existe idRestaurante nem metodo carregar…

    • Baixei e executei… realmente faltou uma linhazinha no código do app que fiz upload. Basta adicionar depois da linha 46 a obtenção do campo de anotações. Fica assim:

      nome = (EditText) findViewById(R.id.nome);
      endereco = (EditText) findViewById(R.id.end);
      tipos = (RadioGroup) findViewById(R.id.tipos);
      anotacoes = (EditText) findViewById(R.id.anotacoes);

      Já corrigi também o arquivo anexo ao post 😉

  3. Lucas

    Rafael,
    Tenho um bom conhecimento em java, vi que é facil programar para android,
    Me tire uma duvida se puder, e me diga o que eu devo estudar, pois o meu conhecimento é basico nesse assunto: eu tenho minha aplicação bem simples por exemplo, os usuarios vão postar o que eles estão fazendo e essas atualizações que os proprios usuarios fazem, serão mandadas pro servidor automaticamente, e já serão distribuidas pra todos os usuarios que usam o aplicativo…. Não sei se ficou claro, mais acredito que sejá bem simples, como devo proceder?

    Muito Obrigado Rafael!

  4. Rafael, gostaria de deixar aqui minhas parabenizações! Muito obrigado por este conteúdo de qualidade! Excelente sua iniciativa, está de parabéns. Aproveitando, estou utilizando seu tutorial para fazer um pequeno aplicativo, com algumas coisas a mais. Consegui executar estes passos com sucesso, porém, tive um problema. Ao clicar no menu e exibir a anotação, ele exibe as opções do Menu Radio Group. Por exemplo, se cadastrei como Rodizio, ele mostra a anotação Rodizio. Onde será que errei? Obrigado mais uma vez! Abraços

  5. Olá Rafael Eu estava com um problema ao testar o app. ao clicar sobre os registros da lista na tentativa de ver as anotações, o app era encerrado. Então resolvi baixar o projeto e substitui-lo todo. Ao executar o projeto baixado acontece o mesmo problema. Sempre ao clicar sobre algum item da lista que cadastrei. Então exibe a mensagem: The application has stopped unexpectedly. Please try again.

Deixe uma resposta