Mostrando postagens com marcador Android. Mostrar todas as postagens
Mostrando postagens com marcador Android. Mostrar todas as postagens

28 de fevereiro de 2019

Exibindo uma Activity no Android

No sistema operacional do Android as telas que são apresentadas para o usuário são chamadas de Activity. Este post demonstra como chamar uma Activity “filha” através de uma Activity “pai” e retornar uma resposta quando o usuário retorna para a Activity “pai”.

Para demonstrar a chamada da Activity foi utilizado o mesmo projeto do post Criando um ListAdapter Customizado para o ListView do Android como projeto de partida. No projeto foi criado um ListView que exibe as informações dos Estados do Brasil.

Neste post será implementada a chamada de uma Activity “filha” para exibir os detalhes do Estado que foi selecionado pelo usuário. Na Figura 1 temos a Activity principal que exibe a relação de Estados do Brasil, juntamente com as informações da Capital e Área do Estado. Na Figura 2 temos a Activity “filha” que exibe os detalhes do Estado selecionado pelo usuário. No exemplo o Estado selecionado foi Rio Grande do Norte.

figura_1_2
Para exibir a Activity “filha” foi necessário fazer algumas inserções no código do projeto de partida, demonstrado na Listagem 1:

Listagem 1:
   1:  package br.com.romar;
   2:   
   3:  import java.util.ArrayList;
   4:  import java.util.List;
   5:   
   6:  import android.app.ListActivity;
   7:  import android.content.Intent;
   8:  import android.os.Bundle;
   9:  import android.view.View;
  10:  import android.widget.AdapterView;
  11:  import android.widget.ListView;
  12:  import android.widget.AdapterView.OnItemClickListener;
  13:   
  14:  public class Main extends ListActivity implements OnItemClickListener {
  15:      List<State> stateList;
  16:      
  17:      /** Called when the activity is first created. */
  18:      @Override
  19:      public void onCreate(Bundle savedInstanceState) {
  20:          super.onCreate(savedInstanceState);
  21:          
  22:          stateList = new ArrayList<State>();
  23:          
  24:          for (int i = 0; i < states.length; i++) {
  25:              State state = new State();
  26:              state.setState(states[i][0]);
  27:              state.setAbbreviation(states[i][1]);
  28:              state.setCapital(states[i][2]);
  29:              state.setArea(Float.parseFloat(states[i][3]));
  30:              state.setBanner(images[i]);
  31:              
  32:              stateList.add(state);
  33:          }  
  34:          
  35:          setListAdapter(new StateAdapter(this, stateList));
  36:          
  37:          ListView listview = getListView();
  38:          
  39:          listview.setOnItemClickListener(this);
  40:      }
  41:      
  42:      @Override
  43:      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  44:          
  45:          // Obtêm os detalhes do Estado selecionado
  46:          State state = stateList.get(position);
  47:          
  48:          // Exibe a Activity com os detalhes dos Estados
  49:          Intent intent = new Intent(this, DetailActivity.class);
  50:          Bundle params = new Bundle();
  51:          params.putString("estado", state.getState());
  52:          params.putString("capital", state.getCapital());
  53:          params.putFloat("area", state.getArea());
  54:          intent.putExtras(params);
  55:          
  56:          startActivity(intent);
  57:      }
  58:   
  59:      
  60:      private String[][] states = new String[][]{
  61:             {"Acre", "AC", "Rio Branco", "152581.4"},
  62:             {"Alagoas", "AL", "Maceió", "27767.7"},
  63:             {"Amapá", "AP", "Macapá", "142814.6"},
  64:             {"Amazonas", "AM", "Manaus", "1570745.7"},
  65:             {"Bahia", "BA", "Salvador", "564692.7"},
  66:             {"Ceará", "CE", "Fortaleza", "148825.6"},
  67:             {"Distrito Federal", "DF", "Brasília", "5822.1"},
  68:             {"Espírito Santo", "ES", "Vitória", "46077.5"},
  69:             {"Goiás", "GO", "Goiânia", "340086.7"},
  70:             {"Maranhão", "MA", "São Luís", "331983.3"},
  71:             {"Mato Grosso", "MT", "Cuiabá", "903357.9"},
  72:             {"Mato Grosso do Sul", "MS", "Campo Grande", "357125.0"},
  73:             {"Minas Gerais", "MG", "Belo Horizonte", "586528.3"},
  74:             {"Pará", "PA", "Belém", "1247689.5"},
  75:             {"Paraíba", "PB", "João Pessoa", "56439.8"},
  76:             {"Paraná", "PR", "Curitiba", "199314.9"},
  77:             {"Pernambuco", "PE", "Recife", "98311.6"},
  78:             {"Piauí", "PI", "Teresina", "251529.2"},
  79:             {"Rio de Janeiro", "RJ", "Rio de Janeiro", "43696.1"},
  80:             {"Rio Grande do Norte", "RN", "Natal", "52796.8"},
  81:             {"Rio Grande do Sul", "RS", "Porto Alegre", "281748.5"},
  82:             {"Rondônia", "RO", "Porto Velho", "237576.2"},
  83:             {"Roraima", "RR", "Boa Vista", "224299.0"},
  84:             {"Santa Catarina", "SC", "Florianópolis", "95346.2"},
  85:             {"São Paulo", "SP", "São Paulo", "248209.4"},
  86:             {"Sergipe", "SE", "Aracaju", "21910.3"},
  87:             {"Tocantins", "TO", "Palmas", "277620.9"}
  88:         };
  89:      
  90:      private int[] images = new int[]{
  91:              R.drawable.acre,
  92:              R.drawable.alagoas,
  93:              R.drawable.amapa,
  94:              R.drawable.amazonas,
  95:              R.drawable.bahia,
  96:              R.drawable.ceara,
  97:              R.drawable.distritofederal,
  98:              R.drawable.espiritosanto,
  99:              R.drawable.goias,
 100:              R.drawable.maranhao,
 101:              R.drawable.matogrosso,
 102:              R.drawable.matogrossosul,
 103:              R.drawable.minasgerais,
 104:              R.drawable.para,
 105:              R.drawable.paraiba,
 106:              R.drawable.parana,
 107:              R.drawable.pernambuco,
 108:              R.drawable.piaui,
 109:              R.drawable.riojaneiro,
 110:              R.drawable.riograndenorte,
 111:              R.drawable.riograndesul,
 112:              R.drawable.rondonia,
 113:              R.drawable.roraima,
 114:              R.drawable.santacatarina,
 115:              R.drawable.saopaulo,
 116:              R.drawable.sergipe,
 117:              R.drawable.tocatins
 118:      };
 119:   
 120:  }

