Java com café

ProgressBar dentro de uma ActionBar - Android

Olá! Hoje vou mostrar como adicionar uma progressbar dentro de uma actionbar. Um bom exemplo de implementação deste recurso é o aplicativo do Gmail, onde você tem um item de menu (usado para atualizar seus e-mails) que quando clicado, é substituído pela progressbar até o procedimento ser concluído e então volta novamente a ser o item.
Para utilizar este recurso, a versão mínima requerida do sdk é a 11. Você também vai precisar dos ícones, que podem variar de acordo com o tema (baixe o pacote neste link). Para o exemplo estarei utilizando estes que são adequados para o tema Holo Light:
res/drawable-mdpi
res/drawable-hdpi
res/drawable-xhdpi

Para começar salve os ícones nas respectivas pastas de acordo com as legendas nas imagens. Agora vamos precisar de dois arquivos xml, um para o item de menu e outro para a progressbar. Para o item, dentro da pasta res/menu, crie um arquivo chamado atualizar.xml com o seguinte conteúdo:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/action_atualizar"
        android:icon="@drawable/ic_menu_refresh"
        android:showAsAction="always"
        android:title="Atualizar"/>
</menu>
E na pasta res/layout o arquivo actionbar_atualizar.xml:
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="30dp"
    android:layout_gravity="right"
    android:paddingRight="5dp" />
