sábado, 8 de março de 2014

Android - Projeto 51 - Faltam (2ª Parte)

Na postagem passada vimos a criação da janela principal do projeto com uma primeira montagem. Nesta faremos algumas adaptações para concluirmos o projeto.

Camada das Funções

A primeira camada que vamos criar corresponde as funções que necessitamos. No caso deste projeto, funções de tratamento com Datas. Criamos uma nova classe no pacote projeto.livro.faltam chamada UtilData com a seguinte codificação:
public class UtilData {

  private static Date dataAtual = new Date();

  private static String formatarData(final Date data) {
    DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.UK);
    return sdf.format(data);
  }

  private static Date strToDate(final String dataStr) {
    DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.UK);
    try {
      return sdf.parse(dataStr);
    } catch (Exception e) {
      Log.e("Faltam", "Erro na data: " + e.getMessage());
    }
    return null;
  }

  public static Date convDiaMesToDate(final String diaMes) {
    Calendar original = new GregorianCalendar();
    original.setTime(dataAtual);
    int anoAtual = original.get(Calendar.YEAR);
    String montaData = diaMes.substring(0, 2) + "/" + diaMes.substring(2) + "/"
        + anoAtual;
    return strToDate(montaData);
  }

  public static Date convDiaToDate(final String dia) {
    Calendar original = new GregorianCalendar();
    original.setTime(dataAtual);
    int diaAtual = original.get(Calendar.DAY_OF_MONTH);
    int mesAtual = original.get(Calendar.MONTH);
    int anoAtual = original.get(Calendar.YEAR);
    if (diaAtual > Integer.parseInt(dia)) {
      mesAtual++;
      if (mesAtual > 11) {
        mesAtual = 0;
        anoAtual++;
      }
    }
    mesAtual++;
    String montaData = dia + "/"
        + ((mesAtual < 10) ? "0" + mesAtual : mesAtual) + "/" + anoAtual;
    return strToDate(montaData);
  }

  public static String retornarDiferencaComAtual(final Date dataOri) {
    // Data é hoje
    if (formatarData(dataAtual).equals(formatarData(dataOri))) {
      return "É hoje";
    }
    Calendar diaCirc = new GregorianCalendar();
    diaCirc.setTime(dataAtual);
    int dia = 0;
    while (!formatarData(diaCirc.getTime()).equals(formatarData(dataOri))) {
      diaCirc.add(Calendar.DAY_OF_MONTH, 1);
      dia++;
    }
    return dia + (dia > 2 ? " dias" : " dia");
  }
}
Os métodos desta classe são:
  • formatarData(final Date data): converter um objeto do tipo Date para um objeto tipo String no formato DD/MM/AAAA.
  • strToDate(final String dataStr): converter um objeto tipo String no formato DD/MM/AAAA para um objeto tipo Date.
  • convDiaMesToDate(final String diaMes): converter um objeto tipo String no formato DDMM (Dia e Mês) para um objeto tipo Date.
  • convDiaToDate(final String dia): converter um objeto tipo String no formato DD (Dia) para um objeto tipo Date. Se o dia informado for menor que o dia atual o mês será o atual, caso contrário o mês será o seguinte.
  • retornarDiferencaComAtual(final Date dataOri): retornar quantos dias faltam para chegar até o dia atual, recomenda-se que este método seja utilizado somente para períodos de um ano.
Java trata objetos tipo Date com duas classes: Calendar e GregorianCalendar. Se observamos veremos que todos os métodos contidos na própria Date estão depreciados, então usamos basicamente essas duas classes para lidar com esse tipo de objeto. Já as conversões para o tipo String são tratadas pela classe: SimpleDateFormat.

Camada de Dados

Precisamos realizar uma pequena modificação no método toString() da classe Registro, modificá-lo para a seguinte codificação:
  @Override
  public String toString() {
    if (dia.length() == 2) {
      return descricao + ": "
          + UtilData.retornarDiferencaComAtual(UtilData.convDiaToDate(dia));
    } else {
      return descricao + ": "
          + UtilData.retornarDiferencaComAtual(UtilData.convDiaMesToDate(dia));
    }
  }
Agora a diferença de dias já pode ser visualizada sem problemas na lista.

Uma boa prática é separar os métodos de Gravação e Leitura de arquivo e isolá-los em uma nova classe no pacote projeto.livro.faltam chamada UtilArquivo com a seguinte codificação:
public class UtilArquivo {