Na linha 14 foi implementada a interface OnItemClickListener.

Na linha 37 foi obtido o widget ListView da Activity através do método getListView().

Na linha 39 foi registrado um callback para ser chamado quando um item no AdapterView é clicado, através do método setOnItemClickListener(AdapterView.OnItemClick listener).

Nas linhas 42 a 57 é sobrescrito o método onItemClick(AdapterView<?> parent, View view, int position, long id), que obtêm o Estado selecionado e as informações a serem exibidas na segunda Activity. Através do método startActivity(Intent) a Activity da Figura 2 é exibida.

Nas linhas 49 a 54 é criado o objeto intent e o objeto params onde são definidas as informações que serão passadas para a Activity “filha”.

Para que seja exibida a Activity “filha” (chamada de DetailActivity) não esquecer de declarar a Activity  no arquivo AndroidManifest.xml do projeto.
        <activity android:name=".DetailActivity" android:label="Detalhes do Estado"/>
No arquivo da Activity DetailActivity no método OnCreate(Bundle savedInstanceState) são obtidos os valores das informações passadas pela Activity “pai”, demonstrado na Listagem 2.

Listagem 2:
   1:      @Override
   2:      protected void onCreate(Bundle savedInstanceState) {
   3:          super.onCreate(savedInstanceState);
   4:          
   5:          // Configura o layout da Activity
   6:          setContentView(R.layout.state_detail);
   7:          
   8:          // Widgets da Activity
   9:          TextView textEstado = (TextView)findViewById(R.id.textEstado);
  10:          TextView textCapital = (TextView)findViewById(R.id.textCapital);
  11:          TextView textArea = (TextView)findViewById(R.id.textArea);
  12:          
  13:          // Carrega os valores passados pela Activity Principal (Main)
  14:          Intent intent = getIntent();
  15:          
  16:          if (intent != null) {
  17:              textEstado.setText(intent.getStringExtra("estado"));
  18:              textCapital.setText(intent.getStringExtra("capital"));
  19:              textArea.setText(String.valueOf(intent.getFloatExtra("area", 0)));            
  20:          }
  21:      }

Na linha 14 as informações passadas pela Activity “pai” são obtidas através do método getIntent().
Agora, e se com a apresentação da Activity “filha” quisessemos preencher algum campo e retornar esta informação para a Activity “pai”? Isto pode ser feito utilizando-se o método startActivityForResult(Intent, int) ao invés do método startActivity(Intent) utilizado anteriormente.

O método startActivityForResult(Intent, int)  faz a chamada de outra Activity, só que desta vez além do parâmetro Intent é passado como parâmetro um número inteiro utilizado para identificar a chamada.O resultado retornará através do método onActivityResult(int, int, Intent).

Quando saímos de uma Activity, ela pode chamar o método setResult(int) para retornar um dado de volta para a Activity chamadora. A Activity deve fornecer um código, que pode ser os resultados padrão RESULT_CANCELED, RESULT_OK, ou qualquer valor customizado. Opcionalmente ela pode retornar um Intent contendo dados adicionais.

Para demonstrar como retornar dados de uma Activity “filha”, vou utilizar o mesmo projeto fazendo pequenas alterações.

Na classe Main exibida na Listagem 1 acima, incluir a declaração da seguinte constante na classe.
private final static int CALL_SECOND_ACTIVITY = 1;

Esta constante define o valor de identificação a ser passado no segundo parâmetro do método startActivityForResult(Intent, int).

No método onItemClick(AdapterView<?> parent, View view, int position, long id) demonstrado na linha 43 da Listagem 1, substituir a chamada da Activity startActivity(Intent) (linha 56) pela linha abaixo:

startActivityForResult(intent,CALL_SECOND_ACTIVITY);




No código da Listagem 1 incluir o método onActivityResult(int, int, Intent), conforme demonstrado abaixo na Listagem 3:

Listagem 3:
   1:      /**
   2:       * Método chamado quando saímos da DetailActivity
   3:       * @param requestCode O código inteiro originalmente fornecido
   4:       * no método startActivityForResult(), permitindo identificar
   5:       * quem fez a chamada.
   6:       * @param resultCode O código inteiro retornado pela Activity
   7:       * filha através do método setResult().
   8:       * @param data Um Intent que pode retornar dados ao chamador.
   9:       */
  10:      @Override
  11:      protected void onActivityResult(int requestCode, 
  12:              int resultCode, Intent data) {
  13:          super.onActivityResult(requestCode, resultCode, data);
  14:          
  15:          // Verifica se é o código de quem fez a chamada
  16:          if (requestCode == CALL_SECOND_ACTIVITY) {
  17:              // Verifica se o retorno foi com sucesso
  18:              if (resultCode ==RESULT_OK) {
  19:                  if (data != null) {
  20:                      Bundle params = data.getExtras();
  21:                      if (params.getBoolean("visitar") == true) {
  22:                           Toast.makeText(this, "Quero visitar " + 
  23:                                   params.getString("estado"), 
  24:                                   Toast.LENGTH_SHORT).show();
  25:                      }
  26:                  }
  27:              }
  28:          }
  29:      }