Para controlar quando vai aparecer o item ou a progressbar, a estrutura da classe deverá ser parecida com a que segue (odeio colar trechos muito grandes de código no site, mas paciência que vou explicar tudo lá embaixo):
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends Activity {

 private boolean atualizando = false;
 private Menu mOptionsMenu;

 @Override
 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  getActionBar().setDisplayHomeAsUpEnabled(true);

 }

 @Override
 public void onBackPressed() {

  if (atualizando == true) {
   Toast.makeText(this, "Aguarde...", Toast.LENGTH_LONG).show();
  } else {
   super.onBackPressed();
  }

 }

 private void atualizar() {

  atualizando = true;
  setAtualizando(atualizando);

  new Operacao().execute();

 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {

  switch (item.getItemId()) {

  case android.R.id.home:
   onBackPressed();
   break;

  case R.id.action_atualizar:
   atualizar();
   break;

  }

  return true;

 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {

  super.onCreateOptionsMenu(menu);
  mOptionsMenu = menu;
  getMenuInflater().inflate(R.menu.atualizar, menu);
  // atualizar();
  return true;

 }

 public void setAtualizando(boolean atualizando) {

  if (mOptionsMenu == null) {
   return;
  }

  MenuItem atualizarItem = mOptionsMenu.findItem(R.id.action_atualizar);

  if (atualizarItem != null) {

   if (atualizando) {
    atualizarItem.setActionView(R.layout.actionbar_atualizar);
   } else {
    atualizarItem.setActionView(null);
   }

  }

 }

 class Operacao extends AsyncTask<Void, Void, Void> {

  Exception exception = null;

  @Override
  protected Void doInBackground(Void... params) {

   try {

    // Neste bloco vocẽ executa suas operações
    Thread.sleep(2000);

   } catch (Exception e) {
    exception = e;
   }

   return null;

  }

  @Override
  protected void onPostExecute(Void result) {
   super.onPostExecute(result);

   if (exception == null) {
    atualizando = false;
    setAtualizando(atualizando);
   } else {
    Toast.makeText(MainActivity.this,
      "Ocorreu um erro: " + exception.getMessage(),
      Toast.LENGTH_LONG).show();
   }

  }

 }

}
Bom, no começo temos duas variáveis: atualizando e mOptionsMenu. A primeira é simplesmente para identificar se a atualização está ocorrendo ou não em determinado momento. A segunda receberá o menu assim que ele for criado e permitirá a troca entre o item e a progressbar quando necessário.
Em seguida temos os seguintes métodos:
  • onCreate: Não tem nada de especial além do comando getActionBar().setDisplayHomeAsUpEnabled(true), que adiciona o item "voltar" na actionbar
  • onBackPressed: É tratado se a atualização está ocorrendo e não permitirá voltar se for verdade.
  • atualizar: Configura a barra com o setAtualizando e executa a tarefa que executará as operações de atualização. Falo sobre a classe que executa a tarefa logo abaixo.
  • onOptionsItemSelected: Verifica qual item do menu foi clicado e executa a ação correspondente.
  • onCreateOptionsMenu: A variável recebe o menu e caso queira que a Activity já inicie atualizando, basta descomentar o comando atualizar().
  • setAtualizando: É o responsável por mostrar o item ou a progressbar na actionbar
Por fim temos a classe interna Operacao, uma subclasse de AsyncTask que nos permite executar tarefas em background e atualizar a interface sem precisar de threads. Não vou entrar em detalhes sobre este tipo de classe, apenas saiba que o método doInBackground é usado para executar as operações (no exemplo chamei Thread.sleep(2000) apenas pra simular um procedimento rápido) e onPostExecute é executado quando doInBackground finalizar. E temos a variável exception que é usada pelos métodos para descobrir se a operação finalizou com sucesso.

Qualquer dúvida deixe nos comentários!

Android + ksoap2: Recebendo um Array de Objetos

Neste tutorial pretendo mostrar de forma simples como criar um Web Service em Java utilizando o NetBeans e uma aplicação cliente para consumir o serviço no Android. O Web Service vai implementar uma operação que deverá retornar um array de objetos. Na aplicação Android esse array será recebido e utilizado para preencher uma lista de itens (ListView). 

Ferramentas utilizadas: 
  • NetBeans 7.1 + Tomcat 7 (servidor) 
  • Eclipse + ADT - Android Developer Tools v21.0.1-543035 (cliente) 

Criando o Web Service

Não vou entrar em detalhes sobre a criação da aplicação no NetBeans porque já existe um bom tutorial disponível aqui. Após criar o projeto, crie também uma classe para o objeto que será retornado. Para fins de exemplo vou utilizar a seguinte classe:

public class Fornecedor {

    int codigo;
    String razaoSocial;
    String cpfCnpj;

    public Fornecedor() {
    }

    public Fornecedor(int codigo, String razaoSocial, String cpfCnpj) {
        this.codigo = codigo;
        this.razaoSocial = razaoSocial;
        this.cpfCnpj = cpfCnpj;
    }

    public int getCodigo() {
        return codigo;
    }

    public void setCodigo(int codigo) {
        this.codigo = codigo;
    }

    public String getCpfCnpj() {
        return cpfCnpj;
    }

    public void setCpfCnpj(String cpfCnpj) {
        this.cpfCnpj = cpfCnpj;
    }

    public String getRazaoSocial() {
        return razaoSocial;
    }

    public void setRazaoSocial(String razaoSocial) {
        this.razaoSocial = razaoSocial;
    }

    @Override
    public String toString() {
        return getCodigo() + ". " + getRazaoSocial();
    }
}

É importante que sua classe tenha um construtor padrão, sem argumentos. Pode parecer inútil mas o Tomcat não vai implantar o projeto sem ele. O método toString deverá ser sobrescrito na classe, mas isto será útil apenas na aplicação cliente ao preencher a lista, pois será utilizada a mesma classe. 

Seguindo o tutorial que foi sugerido, você deve criar uma operação que não receberá parâmetros e que a saída será do tipo List<Fornecedor>. Para o exemplo vou chamar esta operação de getFornecedores. O código deverá ser parecido com este: 

@WebService(serviceName = "GetFornecedores")
public class GetFornecedores {

    @WebMethod(operationName = "getFornecedores")
    public List<Fornecedor> getFornecedores() {

        List<Fornecedor> listFornecedores = new ArrayList<Fornecedor>();

        listFornecedores.add(new Fornecedor(1, "Fornecedor 1", "0123456789"));
        listFornecedores.add(new Fornecedor(2, "Fornecedor 2", "0123456789"));
        listFornecedores.add(new Fornecedor(3, "Fornecedor 3", "0123456789"));

        return listFornecedores;

    }
    
}

Após implantar e executar o projeto, o arquivo wsdl pode ser acessado através do browser, de acordo com suas configurações:
http://localhost:8080/TestWSApp/GetFornecedores?wsdl
Com o projeto rodando vamos para a segunda parte.


Criando o cliente Android 

Crie um projeto no Eclipse com uma Activity principal e adicione a lib ksoap2 no Build Path. Copie a classe Fornecedor citada anteriormente e cole no pacote do projeto. Então crie uma classe chamada ConexaoWS com o seguinte código:

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

public class ConexaoWS {

 private static final String NAMESPACE = "http://testws.me.org/";
 private static final String METHOD_NAME = "getFornecedores";
 private static final String SOAP_ACTION = "http://testws.me.org/getFornecedores";
 private static final String URL = "http://10.1.0.116:8080/TestWSApp/GetFornecedores?wsdl";

 public Fornecedor[] getFornecedores() throws Exception {

  SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

  SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
  envelope.dotNet = true;
  envelope.setOutputSoapObject(request);

  HttpTransportSE ht = new HttpTransportSE(URL);
  ht.call(SOAP_ACTION, envelope);

  SoapObject response = (SoapObject) envelope.bodyIn;

  Fornecedor[] fornecedores = new Fornecedor[response.getPropertyCount()];

  for (int c = 0; c < fornecedores.length; c++) {

   SoapObject soapObject = (SoapObject) response.getProperty(c);

   Fornecedor f = new Fornecedor();
   f.codigo = Integer.parseInt(soapObject.getProperty(0).toString());
   f.cpfCnpj = soapObject.getProperty(1).toString();   
   f.razaoSocial = soapObject.getProperty(2).toString();
   fornecedores[c] = f;

  }

  return fornecedores;

 }

}

As principais alterações a serem feitas são nas constantes: 

  • NAMESPACE: Pode ser encontrado no arquivo wsdl, mas normalmente é "http://" + pacote + "/" 
  • METHOD_NAME: Mesmo nome dado a operação 
  • SOAP_ACTION: NAMESPACE + METHOD_NAME 
  • URL: Endereço do arquivo wsdl (nos meus testes a conexão foi recusada quando utilizei localhost ou 127.0.0.1, portanto use o IP real da sua máquina

Com isto fica faltando o layout da aplicação e a chamada do método. Para o layout utilize a seguinte definição:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" >

    </ListView>

</RelativeLayout>

E para a classe principal utilize o seguinte código:
public class MainActivity extends ListActivity {

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

  // Operações de rede não podem ser realizadas na thread principal
  new MyAsyncTask().execute();

 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.activity_main, menu);
  return true;
 }

 private class MyAsyncTask extends AsyncTask<Void, Void, Fornecedor[]> {

  boolean erro = false;

  private final ProgressDialog dialog = new ProgressDialog(
    MainActivity.this);

  @Override
  protected void onProgressUpdate(Void... values) {

   this.dialog.setMessage("Aguarde...");
   this.dialog.show();

  }

  @Override
  protected Fornecedor[] doInBackground(Void... params) {

   Fornecedor[] retorno = null;

   try {

    ConexaoWS conexao = new ConexaoWS();
    retorno = conexao.getFornecedores();

   } catch (Exception e) {
    erro = true;
    e.printStackTrace();
   }

   return retorno;

  }

  @Override
  protected void onPostExecute(Fornecedor[] result) {

   if (this.dialog.isShowing()) {
    this.dialog.dismiss();
   }

   if (erro == false) {

    List<Fornecedor> listaFornecedores = Arrays.asList(result);
    ArrayAdapter<Fornecedor> adapter = new ArrayAdapter<Fornecedor>(
      MainActivity.this, android.R.layout.simple_list_item_1,
      listaFornecedores);
    setListAdapter(adapter);

   } else {
    
    Toast.makeText(MainActivity.this,
      "Ocorreu um erro ao acessar o Web Service.",
      Toast.LENGTH_LONG).show();
    
   }

  }

 }

}
Se você é leigo em programação para Android, recomendo que procure entender como funcionam as classes ListActivity e AsyncTask para melhor compreensão do exemplo. O resultado da aplicação será este:  