  protected static void carregarLista(final List<registro> lista,
      final FileInputStream arquivo) throws Exception {
    ObjectInputStream ois = null;
    try {
      ois = new ObjectInputStream(arquivo);
      Registro reg;
      do {
        reg = (Registro) ois.readObject();
        lista.add(reg);
      } while (true);
    } catch (EOFException e) {
    } finally {
      ois.close();
    }
  }

  protected static void gravarLista(final List<registro> lista, 
      final FileOutputStream arquivo) throws Exception {
    ObjectOutputStream oos = null;
    try {
      oos = new ObjectOutputStream(arquivo);
      for (Registro regs : lista) {
        oos.writeObject(regs);
        oos.flush();
      }
    } finally {
      oos.close();
    }
  }
}
São os mesmos métodos que já vimos na postagem anterior. Isolados aqui, foram mais simplificados, ou seja, o método carregarLista() carrega todos os objetos do arquivo para a lista, enquanto que o método gravarLista() grava todos os objetos da lista para o arquivo.

Atividade Principal

Para finalizarmos nosso aplicativo, procederemos mudanças na classe MainActivity:
public class MainActivity extends ListActivity {

  private EditText edDescricao;
  private EditText edDia;
  private final List<registro> lista = new ArrayList<registro>();
  private ListView listaRegs;

  private void eliminarRegistro() {
    for (int i = listaRegs.getCount(); i > -1; i--) {
      if (listaRegs.isItemChecked(i)) {
        lista.remove(i);
      }
    }
    mostrarLista();
  }

  private void guardarRegistro() {
    lista.add(new Registro(edDescricao.getText().toString(), edDia.getText()
        .toString()));
    mostrarLista();
    edDescricao.setText("");
    edDia.setText("");
    edDescricao.requestFocus();
  }

  private void mostrarLista() {
    ArrayAdapter adaptador = new ArrayAdapter(this,
        android.R.layout.simple_list_item_checked, lista);
    listaRegs.setAdapter(adaptador);
  }

  @Override
  protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    edDescricao = (EditText) findViewById(R.id.descricao);
    edDia = (EditText) findViewById(R.id.dia);
    listaRegs = getListView();
    listaRegs.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    findViewById(R.id.guardar).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(final View v) {
        guardarRegistro();
      }
    });
    findViewById(R.id.eliminar).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(final View v) {
        eliminarRegistro();
      }
    });
  }

  @Override
  protected void onStart() {
    super.onStart();
    try {
      UtilArquivo.carregarLista(lista, openFileInput("dias.reg"));
    } catch (Exception e) {
      Log.e("Faltam", "Erro Leitura: " + e.getMessage());
    }
    mostrarLista();
  }

  @Override
  protected void onStop() {
    super.onStop();
    try {
      UtilArquivo.gravarLista(lista,
          openFileOutput("dias.reg", Context.MODE_PRIVATE));
    } catch (Exception e) {
      Log.e("Faltam", "Erro Leitura: " + e.getMessage());
    }
  }
}
Vamos começar pelo método onCreate() que é chamado assim que o aplicativo é iniciado, a lista da tela é associada a um objeto do tipo ListView, sua visão também foi modificada permitindo que o usuário possa selecionar os registros que deseja excluir. Através do ciclo de vida o próximo método chamado é o onStart() que será responsável por carregar a lista do arquivo texto, e esta lista agora só será gravada quando o método onStop() for chamado (assim evitamos I/O desnecessários). O método mostrarLista() procede a atualização da lista na tela (devemos lembrar que estamos trabalhando com dois objetos o ListView e um List que não estão sincronizados).

Finalmente os dois métodos responsáveis pelas ações de Gravar e Eliminar os registros da lista através do pressionamento dos botões pelo usuário. O método guardarRegistro() adiciona um novo registro a lista e o método eliminarRegistro() verifica qual registro da lista foi selecionado pelo usuário e procede sua retirada (observe que o laço anda de trás para frente eliminando os registros, isso é feito pois tudo é posicional).


Pronto, podemos rodar sem problemas nosso projeto e inclusive passá-lo para o celular (não esqueça da diretiva android:installLocation=”auto” que permitirá que a aplicação seja colocada no cartão de memória.


Utilize este projeto para descobrir como:
  • Realizar gravações de objetos em arquivos;
  • Realizar a leitura de objetos em arquivos;
  • Utilizar o objeto tipo Date e suas conversões para o tipo String;
  • Utilizar lista com marcadores tipo Check;
  • Incluir e eliminar dinamicamente registros em listas.
Obrigado e até a próxima
Fernando Anselmo

0 comentários:

Postar um comentário