Este método será chamado quando a Activity “filha” for fechada.
Na linha 16 é verificado se o código da identificação representa o mesmo código passado na chamada.

Na linha 21 através da classe Bundle obtemos a informação se o usuário deseja visitar o Estado e o nome do Estado.

Substituir o código da Listagem 2 pelo da Listagem 4:
Listagem 4:
   1:  public class DetailActivity extends Activity implements OnClickListener {
   2:      TextView textEstado;
   3:      TextView textCapital;
   4:      TextView textArea;
   5:      
   6:      @Override
   7:      protected void onCreate(Bundle savedInstanceState) {
   8:          super.onCreate(savedInstanceState);
   9:          
  10:          // Configura o layout da Activity
  11:          setContentView(R.layout.state_detail);
  12:          
  13:          // Widgets da Activity
  14:          textEstado = (TextView)findViewById(R.id.textEstado);
  15:          textCapital = (TextView)findViewById(R.id.textCapital);
  16:          textArea = (TextView)findViewById(R.id.textArea);
  17:          
  18:          // Carrega os valores passados pela Activity Principal (Main)
  19:          Intent intent = getIntent();
  20:          
  21:          if (intent != null) {
  22:              textEstado.setText(intent.getStringExtra("estado"));
  23:              textCapital.setText(intent.getStringExtra("capital"));
  24:              textArea.setText(String.valueOf(intent.getFloatExtra("area", 0)));            
  25:          }
  26:          
  27:          final RadioButton rbSim = (RadioButton)findViewById(R.id.rbSim);
  28:          final RadioButton rbNao = (RadioButton)findViewById(R.id.rbNao);
  29:          
  30:          rbSim.setOnClickListener(this);
  31:          rbNao.setOnClickListener(this);
  32:      }
  33:   
  34:      @Override
  35:      public void onClick(View v) {
  36:          Intent intent = new Intent();        
  37:          // Verifica qual radiobutton foi selecionado
  38:          if (v.getId() == R.id.rbSim) {            
  39:              intent.putExtra("visitar", true);
  40:              intent.putExtra("estado", textEstado.getText().toString());
  41:              setResult(RESULT_OK, intent);
  42:          }
  43:      }
  44:  }

Nas linhas 27 e 28 foram incluídas as declarações do botões RadioButton.
Nas linhas 30 3 31 foram registrados os eventos OnClickListener(View.OnClickListener) para os botões Sim e Não.

A partir da linha 35 temos o método que trata o botão selecionado.  Através de um Intent, é passado um boolean informando se deseja visitar o Estado e uma String com o nome do Estado.

Na linha 41 é utilizado o método setResult(int, Intent), para poder passar o resultado OK da operação e a Intent com as informações se deseja visitar, e o nome do Estado.

Executando a aplicação com as alterações descritas e selecionando o Estado Rio Grande do Norte com a opção de visitar, quando fechamos a Activity “filha” será apresentada uma mensagem com o nome do Estado que se deseja visitar, como apresentado na Figura 3:

figura3
Atualizado: Como comentado por um leitor do post ficou faltando o xml da Activity dos detalhes do estado, que segue abaixo:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:stretchColumns="2"
  android:padding="5dp"
  >
  
  <TableRow>
      <TextView
          android:layout_column="1" 
          android:text="Estado:" 
          android:padding="3dp"
          android:textSize="24sp"
       />
      <TextView
          android:layout_column="2" 
          android:hint="Nome do Estado..." 
          android:id="@+id/textEstado"
          android:textSize="24sp"
       />       
  </TableRow>
  
  <TableRow>
      <TextView
          android:layout_column="1" 
          android:text="Capital:" 
           android:textSize="24sp"
       />
      <TextView
          android:layout_column="2" 
          android:hint="Nome da Capital..." 
          android:id="@+id/textCapital"
          android:textSize="24sp"
       />       
  </TableRow>
  
  <TableRow>
      <TextView
          android:layout_column="1" 
          android:text="Área:"
           android:textSize="24sp"
       />
      <TextView
          android:layout_column="2" 
          android:hint="Area em Km2..." 
          android:id="@+id/textArea"
           android:textSize="24sp"
       />       
  </TableRow>
  
  <TableRow>
      <TextView
          android:layout_column="1" 
          android:text="Visitar?"
           android:textSize="24sp"
       />
    <RadioGroup
        android:orientation="horizontal"
    >
        <RadioButton
            android:id="@+id/rbSim" 
            android:text="Sim"
            android:layout_height="35dp"
              android:textSize="24sp"
        />
        <RadioButton 
            android:id="@+id/rbNao" 
            android:text="Não"
            android:layout_height="35dp"
              android:textSize="24sp"
        />        
    </RadioGroup>
  </TableRow>
      
</TableLayout>

Configurar o Atributo MaxLength Programaticamente no Android

No Android os atributos utilizados no arquivo xml do layout podem ser programados também no código através de métodos. No caso do atributo android:maxLength a programação via código não é tão direta como para os outros atributos. 
Para demonstrar como configurar o número máximo de texto em um widget EditText criei um arquivo de layout xml com dois campos EditText. No primeiro campo editMax1 foi configurado número máximo de texto como 10 (Listagem 1).
Listagem 1
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    
    <EditText
        android:id="@+id/editMax1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:maxLength="10"
    />
    
    <EditText
        android:id="@+id/editMax2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
    />
</LinearLayout>

Para o segundo campo editMax2 vamos configurar o comprimento máximo do texto programaticamente (Listagem 2).
Listagem 2
public class Main extends Activity {
    private static final int MAX_LENGTH = 10;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        EditText edit2 = (EditText)findViewById(R.id.editMax2);
        