Espero que tenham gostado. Qualquer dúvida deixe nos comentários.

Os sistemas legados

É muito comum hoje em dia o profissional da área de sistemas passar anos na faculdade estudando linguagens modernas e quando entra no mercado de trabalho se vê diante de tecnologias totalmente obsoletas. Muitas empresas ainda mantém sistemas antigos rodando e pode ser difícil encontrar um lugar para trabalhar com tecnologias novas.



É notável a velocidade em que as coisas caminham no mundo da informática. Uma das "leis" na programação de computadores diz que Qualquer dado programa, quando rodando, é obsoleto. Mas mesmo com todas atualizações, novas versões com mais recursos e facilidades, as empresas ainda mantém seus antigos sistemas rodando por vários anos.

Por que as empresas mantém sistemas legados?

Muitas empresas acreditam que não devem trocar o sistema simplesmente porque ele está funcionando, mas na verdade é importante avaliar as desvantagens de mantê-lo como por exemplo a dificuldade na integração. Desvantagem esta que foi observada após a obrigatoriedade de emitir nota fiscal eletrônica, visto que muitos sistemas não possuem recursos para comunicação via web. Ainda nesse aspecto, com o avanço no desenvolvimento de dispositivos móveis e a alta disponibilidade de serviços de internet, esses sistemas não estão preparados para aproveitar tais recursos. Mesmo que apresentem soluções para contornar estes problemas, o resultado final não é dos mais elegantes.
Outro motivo pelo qual as empresas optam por não trocam o sistema é o alto custo despendido com o desenvolvimento de um novo sistema. Sim, existem soluções prontas a todos custos mas é preciso que a empresa veja isso como um investimento e entenda a importância de ter um sistema que realmente atenda suas necessidades.

Compensa trabalhar com uma tecnologia obsoleta?


O importante é ter conhecimento e não ficar fora do mercado, independente da tecnologia. As empresas tem dificuldade de encontrar profissionais para trabalhar com determinados sistemas e geralmente são vagas com uma boa remuneração. 
É necessário se manter atualizado pois muitos sistemas estão sendo construídos e outros sendo migrados, mas acredito que é preciso saber aproveitar as oportunidades e fazer o que tem que ser feito.