        // Define o número máximo de caracteres do campo 
        // em 10 caracteres programaticamente
        edit2.setFilters(new InputFilter[]
             {new InputFilter.LengthFilter(MAX_LENGTH)});
        
    }
}

No método onCreate(Bundle) foi criada a variável edit2 que representa o segundo campo editMax2 do arquivo xml de layout.
Para configurar o número máximo de texto foi utilizado o método setFilters(InputFilter).
Portanto os dois campos EditText estão limitados em no máximo 10 caracteres, o primeiro via arquivo xml e configuração de atributo e o segundo programaticamente. Executando a aplicação teremos os dois campos exatamente com o mesmo comportamento.

Touch Mode no Android

Neste post vou escrever sobre um assunto que achei bem interessante com relação ao que conhecemos como “foco”. É muito comum em aplicações desktop ter se a preocupação em tratar o posicionamento do “foco” nos controles de uma janela e tratar os eventos relacionados ao “foco”. No Android como existem diversos tipos de aparelhos que permitem uma navegação diferenciada de acordo com o hardware, torna-se necessário conhecer melhor como é o funcionamento do “foco” em aparelhos com tela touch screen, com trackball ou com teclado.

O Android tem uma funcionalidade chamada de Touch Mode. Esta funcionalidade foi criada para diferenciar a forma como um widget é exibido no momento em que é pressionado ou selecionado, seja com um clique através da tela touchscreen ou através do teclado ou um trackball.

Quando um widget é selecionado através da tela touchscreen o Android entre automaticamente em modo Touch Mode e fica neste modo até que uma tecla de navegação ou um trackball seja acionado, fazendo com que o Android saia do modo TouchMode.

Portanto o framework do Android reconhece automaticamente quando um widget é acionado através da tela touchscreen ou através do teclado.

No caso em que o dispositivo esteja em Touch Mode (quando o usuário utiliza a tela touchscreen) os widgets não apresentam o “foco”ou indicação de estarem selecionados. A exceção a esta regra são os widgets que possuem o atributo android:focusableinTouchMode setado como true, como por exemplo o EditText, o ListView quando está configurado para trabalhar com filtro. Neste caso o widget que permite a digitação de texto aparece com foco no local onde o texto vai ser inserido.

Quando um widget é selecionado através da tela touch screen não existe uma dúvida de qual widget foi selecionado, por isso alguns widgets simplesmente respondem ao evento sem dar qualquer indicação na tela, por exemplo button, checkbox, radion button.

No caso do usuário estar utilizando o teclado para navegar ou um trackball, os widgets recebem foco e permanecem selecionados para que fique visível o item selecionado.

Uma situação que causa confusão é quando o usuário seleciona um widget através do teclado, este widget recebe foco, quando o usuário toca a tela, o widget perde o foco e só retorna a ter o foco quando a tecla de navegação é acionada novamente. Isto ocorre porque o dispositivo muda de modo de operação de Touch Mode para não Touch Mode.

Para saber se a aplicação está em modo Touch Mode utilize o método isInTouchMode() da classe View.

View.isInTouchMode()

Quando uma aplicação que possui um ListView e está em modo Touch Mode não é possível obter o item selecionado através do método ListView.getSelectedItemPosition(). Neste caso o método vai retornar INVALID_POSITION. O método só vai retornar a posição do item selecionado se não estiver no modo Touch Mode. Para resolver este problema utilize o click listener da View.
No momento de desenvolver a interface de uma aplicação deve-se levar em consideração a utilização da aplicação e lembrar o padrão de funcionamento do framework. Não desenvolver a aplicação baseando-se em foco e seleção.

Como Receber Dados de um WebService no Android

Neste post vou demonstrar como tratar informações vindas de um webservice desenvolvido em .NET em uma aplicação Android. 

O webservice utilizado nesta aplicação é o mesmo publicado no post Retornando Dados em Json com WebService .NET. Este WebService retorna a relação de estados do Brasil juntamente com as informações de área, capital e abreviação em formato Json.

A aplicação Android vai receber as informações do webservice, tratar os dados e exibir  a relação de estados em um ListView.

A aplicação possui duas classes Principal.java e WebService.java. Na classe WebService.java foi criado o método getEstados() que se conecta ao WebService e retorna os dados em uma String que contêm as informações formatas em Json. (Listagem 1).

Listagem 1:
public class WebService {
    private static final int TIMEOUT_CONEXAO = 20000; // 20 segundos
    private static final int TIMEOUT_SOCKET = 30000; // 30 segundos
    private static final int TAM_MAX_BUFFER = 10240; // 10Kbytes
    private String url;
    
    public WebService(String url) {
        this.url = url;
    }
    
    public String getEstados(){
        String parserbuilder = "";
        
        try{
            HttpParams httpParameters = new BasicHttpParams();
            
            // Configura o timeout da conexão em milisegundos até que a conexão
            // seja estabelecida
            HttpConnectionParams.setConnectionTimeout(httpParameters, 
                    TIMEOUT_CONEXAO);
            
            // Configura o timeout do socket em milisegundos do tempo 
            // que será utilizado para aguardar os dados
            HttpConnectionParams.setSoTimeout(httpParameters, 
                    TIMEOUT_SOCKET);   
            
            HttpClient httpclient = new DefaultHttpClient(httpParameters);
            HttpPost httppost = new HttpPost(url + "/GetEstados");
    
            HttpResponse response = httpclient.execute(httppost);
            
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(response.getEntity().getContent(),
                            "UTF-8"), TAM_MAX_BUFFER);
            
            StringBuilder builder = new StringBuilder();
            
            for (String line = null ; (line = reader.readLine())!= null;) {
                builder.append(line).append("\n");
            }
            
            parserbuilder = builder.toString();
            
            // Retira a string <?xml version="1.0" encoding="utf-8" ?> 
            // <string xmlns="http://tempuri.org/"> e a tag </string> 
            // para obter o resultado em Json, já que o webservice está
            // retornando uma string
            Integer firstTagString = parserbuilder.indexOf("<string");
            Integer posXml = parserbuilder.indexOf(">", firstTagString);
            Integer posTagString = parserbuilder.indexOf("</string>");
            parserbuilder = parserbuilder.substring(posXml + 1, posTagString + 1);
        
        }catch(ClientProtocolException e){
            Log.e("WebService", e.toString());
        }
        catch(IOException e){
            Log.e("WebService", e.toString());
        }
        
        return parserbuilder;    
    }
}
No método getEstados() foi utilizada a classe BasicHttpParams() para configurar os timeouts de conexão, e as classes DefaultHttpClient(HttpParams) e HttpPost(String) para fazer uma requisição tipo Post no WebService.

A informação retornada pelo WebService .NET é um Xml que retorna uma String com dados no formato Json. Para facilitar a obtenção das informações no formato Json, optei em tratar a String retornada pelo WebService de modo a ter somente a informação no formato Json.

Na classe Principal.java no método onCreate(Bundle) foi chamada a classe WebService() e feito o tratamento para exibir os estados no ListView. (Listagem 2)

Listagem 2:
public class Principal extends ListActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        List<String> estados = new ArrayList<String>();
        
        // o endereço http://localhost no emulador deve
        // se chamado como http://10.0.2.2, porque o
        // endereço http://localhost ou http://127.0.0.1
        // é utilizado pelo próprio emulador
        WebService webService = 
            new WebService("http://10.0.2.2:4362/websitejson/service.asmx");
        
        // Obtêm a resposta do webservice
        String resultado = webService.getEstados();
        
        try {
            // Utiliza a classe JSONArray para obter as
            // informações e separar o campo que será
            // utilizado no listview
            JSONArray json = new JSONArray(resultado);
            
            for (int i = 0; i < json.length(); i++) {
                JSONObject jsonObj = json.getJSONObject(i);
                
                // Insere o estado na lista de estados
                estados.add(jsonObj.getString("Nome"));
            }
            
        } catch (JSONException e) {
            Log.e("WebService", e.toString());
        }
        finally{
            setListAdapter(new ArrayAdapter<String>(this, 
                    android.R.layout.simple_list_item_1, estados));
        }
        
    }
}
O webservice está rodando no localhost (127.0.0.1) porta 4362, mas para chamar o webservice no emulador do android não pode ser utilizado o IP 127.0.0.1 porque este IP é utilizado no emulador, por isso na URL utilizar o IP 10.0.2.2.

Para obter somente os estados utilizei as classe JSONArray e JSONObject para extrair a informação desejada. 

Antes de rodar a aplicação não esquecer de dar permissão de acesso à internet no arquivo AndroidManifest.xml.
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
A aplicação rodando vai exibir a seguinte tela:

tela1

A API utilizada HttpDefaultClient é melhor para dispositivos que utilizam a versão 2.2 (Froyo) ou anterior do Android. Para as versões 2.3 (Gingerbread) ou superior é melhor utilizar a API HttpURLConnection. O post Usando HttpURLConnection para Receber Dados no Android demonstra como utilizar esta API.

Como Verificar se o Android está Conectado na Internet

Os dispositivos com sistema Android podem ter acesso à internet através do WiFi ou de uma rede móvel como por exemplo 3G. Em algumas aplicações é interessante verificar se o equipamento está conectado na internet, antes de executar alguma operação que necessita da conexão.

Fazer este tipo de verificação pode ser útil para informar o usuário que o equipamento está sem conexão à internet, antes de executar a tarefa que pode retornar um erro, ou demorar para responder devido à falta de conexão.

Para fazer esta verificação pode-se utilizar a classe ConnectionManager, que através do método getActiveNetworkInfo() permite obter detalhes sobre a rede de dados ativa no momento.

O código abaixo exemplifica uma forma de verificar se o dispositivo está ou não conectado na internet através do método existeConexao() que retorna true se existir algum tipo de conexão WiFi ou Mobile e false se não existir conexão.
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

public class DetectaConexao {
    private Context _context;
    
    public DetectaConexao(Context context){
        this._context = context;
    }
 
    public boolean existeConexao(){
        ConnectivityManager connectivity = (ConnectivityManager)
         _context.getSystemService(Context.CONNECTIVITY_SERVICE);
          if (connectivity != null) 
          {
              NetworkInfo netInfo = connectivity.getActiveNetworkInfo();
              
              // Se não existe nenhum tipo de conexão retorna false
              if (netInfo == null) {
                return false;
              }
              
              int netType = netInfo.getType();

              // Verifica se a conexão é do tipo WiFi ou Mobile e 
              // retorna true se estiver conectado ou false em
              // caso contrário
              if (netType == ConnectivityManager.TYPE_WIFI || 
                    netType == ConnectivityManager.TYPE_MOBILE) {
                  return netInfo.isConnected();

              } else {
                  return false;
              }
          }else{
            return false;
          }
    }
    
}

Para que seja possível verificar o estado da conexão do dispositivo Android, é necessário incluir a solicitação de permissão de acesso. A solicitação de permissão é apresentada ao usuário somente no momento da instalação da aplicação e deve ser inserida durante a programação no arquivo AndroidManifest.xml.
Para o caso de verificar o estado da rede inserir a seguinte tag no arquivo AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Como Exibir uma Notificação no Android

O sistema de notificação do Android é uma maneira de informar os eventos importantes que aconteceram, a interface permite visualizar e interagir com a aplicação que gerou a notificação, no momento que for mais conveniente.

Para criar uma notificação no Android uma opção é usar a classe NotificationCompat.Builder para versões de Android a partir da API 4 (Android 1.6), e a classe Notification.Builder a partir da API 11 (Android 3.0). No exemplo deste post e toda a descrição para criação de notificação foi utilizada a classe Notification.Builder. O projeto foi desenvolvido para exibir notificação nos dispositivos com Android versão 4.1 ou superior.

Como Criar a Notificação

Para criar uma notificação em dispositivos com API 11 ou superior, é necessário instanciar a classe a Notification.Builder, configurar os parâmetros obrigatórios e chamar o método build(), que irá retornar um objeto tipo Notification.

O objeto Notification deve ter no mínimo:
  • Um ícone
  • Um título
  • Um texto para notificação
Apesar das ações executadas serem opcionais, é interessante incluir pelo menos uma ação que deve ser executada quando o usuário selecionar a notificação. Um exemplo de ação bem comum é abrir uma aplicação.

Na classe Notification a ação é definida através da classe PendingIntent, que retorna um objeto Intent e  pode inicializar uma Activity.

A classe Notification permite definir um nível de prioridade no qual a notificação é exibida, comparada com outras notificações, o que garante que as notificações mais importantes sejam exibidas primeiro.

Os nível de notificação possíveis são:
  • Notification.PRIORITY_DEFAULT: Nível padrão de notificação, onde a prioridade não é definida. 
  • Notification.PRIORITY_HIGH: Notificação de alta prioridade utilizada para fazer comunicações importantes, como por exemplo mensagens.
  • Notification.PRIORITY_LOW: Notificação de prioridade baixa, onde o usuário precisa ficar ciente, mas que não apresenta nenhuma urgência.
  • Notification.PRIORITY_MAX: Notificação considerada crítica ou urgente, que depende do fator tempo e deve ser resolvida antes de alguma outra tarefa.
  • Notification.PRIORITY_MIN: Notificação de prioridade mínima que o usuário precisa ficar ciente, mas não apresenta nenhuma urgência, como por exemplo previsão do tempo. As notificações com prioridade mínima não são apresentadas na barra de status, somente são visíveis quando o usuário acessa a bandeja de notificações.

Criando a Notificação

Para demonstrar como exibir uma notificação criei um projeto que avisa quando foi alterado o modo de conexão do dispositivo, de conexão normal para conexão modo avião ou vice versa. No modo avião o dispositivo não tem acesso ao sinal de telefone, rede e rádio.

Quando o modo de conexão é alterado, uma notificação é exibida na barra de status do dispositivo. Esta notificação quando selecionada apresenta uma tela, com um texto explicando como verificar se o dispositivo está em modo avião.

Para este exemplo criei duas classes no projeto da aplicação Android. A classe PrincipalActivity() utilizada para exibir a tela com o texto explicativo do modo de conexão, e a classe IniciarAplicaçao() utilizada para criar a notificação e definir qual a Activity será exibida quando a notificação for selecionada.

Para verificar se o modo de conexão foi alterado utilizei um BroadcastReceiver que foi registrado na ação android.intent.action.AIRPLANE_MODE. Para ver um exemplo completo de como criar e utilizar um BroadcastReceiver acesse o post Utilizando BroadcastReceiver para Iniciar uma Aplicação.

O registro do BroadcastReceiver foi feito no arquivo AndroidManifest.xml. Este código deve ser inserido dentro da tag <application/>.

      <!-- Publicação do Broadcast -->
      <receiver android:name=".IniciarAplicacao">
          <intent-filter>
              <action android:name="android.intent.action.AIRPLANE_MODE" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </receiver>
A classe IniciarAplicacao() que foi registrada no arquivo AndroidManifest.xml estende a classe BroadcastReceiver e sobrescreve o método onReceive(), que é executado no momento em que o modo de conexão do dispositivo é alterado. Este método cria a notificação que será exibida na barra de status do dispositivo.

public class IniciarAplicacao extends BroadcastReceiver {

  public static final int ALTEROU_MODO_CONEXAO = 1;
  @Override
  public void onReceive(Context ctx, Intent i) {

    // Cria a notificação com as informações de
    // ícone, título, prioridade e texto
    Notification.Builder mBuilder =
            new Notification.Builder(ctx)
            .setSmallIcon(R.drawable.ic_notification_romar)
            .setContentTitle("Modo avião")
            .setPriority(Notification.PRIORITY_DEFAULT)
            .setContentText("Foi alterado o modo de conexão do " +
                "celular.");
   
    // Cria o intent que vai ser chamado quando a notificação
    // for selecionada. Neste caso exibe a Activity PrincipalActivity
    Intent resultIntent = new Intent(ctx, PrincipalActivity.class);

    // Cria uma pilha de tarefas para garantir que a navegação a
    // partir da Activity chamada pela notificação, retorne para
    // a tela principal do Android, quando selecionado o botão voltar
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctx);
    stackBuilder.addParentStack(PrincipalActivity.class);
    stackBuilder.addNextIntent(resultIntent);
    PendingIntent resultPendingIntent =
            stackBuilder.getPendingIntent(
                0,
                PendingIntent.FLAG_UPDATE_CURRENT
            );
    mBuilder.setContentIntent(resultPendingIntent);
    NotificationManager mNotificationManager =
        (NotificationManager) ctx.getSystemService
        (Context.NOTIFICATION_SERVICE);
   
    // A constante ALTEROU_MODO_CONEXAO é uma identificação
    // que pode ser utilizada para atualizar a notificação posteriormente
    mNotificationManager.notify(ALTEROU_MODO_CONEXAO, mBuilder.build());
  }
}


Durante a criação da notificação é definido um Intent para que seja exibida uma Activity, com as informações de como verificar se o dispositivo está em modo avião, portanto  foi criada a classe PrincipalActivity(), cujo método onCreate(Bundle) exibe a informação em um TextView.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_principal);
       
        TextView textView = (TextView)findViewById(R.id.textview);
        textView.setText("O modo de conexão do celular foi alterado. " +
        "Se o ícone de conexão for um avião, o celular está " +
            "sem sinal de telefone, rede e/ou rádio.");
    }

Exibindo a Notificação

Depois de instalada a aplicação no dispositivo, feche o aplicativo e nas configurações do dispositivo altere o modo de conexão para modo avião. Quando o dispositivo alterar o modo de conexão, será apresentada na barra de status o ícone da notificação (letra R).

notificacao1

Quando visualizar a notificação na bandeja será exibido o ícone da notificação (letra R), o título e o texto.

notificacao2

Quando a notificação for selecionada será apresentada a tela com as informações de como verificar se o dispositivo está em modo avião.

notificacao3

A notificação está pronta e funcionando.

Emulador para Debug de Aplicação Android

Além do Plugin para Android que pode ser instalado no Eclipse e que possui todas as ferramentas necessárias para o desenvolvimento, debug e testes de aplicações, existem outras ferramentas no mercado para fazer o debug de aplicações Android, e que pode ser utilizado em casos onde o emulador do Plugin é lento ou tem problemas de execução.

Uma opção de emulador freeware para utilização pessoal, é o emulador do Genymotion, que roda sobre uma máquina virtual e permite a criação de vários emuladores para diversas versões de Android. Este emulador pode ser utilizado juntamente com o Plugin de desenvolvimento do Eclipse, só que ao invés de utilizar o Emulador do Plugin, no momento de testar ou debugar a aplicação,  utilizar o Emulador do Genymotion.

A instalação é simples, é só seguir as instruções do site do Emulador que possui basicamente os seguintes passos:
1. Criar uma conta gratuita no site.
2. Fazer o download do instalador do Emulador.
3. Instalar o software.
4. Criar o Emulador selecionando a versão da API do Android e o tipo de dispositivo que o Emulador deve simular.
5. Instalar o Plugin do Genymotion no Eclipse.

Como exemplo foi criado um Emulador para o dispositivo Nexus 5 com API 19, para o Android KitKat 4.4.2. A figura abaixo mostra o Emulador sendo executado.
emulador1
Depois que o emulador estiver instalado e configurado no Eclipse é só rodar a aplicação Android no emulador genymotion configurado. Segue um exemplo da tela de configuração no Eclipse do Emulador a ser utilizado:
emulador2
Segue um exemplo de uma aplicação rodando no Emulador:
emulador3
Este Emulador é uma boa opção para utilizar com o Eclipse em máquinas Windows, porque é uma Emulador rápido e consegue emular o Android versão 4.4.














ART - Novo Runtime para Android

A partir da versão 4.4 (KitKat) do Android, o Google apresentou o novo runtime que será utilizado nos tablets e celulares, o ART (Android Runtime) que irá substituir no futuro o Dalvik, utilizado atualmente nos dispositivos. 
 
Os dispositivos com a versão 4.4 ou superior possuem os dois runtime instalados, o Dalvik e o ART, sendo que o Dalvik continua sendo o runtime padrão. Um dos motivos em manter os dois runtime nos dispositivos, é possibilitar que os desenvolvedores possam começar a utilização e os parceiros possam fornecer um avaliação do runtime. 
 
Apesar de que as aplicações que rodam com Dalvik devem rodar com o ART, mesmo assim existem diferenças entre os runtime, e é preciso ficar atento nestas diferenças para obter o melhor resultado na utilização da aplicação. A tabela a seguir lista algumas das diferenças entre o Dalvik e o ART:

Dalvik
ART
Tipo de Compilação JIT (Just in Time) compilação dinâmica. A compilação ocorre no momento da execução. AOT (Ahead-of-time) . A compilação é feita no momento da instalação da aplicação.
Tempo de Instalação Mais rápido porque compila na execução. Mais lento devido a compilação que é feita no momento da instalação.
Tempo de Inicialização da Aplicação Mais lento devido a compilação e interpretação JIT. Mais rápido devido à compilação AOT.
Espaço de Armazenamento Menor. Maior, com precompilação binária.
Espaço de Memória Maior devido ao cache do código JIT. Menor.

Além das diferenças listadas na tabela, o ART apresenta mais algumas características importantes para os desenvolvedores. 

Os desenvolvedores contam hoje com uma ferramenta de visualização o TraceView, que exibe utilizando uma amostragem das informações, o que está acontecendo durante a execução da aplicação. Esta forma de amostragem faz com que a execução da aplicação fique muito lenta, afetando o desempenho. Com o ART está sendo desenvolvida uma nova forma de acompanhar as informações durante a execução de forma dedicada, de modo que não afete o desempenho da aplicação. 

Além da ferramenta de visualização o ART possui mais mensagens de exceção do que o Dalvik com mais informações, e que podem ser utilizadas no desenvolvimento de aplicações. 

Outra diferença entre o ART e o Dalvik é a área reservada para o armazenamento da pilha (stack) para códigos nativos (C/C++) e código Java. No caso do Dalvik a pilha possui área separada para os códigos nativos e Java e o espaço reservado é de 1MB para código nativo e 32KB para código Java. No caso do ART a área reservada para a pilha (stack) é a mesma tanto para os códigos nativos como para o Java, e o espaço reservado é praticamente o mesmo que para o Dalvik. 

Para escolher qual compilador utilizar em um dispositivo Android com versão 4.4 ou superior acesse SETTINGS - DEVELOPER OPTIONS - SELECT RUNTIME. 

dalvik_art

IMPORTANTE: O runtime Dalvik deve ser escolhido como o padrão nos dispositivos para não correr o risco de danificar as implementações Android ou aplicações de terceiros.

Como Exibir uma Notificação no Android–Parte II

As notificações no android são exibidas normalmente com um título e uma linha de texto, mas existem situações em que é necessário atualizar a notificação, como no caso das notificações que informam a chegada de e-mail. Por exemplo, quando chega mais de um e-mail a notificação pode exibir quantas mensagens chegaram até o momento e o assunto de cada mensagem. 
 
Dando continuidade ao post Como Exibir Notificações no Android - Parte I, este post mostra como exibir notificações, utilizando um estilo grande, onde são exibidas mais de uma linha de informação, e como cancelar programaticamente as notificações sendo exibidas para o usuário. 
 
As notificações em estilo grande somente serão exibidas em dispositivos android versão 4.1 (API 16) ou superior. Para demonstrar como exibir a notificação em estilo grande foi criado um projeto de uma Activity com os botões Criar Notificação, Atualizar Notificação e Fechar Notificação
 
notificacaoexemplo1

Criar a Notificação

O botão Criar Notificação chama o método criaNotificacao() que inicializa a variável mBuilder do tipo Notification.Builder, com as informações de ícone, título e texto, e exibe uma notificação simples através do método notifica(). Este método além de exibir a notificação utilizando o método notify(int, Notification) da classe NotificationManager, também configura o valor da identificação da notificação.
    private void criaNotificacao(){
        
        // Cria a notificação com as informações de
        // ícone, título e texto
        mBuilder =  new Notification.Builder(this)
                .setSmallIcon(R.drawable.ic_notification_romar)
                .setContentTitle("Notificação Estilo Grande")
                .setContentText("Estilo diferente...");

        // Inicializa o número de mensagens
        numMensagens = 0;

        // Exibe a notificação
        notifica();
    }


    private void notifica()
    {
        NotificationManager mNotificationManager =
                (NotificationManager) getSystemService
                (Context.NOTIFICATION_SERVICE);
            
        // A constante NOTIFICACAO_TESTE é uma identificação
        // que pode ser utilizada para atualizar a notificação posteriormente
        mNotificationManager.notify(NOTIFICACAO_TESTE, mBuilder.build());
        
    }
Quando o botão Criar Notificação é clicado a seguinte notificação é exibida no dispositivo: 
 
device-2014-10-01-224914

Até este momento está sendo exibida uma notificação em estilo normal, porque  foi configurado somente a notificação padrão. Para exibir uma notificação em estilo grande optei em inserir várias linhas de mensagens na mesma notificação, simulando a chegada de uma nova mensagem.

Atualizar a Notificação

Para executar esta função foi criado o botão Atualizar Notificação, que tem por tarefa inserir uma linha de mensagem cada vez que o botão for pressionado, este botão chama o método atualizaNotificacao(). Este método configura o título, insere as mensagens em uma lista e configura o número de mensagens que foram inseridas.
    private void atualizaNotificacao()
    {
        Notification.InboxStyle inboxStyle =
                new Notification.InboxStyle();

        // Define o título para a mensagem de estilo grande
        inboxStyle.setBigContentTitle("Exibe várias Mensagens");
        
        // Incrementa o número de mensagens
        numMensagens++;
        
        // Insere a mensagem na lista de mensagens
        listaMensagens.add("Mensagem de notificação " +  String.valueOf(numMensagens));
        
        // Escreve a mensagem 
        for (String mensagem : listaMensagens) {
            inboxStyle.addLine(mensagem);
        }
        
        // Configura o estilo na notificação.
        mBuilder.setStyle(inboxStyle);
        
        // Atualiza o número de notificações sendo exibida
        mBuilder.setNumber(numMensagens);

        // Exibe a notificação
        notifica();
    }

Cada vez que o botão Atualizar Notificação for clicado uma mensagem é inserida na notificação existente, porque estamos atualizando sempre a notificação com a mesma identificação. Se por exemplo clicarmos 5 vezes no botão a notificação fica com a seguinte aparência:

not_varias_msg_1

Observe que a notificação é apresentada com o título, o texto padrão e o número 5 no canto direito, indicando que existem mais mensagens a serem visualizadas. Note que a notificação criada pelo programa está sendo exibida no meio de outras duas notificações. Quando isto ocorre é necessário expandir a notificação, utilizando o movimento de pinça com os dedos, para visualizar todas as mensagens, como exibida na figura abaixo:

not_varias_msg_2

Uma observação importante é que, a notificação só vai ser apresentada em estilo grande expandido se for a primeira notificação da lista de notificações. 

Cancelar a Notificação 
 
A notificação pode ser cancelada programaticamente de várias formas. Podemos configurar a notificação para que seja cancelada no momento em que é clicada durante a visualização, utilizando o método setAutoCancel(boolean) da classe Notification.Builder, que pode ser configurado no momento da criação da notificação. No caso do projeto deste post bastaria utilizar o objeto mBuilder para configurar o método da seguinte forma:
mBuilder.setAutoCancel(true);  
Uma outra forma de cancelar a notificação é utilizar o método cancel(int) da classe NotificationManager. Para demonstrar como utilizar este método foi criado o botão Fechar Notificação que cancela a notificação sendo exibida. O botão chama o método cancelaNotificacao() que cancela a notificação, passando para o método cancel(int) a constante NOTIFICACAO_TESTE que foi criada para identificar a notificação. Este método também apaga a lista de mensagens que foi criada para armazenar cada mensagem gerada.
    private void cancelaNotificacao(){
        
        // Cancela a notificação 
        NotificationManager mNotificationManager =
                (NotificationManager) getSystemService
                (Context.NOTIFICATION_SERVICE);
        
        mNotificationManager.cancel(NOTIFICACAO_TESTE);
        
        // Apaga a lista de mensagens 
        listaMensagens.clear();

    }