the club - megazine · 05 autor: jeferson silva de lima versionamentos de arquivos o...

32
outubro 2014

Upload: vulien

Post on 21-Jan-2019

227 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014

Page 2: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014

Page 3: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 03

19

Índice

Dicas The Club

28

Editorial

04

11

Autor: Hamden Vogel

05

Autor: Jeferson Silva de Lima

Versionamentos de Arquivos o THVCheckVersion – Parte 3

Autor: Luciano Pimenta

Delphi XE5 – Leitura de Arquivos

Android Studio - Parte V

Page 4: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201404

Delphi é marca registrada da Borland International, as demais marcas citadas são registradas

pelos seus respectivos proprietários.

Thiago Montebugnoli - Editor [email protected]

Olá amigo,

Estamos quase no final de mais um ano e o The Club continua como sempre cada vez mais empenhado em auxiliá-los com dicas, truques e novidades neste tão imenso mundo da programação. Ao longo deste perí-odo, tivemos o privilégio de proporcionar assuntos de diversas linguagens de programação, procurando sempre agradar o nosso público alvo que é você, caro leitor! Saliento que nossos consultores e colunistas estão sempre abertos a sugestões para poder melhor satisfazê-los.

Neste mês, nosso consultor técnico Jeferson Silva de Lima escreveu o artigo “Delphi XE5 - Leitura de Arquivos”, abordando diversos tipos, como por exemplo: Imagens, textos e oriundos de Banco de Dados. Esta prática é muito comum em todos os projetos que desenvolvemos, neste caso para dispositivos móveis. Já nosso colaborador Hamden Vogel, escreveu a parte final do artigo “Versionamentos de Arquivos o THVCheckVersion”. Nesta terceira parte teremos como escopo principal a utilização da classe “TLogClass” que implementará o controle de Logs renderizando tudo em um Dbgrid personalizado. Para finalizar, nosso colunista Luciano Pimenta escreveu a quinta parte da série de artigos relacionados ao Android Studio. Recomendo a leitura para quem deseja aprimorar os conhecimentos sobre controle de telas, “ListView” agrupado, “ExpandableView” e o “SearchView”, ambos úteis para desenvolvimento de aplicativos móveis.

Não podemos esquecer de nossa seção clássica de Dicas Delphi.

Nós do The Club desejamos a todos uma boa leitura e um abraço!

Av. Profº Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150

Informações e Suporte: (14) 3732-1529

Internethttp://www.theclub.com.br

Cadastro: [email protected]: [email protected] Informações: [email protected] Cadastro: theclub_cadastro

Skype Suporte: theclub_linha1 theclub_linha2 theclub_linha3

www.twitter.com/theclubbr

Copyright The Club 2013

Diretor TécnicoMarcos César Silva

DiagramaçãoVitor M. Rodrigues

DesignVitor M. Rodrigues

RevisãoDenise Blair

ColunistasHamden Vogel

Jeferson Silva de LimaLuciano Pimenta

Lucas Vieira de OliveiraThiago Cavalheiro Montebugnoli

Impressão e acabamento:GRIL - Gráfica e Editora

Taquarituba-SP - Tel. (14) 3762-1345

ReproduçãoA utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da revista “The Club Megazine” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais.

Editorial

Page 5: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 05

Olá amigos, continuamos com nossa série de artigos Delphi XE5 mais especificamente com a plataforma Android, anteriormente vimos à utilização dos Intents e como customizar nossos aplicativos, já neste artigo vamos tratar um assunto mais comum e muito utilizado no Windows o que não poderia ser deixado de fora no Android que trata-se da leitura de arquivos através do Delphi (Imagens, .TXT e Bancos de dados).

Aplicação

Em nosso exemplo vamos simular a seguinte situação:

Nossos arquivos serão colocados em uma pasta chamada ‘THECLUB’ no Path padrão que utilizamos nosso banco de dados (.\assets\internal\). Junta-mente com os arquivos que vamos trabalhar teremos também um arquivo de configuração chamado ‘CONEXAO.txt’, nele terá a seção responsável por gravar a informação da ‘Pasta’ na qual nossos arquivos devem ficar.

Diante desses fatores iniciais vamos começar com a criação do exemplo:

Adicione um TabControl ao seu projeto e crie 2 TabItens para que possamos separar as funcionalidades em duas etapas. No primeiro ‘Item’ precisaremos de 2 ListView para exibir as informações do banco de dados e de nosso arquivo de Imagem, adicione também um Memo para exibir os dados do arquivo de Texto e 3 Buttons que irão chamar cada arquivo separadamente e por fim adicione 1 Edit e 1 Label para exibirmos o Path atual que estivermos manipulando os dados.

Sua tela deve ficar parecida com a imagem abaixo: Veja a Imagem 1 – Tab 1.

Na imagem acima já posicionamos e renomeamos os componentes de acordo com suas funções (Para que nenhum botão fique oculto em sua tela o ideal é adicionar um ‘ScrollBox’ assim será possível mover a tela para exibir

Delphi XE5 – Leitura de Arquivos

Imagem 1 – Tab 1.

Page 6: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201406

Listagem 2 – DDL Tabela de Pessoas.

Listagem 3 – Uses.

Listagem 4 – Arquivo de Conexão.

Listagem 5 - OnCreate.

os itens mais abaixo).

Será necessário adicionar 1 ‘SQLConnection’, 1 ‘SQLDataSet’ para buscar-mos os dados de nosso banco de dados (THECLUB.db) as configurações serão as mesmas que vimos em artigos anteriores, portanto, vincule seu ‘SQLDataSet’ ao ‘SQLConnection’ e utilize a seguinte consulta SQL:

SELECT * FROM PESSOAS

Listagem 1 – SQL Pessoas.

CREATE TABLE [PESSOAS]( [ID_PESSOA] INTEGER, [NOME] VARCHAR(30));

Crie também um ‘ClientDataSet’ para que possamos carregar o arquivo de imagem (THECLUB.jpg) em memória, portanto, em seu ‘ClientDataSet’ crie um campo do Tipo ‘Blob’ e clique na propriedade ‘CreateDateSet’.

Com as conexões criadas vincule cada componente em um ListView

diferente, ou seja, em seu Listview1 vincule o campo ‘NOME’ ao ‘Item.Text’.

Imagem 2 – BindSource 1.

No ListView2 vincule seu campo ‘Blob’ criado anteriormente ao

‘Item.Bitmap’.

Imagem 3 – BindSource 2.

Para que o campo ‘Item.Bitmap’ seja exibido deve-se alterar a propriedade ‘ItemAppearance’ de seu ‘ListView2’ conforme imagem abaixo:

Imagem 4 – ListView.

Antes de prosseguir adicione todos os arquivos ao seu Deployment ‘THE-CLUB.jpg’, ‘THECLUB.txt’, ‘THECLUB.db’ e ‘CONEXAO.txt’ o local de destino no Android será padrão para todos (.\assets\internal\THECLUB) com exceção do arquivo ‘CONEXAO.txt’ que deve ficar em um local fixo (.\assets\internal). Veja exemplo conforme imagem abaixo:

Imagem 5 – Deployment.

Adicione também as seguintes ‘Uses’ ao seu projeto:

System.IniFiles, System.IOUtils

Nosso arquivo ‘CONEXAO.txt’ irá conter a seguinte seção:

[CONEXAO]PASTA=THECLUB

Por padrão vamos manter a ‘PASTA’ com o nome de ‘THECLUB’ para que possamos manipular os dados nela localizados.

Agora vamos por a mão na massa, no evento OnCreate de seu formulário insira o seguinte comando:

procedure FormCreate(Sender: TObject);

Page 7: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 07

Listagem 6 – BeforeConnect.

Listagem 7 – Botão Ler Arquivo.

Var Arq: TIniFile; S: String;begin Try S:= TPath.GetDocumentsPath + ‘/CONEXAO.txt’; If FileExists(S) then Begin Arq:= TIniFile.Create(S); Pasta:= Arq.ReadString(‘CONEXAO’,’PASTA’,’’); End; Finally Arq.Free; End;

// Por padrão a pasta TheClub é Criada If not DirectoryExists(TPath.GetDocumentsPath + ‘/THECLUB’) Then CreateDir(TPath.GetDocumentsPath + ‘/THECLUB’); Edit1.Text:= TPath.GetDocumentsPath + ‘/’ + Pasta;end;

Veja que armazenamos o valor lido no arquivo em nossa variável ‘Pasta’, ou seja, crie essa variável global do tipo ‘String’ em seguida é criada a pasta ‘THECLUB’ (CreateDir(TPath.GetDocumentsPath + ‘/THECLUB’)) que por padrão vamos utilizar no processo inicial de nosso exemplo.

A conexão com o banco de dados segue os padrões de nossos artigos anteriores a única diferença é que indicamos a ‘Pasta’ onde o arquivo está, veja conforme exemplo abaixo:

procedure ConexãoBeforeConnect(Sender: TObject);begin Conexão.Params.Values[‘Database’] := TPath.Combine(TPath.GetDocumentsPath + ‘/’ + Pasta, ‘THECLUB.db’);End;

Próximo passo é inserir os comandos em cada um dos botões que criamos anteriormente, são eles:

• Ler Arquivo (Fará a leitura do arquivo de Texto);• Imagem (Irá exibir a imagem em um ListView);• Banco de Dados (irá apresentar os dados contidos na tabela de

Pessoas).

No botão ‘Ler Arquivo’ insira o seguinte comando:

procedure Button1Click(Sender: TObject);Var S: String;begin Memo1.Lines.Clear; S:= TPath.GetDocumentsPath + ‘/’ + Pasta + ‘/’ + InputBox(‘Indique o Arquivo’,’Nome’,’’) + ‘.txt’; Memo1.Lines.LoadFromFile(S);end;

Na listagem acima utilizamos a função ‘InputBox’ para indicarmos o nome do arquivo que desejamos ler.

Imagem 6 – InputBox.

Page 8: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201408

Listagem 9 – Botão Banco de dados.

Listagem 8 – Botão Imagem.

Logo abaixo temos o comando para a leitura da imagem:

procedure Button2Click(Sender: TObject);Var S: String; Stream: TFileStream;begin Try S:= TPath.GetDocumentsPath + ‘/’ + Pasta + ‘/’ + InputBox(‘Indique o Arquivo’,’Nome’,’’) + ‘.jpg’; If FileExists(S) Then Begin Stream:= TFileStream.Create(S,fmOpenReadWrite); Stream.Position:= 0;

cdsImagem.Append; cdsImagemIMAGEM.LoadFromStream(Stream); cdsImagem.Post;

cdsImagem.Close; cdsImagem.Open; End; Finally Stream.Free; End;end;

Neste trecho armazenamos a imagem em uma variável do tipo ‘TFileStre-am’ e logo em seguida carregamos a mesma no campo ‘IMAGEM’ de nosso ‘ClientDataSet’.

No botão ’Banco de dados’ vamos apenas abrir o ‘DataSet’ para exibir os dados.

procedure Button3Click(Sender: TObject);begin sdsPessoas.Open;end;

Após finalizarmos essas primeiras configurações a tela deve ficar parecida com a imagem abaixo:

Imagem 7 – Tab 1.

A segunda etapa do exemplo é a manipulação dos arquivos em si, ou seja, vamos movê-los entre pastas e ainda sim realizar a leitura dos mesmos. Na segunda aba de seu TabControl insira os seguintes componentes:

• Edit;• Label;• 2 Buttons.

Poderá ajusta-los conforme imagem abaixo:

Imagem 8 – Tab 2.

Page 9: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 09

Listagem 10 – Botão Criar Pasta.

Listagem 11 – Botão Mover Arquivos.

Listagem 12 – Procedure FileCopy.

O primeiro passo é indicar o nome da nova pasta em seu ‘Edit’ e ao clicar no botão ‘Criar Pasta’ vamos adicionar uma nova pasta na raiz que estamos trabalhando, portanto, insira o seguinte comando no botão:

procedure Button5Click(Sender: TObject);Var S: String;begin Edit2.Enabled:= False; If Edit2.Text <> ‘’ then Begin S:= TPath.GetDocumentsPath + ‘/’ + Edit2.Text; If not DirectoryExists(S) Then Begin CreateDir(S); Pasta:= Edit2.Text; End; End;end;

A criação é simples apenas com o comando ‘CreateDir’ já temos nossa nova pasta, porém, note que ao final do comando temos o seguinte trecho:

• Pasta:= Edit2.Text;

Neste momento estamos indicando que nossos arquivos devem ser buscados em nossa nova pasta, porém, eles ainda não estão gravados neste local, portanto, vamos utilizar o comando do nosso botão ‘Mover Arquivos’, veja listagem abaixo:

procedure Button4Click(Sender: TObject);Var aArquivo, S: String; I: Integer;begin If DirectoryExists(TPath.GetDocumentsPath + ‘/’ + Edit2.Text) And (Edit2.Text <> ‘’) Then Begin FileCopy(TPath.GetDocumentsPath + ‘/THECLUB/

THECLUB.txt’, TPath.GetDocumentsPath + ‘/’ + Pasta +’/THECLUB.txt’); DeleteFile(TPath.GetDocumentsPath + ‘/THECLUB/THECLUB.txt’);

FileCopy(TPath.GetDocumentsPath + ‘/THECLUB/THECLUB.jpg’, TPath.GetDocumentsPath + ‘/’ + Pasta +’/THECLUB.jpg’); DeleteFile(TPath.GetDocumentsPath + ‘/THECLUB/THECLUB.jpg’);

FileCopy(TPath.GetDocumentsPath + ‘/THECLUB/THECLUB.db’, TPath.GetDocumentsPath + ‘/’ + Pasta +’/THECLUB.db’); DeleteFile(TPath.GetDocumentsPath + ‘/THECLUB/THECLUB.db’);

Edit2.Enabled:= True; Altera_Connect(Pasta); End;end;

Neste momento movemos todos os arquivos encontrados na pasta ‘THECLUB’ para a nova pasta indicada em seu ‘Edit’ através do procedimento ‘FileCopy’, abaixo temos a sintaxe do comando:

procedure FileCopy(const FOrigem, FDestino: String);var sStream, dStream: TFileStream;begin sStream := TFileStream.Create(FOrigem, fmOpenRead); Try dStream := TFileStream.Create(FDestino, fmCreate);

Page 10: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201410

Listagem 13 – Procedure Altera Connect.

Try dStream.CopyFrom(sStream, 0); Finally dStream.Free; end; Finally sStream.Free; end;end;

Por fim alteramos a seção de nosso arquivo ‘CONNECT.txt’ através do procedimento ‘Altera_Connect’, veja sintaxe abaixo:

procedure Altera_Connect(Pasta: String);Var Arq: TIniFile; S: String;begin Try S:= TPath.GetDocumentsPath + ‘/CONEXAO.txt’; If FileExists(S) then Begin Arq:= TIniFile.Create(S); Arq.WriteString(‘CONEXAO’,’PASTA’,Pasta); End; Finally

Arq.Free; End;end;

Ao iniciar o aplicativo poderá ver que o Path foi alterado de acordo com sua nova pasta.

Imagem 9 – Path.

Conclusão

Vimos neste artigo um exemplo de como trabalhar com arquivos no Android, movendo e lendo os dados em locais diferentes. Este exemplo pode ser melhorado de acordo com suas necessidades.

Espero que tenham gostado desta dica, até a próxima!

[email protected]

Jeferson Silva de LimaConsultor The Club.

Sobre o autor

Page 11: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 11

Nessa última parte da série de Android (continuaremos com mais artigos sobre o assunto, mas com assuntos específicos), vamos continuar conhecendo controles de telas, com exemplos de ListView agrupado, ExpandableView e SearchView.

ListView agrupado

Temos um controle especifico para isso (ExpandableListView), que veremos mais adiante. Mas quero mostrar aqui, uma maneira diferente para simular um agrupamento de ListView. Crie um novo layout e adicione um ListView.

Vamos criar o layout que será a indicação que temos um novo grupo na lista de itens. Crie um novo layout com o nome de “layout_header_listview.xml”. Adicione o seguinte código:

<RelativeLayout android:layout_height=”wrap_content” android:layout_width=”fill_parent” android:orientation=”vertical” xmlns:android=”http://schemas.android.com/apk/res/android”>

<ImageView android:id=”@+id/thumbnail” android:layout_width=”25dip” android:layout_height=”25dip” android:src=”@drawable/ic_action_group” android:scaleType=”fitXY”/>

<TextView android:id=”@+id/name_

Android Studio – Parte V

group” android:layout_width=”fill_parent” android:layout_height=”wrap_content” android:layout_alignTop=”@+id/thumbnail” android:layout_toRightOf=”@+id/thumbnail” android:text=”Nome do grupo” android:textStyle=”bold” android:textSize=”15dip” android:paddingLeft=”5dp”/></RelativeLayout>

Adicionamos um ImageView e um TextView. A imagem já esta configurada, precisamos apenas mudar o nome do grupo quando estivermos preenchendo o ListView (no adapter). Vamos criar um novo Model, para configurarmos o grupo que desejamos.

Para não fazer o trabalho massante de criar a classe, podemos copiá-la com o mesmo conteúdo e nome diferente. Clique com o botão direito na classe Item e escolha Refactor>Copy. Digite “ItemGroup”. Uma nova classe é criada com o mesmo conteúdo da Item.

Vamos criar mais dois atributos:

private boolean bGrupo;private String sNmGrupo;

Para gerar o get e set, use a mesma técnica mostrada anteriormente. Não esqueça de modificar o construtor para receber os dois novos atributos. Agora a parte mais importante, precisamos criar um novo adapter, é nele que

Page 12: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201412

vamos configurar o grupo.

Você pode fazer a mesma técnica de copiar a classe ListItem, dando o nome de “ListItemGroup”. Não esqueça de modificar a variável data para receber uma lista de ItemGroup, assim como o parâmetro do construtor. No getItemId, também devemos mudar o tipo do objeto (ItemGroup).

Vamos modificar o getView com o seguinte código:

View vi = convertView;

ItemGroup item = data.get(position);

if (item.isbGrupo()){ //é um grupo vi = inflater.inflate(R.layout.layout_header_listview, null);

vi.setOnClickListener(null); vi.setOnLongClickListener(null); vi.setLongClickable(false);

TextView groupName = (TextView) vi.findViewById(R.id.name_group); groupName.setText(item.getsNmGrupo());}else{ //não é grupo vi = inflater.inflate(R.layout.single_item_listview_group, null);

ImageView imagem = (ImageView)vi.findViewById(R.id.thumbnail); TextView name = (TextView)vi.findViewById(R.id.item); TextView description = (TextView)vi.findViewById(R.id.descrption);

name.setText(item.getsNmItem()); description.setText(item.getsDSItem()); imagem.setImageResource(item.getiLogo());}

return vi;

Pegamos o dado e verificamos se é um grupo, assim, chamamos o res-pectivo layout e configuramos o controle para mostrar o nome do grupo. Se não, configuramos normalmente os itens de tela (repliquei o layout do item do ListView).

No código para preencher a lista de itens, precisamos apenas configurar o grupo e seus itens:

else if (mItem.id == “4”){ rootView = inflater.inflate(R.layout.layout_listview_group, container, false); //ListView agrupado ListView lview = (ListView)rootView.findViewById(R.id.listView);

List<ItemGroup> lista = new ArrayList<ItemGroup>(); lista.add(new ItemGroup(1, “”, true, “Controles de tela”, “”, 0)); lista.add(new ItemGroup(2, “ProgressBar”, false, “”, “Controle para mostrar uma barra de progresso”, R.drawable.ic_action_progressbar)); lista.add(new ItemGroup(3, “TextView”, false, “”, “Controle para mostrar um texto”, R.drawable.ic_action_textview)); lista.add(new ItemGroup(4, “ImageView”, false, “”, “Controle para mostrar uma imagem”, R.drawable.ic_action_imageview)); lista.add(new ItemGroup(5, “”, true, “Containers”, “”, 0)); lista.add(new ItemGroup(6, “TabHost”, false, “”, “Controle para mostrar abas”, R.drawable.ic_action_tabhost)); lista.add(new ItemGroup(7, “ListView”, false, “”, “Controle para mostrar dados em forma de lista”, R.drawable.ic_action_listview));

lview.setAdapter(new ListItemGroup(getActivity(), lista));}

Page 13: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 13

Indicamos em cada item, se o mesmo é grupo ou não (terceiro parâmetro). Executa a aplicação e veja o resultado na Figura 1.

Figura 1. Agrupamento com ListView

Para deixar os controles dos itens um pouco para a direita, coloquei o seguinte código na imagem do single_item_listview_group (layout replicado):

android:layout_marginLeft=”20dp”

Esse exemplo é bom para quando você precisa que o grupo fique sempre visível. Talvez não seja a melhor opção, mas valeu a pena aprender. Para que você tenha grupos que possam ser expandidos e recolhidos, veremos como funciona com o ExpandableListView.

ExpandableListView

Esse controle, cria grupos no ListView. Crie um novo layout (“layout_ex-pandable_listview.xml”) e adicione esse controle. Precisamos criar um adapter, que herda de BaseExpandableListAdapter. Crie uma nova classe na pasta ListAdapter com o nome de “ListExpandable”.

Herde de BaseExpandableListAdapter e usando o editor, crie os métodos (semelhante ao que já fizemos). Note que vários métodos foram adicionados. Temos que passar para esse adapter, uma lista com os grupos e uma lista com o nome do grupo e seus itens. Declare as seguintes variáveis:

private Activity activity;private static LayoutInflater inflater = null;private HashMap<String, List<Item>> list;private List<String> listGroup;

Para o grupo, temos uma lista de strings. Para os dados, temos um Hash-Map, que nada mais é do que um conjunto de chave valor. Assim, teremos como chave, o nome do grupo e o valor, serão os itens. Assim, podemos associar (retornar) a lista de grupos com a lista de dados.

Crie o construtor com o seguinte código:

public ListExpandable(Activity activity, HashMap<String, List<Item>> data, List<String> groups) { this.activity = activity; this.list = data; this.listGroup = groups; inflater = (LayoutInflater)activity.getSystemService( Context.LAYOUT_INFLATER_SERVICE);}

Semelhante ao criado em adapters anteriores, única diferença que te-mos duas listas agora. Os métodos que retornam a quantidade do grupo e itens, é de fácil implementação. O getChild, que devemos implementar com o seguinte código:

return list.get(listGroup.get(groupPosition)).get(childPosition);

Passamos o código do grupo e do item para retornar o objeto. Agora, temos dois getView: getGroupView e getChildView, um para cada tipo. Vamos usar o mesmo layout usado no exemplo anterior, para os itens.

Para o grupo, faça uma cópia de layout_header_listview.xml (dei o nome de “layout_header_expandablelistview.xml”) e remova a imagem do grupo. O ExpandableListView possui uma imagem. Para o getGroupView, vamos usar o seguinte código:

HolderGroup holder;

Page 14: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201414

if(convertView == null){ convertView = inflater.inflate( R.layout.layout_header_expandablelistview, null);

holder = new HolderGroup(); convertView.setTag(holder);

holder.name = (TextView) convertView.findViewById(R.id.name_group);}else{ holder = (HolderGroup) convertView.getTag();}

holder.name.setText(listGroup.get(groupPosition));

return convertView;

HolderGroup é apenas uma classe, que contém os controles de tela que vamos preencher (nesse caso, o nome do grupo). Implemente as classes, com o seguinte código:

class HolderGroup { TextView name;}

class HolderItem { ImageView imagem; TextView name; TextView description;}

Voltando ao getGroupView, carregamos o layout do grupo e preenchemos o TextView com o nome do grupo. Semelhante ao que fizemos no exemplo anterior. Para o getChildView, o código é semelhante:

HolderItem holder;Item item = (Item)getChild(groupPosition, childPosition);

if(convertView == null){ convertView = inflater.inflate( R.layout.single_item_listview_group, null); holder = new HolderItem(); convertView.setTag(holder);

holder.name = (TextView) convertView.findViewById(R.id.item); holder.description = (TextView)convertView.findViewById(R.id.descrption); holder.imagem = (ImageView)convertView.findViewById(R.id.thumbnail);}else{ holder = (HolderItem) convertView.getTag();}

holder.name.setText(item.getsNmItem());holder.description.setText(item.getsDSItem());holder.imagem.setImageResource(item.getiLogo());

return convertView;

Nada muito diferente do que já fizemos. Precisamos agora, preencher as listas e passar para o adapter. Volte ao ItemDetailFragment e use o seguinte código:

else if (mItem.id == “5”){ rootView = inflater.inflate(R.layout.layout_expandable_listview, container, false); ExpandableListView lview = (ExpandableListView)rootView. findViewById(R.id.expandableListView);

List<String> group = new ArrayList<String>();

Page 15: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 15

HashMap<String, List<Item>> itens = new HashMap<String, List<Item>>();

group.add(“Controles de tela”); group.add(“Containers”);

List<Item> aux = new ArrayList<Item>(); aux.add(new Item(2, “ProgressBar”, “Controle para mostrar uma barra de progresso”, R.drawable.ic_action_progressbar)); aux.add(new Item(3, “TextView”, “Controle para mostrar um texto”, R.drawable.ic_action_textview)); aux.add(new Item(4, “ImageView”, “Controle para mostrar uma imagem”, R.drawable.ic_action_imageview)); itens.put(group.get(0), aux);

aux = new ArrayList<Item>(); aux.add(new Item(2, “TabHost”, “Controle para mostrar abas”, R.drawable.ic_action_tabhost)); aux.add(new Item(3, “ListView”, “Controle para mostrar dados em forma de lista”, R.drawable.ic_action_listview)); itens.put(group.get(1), aux);

lview.setAdapter(new ListExpandable(getActivity(), itens, group));}

Criamos duas listas, onde preenchemos o nome dos grupos, e depois preenchemos os itens, adicionando o grupo criado. Por fim, chamamos o

ListExpandable, passando as listas por parâmetro. Rode a aplicação e veja o resultado (Figura 2).

Figura 2. Criando grupos com o ExpandableListView

Os grupos abrem recolhidos, basta selecionar o mesmo, que será mos-trado os itens. Existe um ícone para quando o grupo esta recolhido e outro para aberto.

SearchView

Esse controle abre uma busca para usarmos na aplicação. Com ele pode-mos pesquisar em um ListView, por exemplo, ou abrir uma nova activity com resultado de uma pesquisa. Nesse exemplo, vamos pesquisar em um ListView.

Crie um novo layout (“layout_searchview.xml”) e adicione um ListView e um SearchView. Nesse primeiro exemplo, o ListView será preenchido com um array de resource e assim, vamos usar um ArrayAdapter, sem criar um novo.

Abra o arquivo strings.xml na pasta values e digite o seguinte código:

<string-array name=”paises”> <item>Brasil</item> <item>Argentina</item> <item>USA</item> <item>Paraguai</item> <item>Rússia</item> <item>Espanha</item> <item>Portugal</item> <item>França</item></string-array>

No ItemDetailFragment, vamos configurar o SearchView e o ListView, usando o seguinte código:

else if (mItem.id == “6”){ rootView = inflater.inflate(R.layout.layout_searchview,

Page 16: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201416

container, false); final SearchView search = (SearchView)rootView. findViewById(R.id.searchView); ListView lview = (ListView)rootView.findViewById(R.id.listView);

Resources res = getResources(); String[] paises = res.getStringArray(R.array.paises);

final ArrayAdapter<String> adapter = new ArrayAdapter<String>( getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, paises);

lview.setAdapter(adapter);

search.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) {

if (!query.isEmpty()){ search.clearFocus(); adapter.getFilter().filter(query); }

return false; }

@Override public boolean onQueryTextChange(String newText) {

if (newText.isEmpty()){ adapter.getFilter().filter(“”); }

return false; } });}

Como sempre, carregamos o layout e os controles que vamos manipular. Usamos uma variável para carregar o array de resource. Criamos um ArrayA-dapter e configuramos no ListView. O SearchView possui um Listener com dois métodos. O onQueryTextSubmit é executado quando o usuário aperta o botão de pesquisar, assim, vamos realizar a pesquisa.

No adapter, temos um método para pesquisar nos valores do mesmo. getFilter().filter, recebe o valor digitado (query). Implementamos o onqueryTex-tChange, para verificar se o que foi digitado esta vazio, assim, retornamos os dados, passando um valor vazio para o filter.

O onQueryTextChange, será chamado, quando o usuário clicar no X para limpar o que foi digitado ou quando usar a tecla que limpa o valor do SearchView. Execute a aplicação e digite valores para pesquisar no ListView (Figura 3).

Figura 3. Filtrando o ListView com o SearchView

Note que o filtro é feito pelo inicio do texto digitado, não é pesquisado por qualquer letra dos itens do ListView. Para os outros exemplos, criamos um adapter para preencher o ListView, assim, o getFilter, não esta disponível, precisamos implementá-lo.

Vamos adaptar a classe ListItem (se preferir, crie uma nova), para criar esse filtro. Primeiro, devemos implementar Filterable no adapter:

public class ListItem extends BaseAdapter implements Filterable

Vamos criar alguns atributos:

private List<Item> originalData;private List<Item> filteredData;public boolean isFilter = false;

Preencha originalData e filteredData no construtor. Teremos essas duas variáveis para trabalhar com os dados filtrados e os dados “normais”. No getView, precisamos adaptar para saber de onde vamos pegar os dados para preencher os controles de tela. Por isso, temos isFilter, assim podemos adaptar para o seguinte código:

Page 17: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 17

Item itemObject;

if (isFilter) { itemObject = filteredData.get(position);} else { itemObject = data.get(position);}

Se estivermos filtrando, precisamos pegar os dados do filteredData, senão, pegamos do objeto que já trabalhamos. Por fim, precisamos implementar o getFilter, usando o código:

@Overridepublic Filter getFilter(){ return new Filter() { @Override protected FilterResults performFiltering(CharSequence charSequence) { FilterResults results = new FilterResults(); if(charSequence == null || charSequence.length() == 0) { results.values = originalData; results.count = originalData.size(); } else { List<Item> filterResultsData = new ArrayList<Item>(); String name = “”;

for(Item data : originalData) {

name = data.getsNmItem().toUpperCase();

if (name.contains(charSequence.toString().toUpperCase())) { filterResultsData.add(data); } }

results.values = filterResultsData; results.count = filterResultsData.size(); }

return results; }

@Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { filteredData = (List<Item>)filterResults.values; isFilter = (filteredData != originalData); notifyDataSetChanged(); } };}

Verificamos se temos valores digitados para filtrar (charSequence), atri-buindo para as variáveis o valor atual. Senão, percorremos os dados originais (com a variável auxiliar), e verificamos se o valor digitado é compatível com os dados. No publishResults é onde indicamos se estamos filtrando o adapter, comparando as variáveis.

Se for, colocamos em filterResultsData e depois retornamos o mesmo (usando results). Por fim, precisamos indicar no getCount a quantidade de registros que temos (que podem ser filtrados ou não), para isso, use o se-guinte código:

if (isFilter) { return filteredData.size();} else { return data.size();}

Crie um novo layout e adicione um ListView e um SearchView. Vamos pegar o mesmo exemplo que criamos o ListView customizado. Digite o se-guinte código:

else if (mItem.id == “7”){

rootView = inflater.inflate(R.layout.layout_searchview_filter, container, false);

Page 18: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201418

final SearchView search = (SearchView)rootView. findViewById(R.id.searchView); ListView lview = (ListView)rootView.findViewById(R.id.listView);

List<Item> lista = new ArrayList<Item>(); ...

final ListItemFilter adapter = new ListItemFilter(getActivity(), lista); lview.setAdapter(adapter);

search.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) {

if (!query.isEmpty()){ search.clearFocus(); adapter.getFilter().filter(query); } return false; }

@Override public boolean onQueryTextChange(String

newText) {

if (newText.isEmpty()){ adapter.getFilter().filter(“”); } return false; }});}

Note que a implementação do filter, é igual ao exemplo anterior, pois implementamos o getFilter. Experimente acessar getFilter no exemplo anterior, não temos. Execute a aplicação, e pesquisa registros no ListView (Figura 4).

Figura 4. Pesquisando registros no ListView com Filter do Adapter

Conclusões

Vimos nessa série, o básico para o desenvolvimento Android. Conhecemos desde o inicio da ferramenta, até controles comuns em aplicações Android. Aprendemos como customizá-los para que nossas aplicações fiquem o mais profissional possível.

Espero ter passado o conhecimento básico para você se aprofundar na programação Android.

Um grande abraço a todos e até a próxima!

www.lucianopimenta.net

Luciano PimentaLuciano Pimenta (NOVO DOMINIO: www.lucianopimenta.com) é desenvolvedor Delphi/C#

para aplicações Web com ASP.NET, Windows com Win32 e Windows Forms com .NET. Palestrante da 4ª edição da Borland Conference (BorCon) e da 1ª Delphi Conference.

É MVP Embarcadero, grupo de profissionais que ajudam a divulgar o Delphi no mundo. Atualmente é desenvolvedor da SoftDesign fábrica de softwares em Porto Alegre-RS.

Autor de mais de 90 artigos e de mais de 600 vídeos aulas publicadas em revistas e sites especializados, além de treinamentos presenciais e multimídias. É consultor da FP2 Tecnologia (www.fp2.com.br) onde ministra cursos de programação e banco de dados.

Sobre o autor

Page 19: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 19

Caros leitores, chegamos a última parte do artigo sobre este tema do componente de versionamento. Esta parte final tem como objetivo frisar no mais importante desta classe, também abordando novos trechos do código--fonte que ainda não foram ilustrados aqui, como é o principal escopo deste artigo, a classe TLogClass que implementará nosso controle de log, renderizado em uma bela grid (nosso DBGrid conterá no seu evento OnDrawColumnCell as figuras que representarão cada ação possível de THVFolderAction, como ‘Novo’, ‘Removido’, ‘Modificado’, ‘Antigo Nome’, ‘Novo Nome’).

Para que construir esta classe de histórico? Ora, tão importante quanto saber quem manipulou seus arquivos tanto obter um registro do momento da alteração/remoção/criação dos mesmos, em um dado diretório monitorado pela aplicação – portanto como dito antes o que o nosso componente precisa conhecer é apenas o diretório que ele vai monitorar – informado na proprie-dade DirectoryMonitoring, como em “HVCheckVersion1.DirectoryMonitoring := ‘C:\meudiretorio’;”. Partindo daí tudo fica fácil para o projeto, pois tudo já está encapsulado no componente; todos os processos principais relevantes ao funcionamento deste “versionador em série”, bastando o componente “tra-balhar”, persistindo os dados encontrados nas ações de arquivos em arquivos XML (files.xml e agora o log.xml).

A Thread TFolderMonWorker e a comunicação com a API ReadDirec-toryChangesW

Esta nossa thread implementará o “grosso” do nosso projeto, ou seja, todo o processo de detecção de mudanças naquele diretório monitorado pela aplicação será chamado por esta thread, que por sua vez chamará esta API do Windows chamada ReadDirectoryChangesW; segue sua declaração em C++ para recapitulação (parâmetros da função):

BOOL WINAPI ReadDirectoryChangesW( _In_ HANDLE hDirectory, _Out_ LPVOID lpBuffer, _In_ DWORD nBufferLength,

Versionamentos de Arquivos o THVCheckVersion – Parte 3

_In_ BOOL bWatchSubtree, _In_ DWORD dwNotifyFilter, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped, _In_opt_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

Segue abaixo alguns trechos do código-fonte que implementa a chamada desta API e que retorna as informações monitoradas dos arquivos contidos nesse diretório-base de monitoração (lembrando que os subdiretórios também são monitorados):

const NOTIFY_FILTERS: array[TChangeType] of DWORD = ( FILE_NOTIFY_CHANGE_FILE_NAME // ctFileName , FILE_NOTIFY_CHANGE_DIR_NAME // ctDirName , FILE_NOTIFY_CHANGE_ATTRIBUTES // ctAttr , FILE_NOTIFY_CHANGE_SIZE // ctSize , FILE_NOTIFY_CHANGE_LAST_WRITE // ctLastWriteTime , FILE_NOTIFY_CHANGE_LAST_ACCESS // ctLastAccessTime , FILE_NOTIFY_CHANGE_

Page 20: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201420

CREATION // ctCreationTime , FILE_NOTIFY_CHANGE_SECURITY // ctSecurityAttr );

type _FILE_NOTIFY_INFORMATION=packed record NextEntryOffset: DWORD; Action: DWORD; FileNameLength: DWORD; FileName: WideChar; end; FILE_NOTIFY_INFORMATION = _FILE_NOTIFY_INFORMATION; PFILE_NOTIFY_INFORMATION = ^FILE_NOTIFY_INFORMATION;

type TFolderMonWorker=class(TThread) private Owner: THVCheckVersion; FFolder: THandle; FMonFilter: DWord; FFolderItemInfo: THVFolderItemInfo; procedure SetUp; procedure TearDown; procedure DoFolderItemChange; protected procedure Execute; override; public constructor Create(AOwner: THVCheckVersion); reintroduce; end;

procedure TFolderMonWorker.Execute;const cBufSize = 32 * 1024; // 32kvar B: Pointer; vCount: DWord; vOffset: DWord; vFileInfo: PFILE_NOTIFY_INFORMATION;begin GetMem(B, cBufSize); try while not Terminated do begin if Owner=nil then Exit;

if ReadDirectoryChangesW(FFolder , B , cBufSize , Owner.MonitorSubFolders , FMonFilter , @vCount , nil , nil ) and (vCount > 0) then begin if Owner=nil then Exit;

vFileInfo := B; repeat vOffset := vFileInfo.NextEntryOffset;

FFolderItemInfo.Name := WideCharLenToString(@vFileInfo^.FileName, vFileInfo^.FileNameLength); SetLength(FFolderItemInfo.Name, vFileInfo^.FileNameLength div 2); case vFileInfo^.Action of FILE_ACTION_ADDED : FFolderItemInfo.Action := faNew; FILE_ACTION_REMOVED : FFolderItemInfo.Action := faRemoved; FILE_ACTION_MODIFIED : FFolderItemInfo.Action := faModified; FILE_ACTION_RENAMED_OLD_NAME: FFolderItemInfo.Action := faRenamedOld; FILE_ACTION_RENAMED_NEW_NAME: FFolderItemInfo.Action := faRenamedNew; end; FFolderItemInfo.EventDate := Now; Synchronize(DoFolderItemChange);

Page 21: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 21

PByte(vFileInfo) := PByte(DWORD(vFileInfo) + vOffset); until vOffset=0; end; end; finally TearDown; FreeMem(B, cBufSize); end; end;

procedure TFolderMonWorker.SetUp;var i: TChangeType;begin FFolder := CreateFile(PChar(Owner.Folder) , FILE_LIST_DIRECTORY or GENERIC_READ , FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE , nil , OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS

, 0);

FMonFilter := 0; for i := Low(TChangeType) to High(TChangeType) do if i in Owner.MonitoredChanges then FMonFilter := FMonFilter or NOTIFY_FILTERS[i];

end;

procedure TFolderMonWorker.TearDown;begin CloseHandle(FFolder);end;

procedure TFolderMonWorker.DoFolderItemChange;begin if Assigned(Owner) and Assigned(Owner.FOnFolderChange) then Owner.FOnFOlderChange(Owner, FFolderItemInfo);end;

Veja a figura 01.

Figura 01 – Última versão da tela de apresentação do projeto, contendo as grid´s de versionamento e a de histórico, além de exibir as listas de diferenciação de conteúdo – o versionamento propriamente dito, caracter a caracter.

Page 22: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201422

A nossa classe de log – a TLogClass que apresentará a nossa “Timeline”

Timeline, ou linha do tempo, é uma forma de representar em ordem cronológica os eventos descritos nela historicamente, a fim de acompanhar em uma legenda todos esses itens citados nessa duração de tempo. É útil para saber tudo que aconteceu em um dado período.

Nossa classe vai servir de apoio para a persistência deste histórico, ar-quivo por arquivo, de todos os acontecimentos (nossas ações) que surgiram nos diretórios monitorados em um determinado período, simulando a ideia

de timeline descrita acima, e por fim, representaremos de forma gráfica esta ideia de histórico em uma linha do tempo personalizada, bonita e interessante.

O resultado final da implementação desta classe e sua camada de apre-sentação deverá ficar assim:

Veja a figura 03. Segue trechos do fonte desta classe abaixo, representando o log do nosso

componente:

Figura 02 – Outro exemplo de tela, contendo as cores de alteração e adição de conteúdo, ilustrando as alterações de conteúdo para um arquivo versionado em particular.

Figura 03 – Tela contendo a apresentação da grid dos dados oriundos da classe de log do nosso projeto.

Page 23: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 23

TLogClass = class(TComponent) private fUser: string; fFolder: string; fName: string; fAction: string; fDtEventDate: TDateTime; fDtEventDateFilter: TDate; procedure SetUser(const Value: string); procedure SetFolder(const Value: string); procedure SetName(const Value: string); procedure SetAction(const Value: string); procedure SetDtEventDate(const Value: TDateTime); procedure SetDtEventDateFilter(const Value: TDate);{ TLogClass }

constructor TLogClass.Create;begin inherited;end;destructor TLogClass.Destroy;begin inherited;end;procedure TLogClass.SetAction(const Value: string);begin

fAction := Value;end;

procedure TLogClass.SetDtEventDate(const Value: TDateTime);begin fDtEventDate := Value;end;

procedure TLogClass.SetDtEventDateFilter(const Value: TDate);begin fDtEventDateFilter := Value;end;

procedure TLogClass.SetUser(const Value: string);begin fUser := Value;end;

procedure TLogClass.SetFolder(const Value: string);begin fFolder := Value;end;

procedure TLogClass.SetName(const Value: string);begin fName := Value;end;

Figura 04 – Implementação de uma Timeline através do componente de terceiro (shareware) TDBAdvSmoothTimeLine de TMS Software.

Page 24: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201424

Figura 05 – Outro exemplo de implementação de uma Timeline através do componente de terceiro (shareware) TDBAdvSmoothTimeLine de TMS Software.

Figura 06 – Outro modelo de timeline customizado pelo componente TDBAdvSmoothTimeLine de TMS Software.

Figura 07 – Outro modelo de timeline customizado pelo componente TDBAdvSmoothTimeLine de TMS Software.

Page 25: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 25

Implementação da classe TLogClass

Segue abaixo códigos principais da classe TLogClass, onde os dados da mesma são armazenados em um TClientDataSet específico (o clLogDataSet)

procedure THVCheckVersion.GetAllVersionedCommittedFiles(const MySearchBaselineFolder: string);var sr: TSearchRec; searchResult: integer;// datahoraArquivo: TDateTime;begin if tempClientDataSet.Active then tempClientDataSet.EmptyDataSet;

with tempClientDataSet.Aggregates.Add do begin Expression := ‘MAX(MODIFIEDDATE)’; AggregateName := ‘MaxData’; Active := True; end; tempClientDataSet.AggregatesActive := True; tempClientDataSet.IndexFieldNames :=

‘MODIFIEDDATE’; //para ordenar por data try searchResult := FindFirst(GetTempBaselineFullPath + IncludeTrailingPathDelimiter(MySearchBaselineFolder)+’*.*’,faArchive,sr); while searchResult = 0 do begin if sr.Name[1] <> ‘.’ then ( … )procedure THVCheckVersion.RegisterLog;var sExtensao: string; i: integer; bExtensaoExiste: Boolean;begin if Pos(‘~’, ExtractFileExt(fLog.Name)) > 0 then exit;

sExtensao := (ExtractFileExt(fLog.Name)); if (sExtensao = EmptyStr) then Exit;

sExtensao := LowerCase(sExtensao); bExtensaoExiste := False;

Figura 08 – Nossa tela de grid de log (TLogClass) com os métodos de pesquisa (usuário e data de evento).

Page 26: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201426

for i := 0 to length(arExtensoesValidas) do begin if (arExtensoesValidas[i] = sExtensao) then begin bExtensaoExiste := True; Break; end; end;

if (Pos(TmpDir, fLog.Name) > 0) then Exit; if not (bExtensaoExiste) then Exit;( … )

Veja a figura 09.

Desenho do gráfico

Segue abaixo códigos relacionados ao SQL para a geração do gráfico e também de um trecho do código-fonte para criar o efeito “zebrado” em um quickreport:

SQL:SELECTACTION,

COUNT(*) ARQUIVOS,EVENTDATEFROM LOGGROUP BY ACTION, EVENTDATEORDER BY EVENTDATE

Fonte:procedure TqrpEstatistica.QRSubDetail1BeforePrint(Sender: TQRCustomBand; var PrintBand: Boolean);const Colors: array[boolean] of TColor = (clWhite, $20ACCCCC);begin QRSubDetail1.Color := Colors[QRSubDetail1.Color = Colors[False]]; qrlTotal.Caption := FormatCurr(‘R$ ###,##0.00’, frmEstatistica.auxEstatisticaTOTAL.AsFloat);end;

Foi criada uma tabela de um banco DBISAM, mas claramente poderia ser qualquer outro de sua preferência; o intuito aqui é utilizar uma tabela de um banco de dados e extrair estes dados na forma de um gráfico e relatório. Os dados das colunas seguem em anexo:

• USER String 60

Figura 09 – Gráfico (TQRChart) para ilustrar o indicador de arquivos versionados em um determinado período

Page 27: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 27

• FOLDER String 60• NAME String 100• ACTION String 60• EVENTDATE Date

Veja a figura 10.

Conclusão

Versionamento de arquivos sempre será útil para desenvolvedores que necessitam comparar versões, fazer merge, gerar build, entregar uma certa build pro ambiente de produção e que frequentemente acompanham linha a linha determinados arquivos de fonte para saber quem alterou ou acrescen-tou certos trechos deste fonte. O que é trazido aqui é a implementação de um componente que renderiza em uma TStringGrid as cores de linha a linha resultantes das comparações (sempre de dois em dois) de arquivos, muito

Figura 10 – Criação da tabela de log, no utilitário “Database System Utility” do SGBD DBISAM

eficientes (na verdade comparando lista de hashes do que linhas de texto).

Trouxe algumas implementações interessantes relacionadas a camada de apresentação do projeto que dá suporte ao nosso componente THVCheckVer-sion, como manipulação de dados em objetos do tipo TListView, TStringGrid e TDBGrid, como customizações persistentes em um arquivo de preferências chamado Versionador.settings, persistência de histórico e log em arquivos XML, implementação de um objeto do tipo Thread que invoca a API ReadDi-rectoryChangesW do Windows, e finalmente a construção de timelines e gráficos para ilustrar de forma mais interessante os nossos log´s inseridos no XML do nosso componente.

Portanto, fica a dica de como aperfeiçoar e implementar novas ideias e também em novos projetos, construindo novas funcionalidades ao componente THVCheckVersion, encapsulando melhorias e novas funções e procedimentos para novas versões baseadas nesta ideia genial deste componente de versio-namento nascido e amadurecido durante o decorrer do escopo destes três últimos artigos. Bons estudos e bons projetos !

[email protected]

Hamden VogelAnalista de Sistemas pós-graduado em Engenharia de Software pela

UPIS e Programador Delphi com larga experiência desde 2000, tem de-senvolvido e vendido softwares em Delphi para a África e Estados Unidos, além do mercado nacional. Colaborou com dicas e componentes para sites especializados em Delphi. Também desenvolve em outras linguagens como C/C++, ASP, PHP e .NET.

Sobre o autor

Page 28: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014

dicas the club

28

A seção de dicas deste mês iremos dar uma atenção especial ao com-ponente: StringGrid do Delphi! Em se tratando do Stringgrid ensinarei como realizar alinhamentos e explicarei a utilização dos eventos OnGetEditMask, OnGetEditText e OnSetEditText.

Seção StringGrid

1) Alinhamento à Direita, à Esquerda e Centralizado

Nesta dica iremos montar um StringGrid com rotinas para alinhar os valores à Direita, à Esquerda e Centralizado.

private { Private declarations } TempString: array[0..80] of char; Labor: double; Materiais: double; Shipping: double; COGS: double; Price: double; Sales: double; Revenue: double; Income: double; GrowthRate: double; procedure InitGrid; procedure Calculate;public { Public declarations }end;

var Form1: TForm1;

implementation

uses printers;{$R *.DFM}

type THack = class(TStringGrid) end;const Totals: set of 0..10 = [3, 8, 10]; FmtCentered = DT_SingleLine or DT_VCenter or DT_NoClip or DT_Center; FmtLeft = DT_SingleLine or DT_VCenter or DT_NoClip or DT_Left; FmtRight = DT_SingleLine or DT_VCenter or DT_NoClip or DT_Right;procedure TForm1.FormCreate(Sender: TObject);begin GrowthRate := 10.0; Labor := 0.0; Materiais := 0.0; Shipping := 0.0; COGS := 0.0; Sales := 1000.0;

Price := 25.0; Revenue := 0.0; Income := 0.0; InitGrid; Calculate;end;

procedure TForm1.Calculate;const LbrRate = 2.00; MtrlRate = 1.50; ShpRate = 1.00; FmtMoney = ‘%1.0m’; FmtNumber = ‘%1.0n’;var i: integer; OldSales: double;begin OldSales := Sales; for i := 1 to StringGrid1.RowCount - 1 do begin Revenue := Sales * Price; Labor := LbrRate * Sales; Materiais := MtrlRate * Sales; Shipping := ShpRate * Sales; COGS := Labor + Materiais + Shipping; Income := Revenue - COGS; with StringGrid1 do begin Cells[i, 1] := Format(FmtMoney, [Price]); Cells[i, 2] := Format(FmtMoney, [Sales]); Cells[i, 3] := Format(FmtMoney, [Revenue]); Cells[i, 5] := Format(FmtMoney, [Labor]); Cells[i, 6] := Format(FmtMoney, [Materiais]); Cells[i, 7] := Format(FmtMoney, [Shipping]); Cells[i, 8] := Format(FmtMoney, [COGS]); Cells[i, 10] := Format(FmtMoney, [Income]); end; Sales := Sales * GrowthRate; end; Sales := OldSales;end;

procedure TForm1.InitGrid;begin with StringGrid1 do begin ColWidths[0] := 100; Cells[1, 0] := ‘Qtr 1’; Cells[2, 0] := ‘Qtr 2’; Cells[3, 0] := ‘Qtr 3’; Cells[4, 0] := ‘Qtr 4’; Cells[0, 1] := ‘Price’; Cells[0, 2] := ‘Amount Sold’; Cells[0, 3] := ‘Total

Page 29: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 29

Revenue’; Cells[0, 5] := ‘Labor’; Cells[0, 6] := ‘Materiais’; Cells[0, 7] := ‘Shipping’; Cells[0, 8] := ‘Cost Of Goods Sold’; Cells[0, 10] := ‘Income’; end;end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);var ACol: integer absolute Col; ARow: integer absolute Row; Format: integer;begin if ACol = 0 then Exit; with StringGrid1 do begin Canvas.Font := Font; if State = [gdFixed] then Format := FmtCentered else begin // Define centralizado Format := FmtCentered; // Define alinhado a direita Format := FmtRight;

if ARow in Totals then Canvas.Font.Style := [fsBold] else Canvas.Font.Style := []; if State = [gdSelected, gdFocused] then begin Canvas.Font.Color := clHighLightText; Canvas.Brush.Color := clHighLight; end else begin Font.Color := clWindowText; Canvas.Brush.Color := clWindow; end; end; Canvas.FillRect(Rect); Rect.Right := Rect.Right - 3; StrPCopy(@(TempString[0]), StringGrid1.Cells[ACol, ARow]); DrawText(Canvas.Handle, TempString, -1, Rect, Format); end;end;

procedure TForm1.StringGrid1SelectCell(Sender:

Page 30: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro201430

TObject; Col, Row: Longint; var CanSelect: Boolean);begin if (Col = 1) and ((Row = 1) or (Row = 2)) then StringGrid1.Options := StringGrid1.Options + [goEditing] else StringGrid1.Options := StringGrid1.Options - [goEditing];end;

procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Longint; const Value: string);begin if Value <> ‘’ then if StringGrid1.EditorMode = False then begin case ARow of 1: Price := StrToInt(Value); 2: Sales := StrToInt(Value); end; if THack(StringGrid1).InplaceEditor.Modified then Calculate; end;end;

procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Longint; var Value: string);begin case ARow of 1: Value := IntToStr(Trunc(Price)); 2: Value := IntToStr(Trunc(Sales)); end;end;

end.

2-) Para que servem os eventos OnGetEditMask, OnGetEditText e OnSe-tEditText

O evento OnGetEditMask ocorre quando entramos no modo de edição. Neste momento podemos verificar em qual linha/coluna se encontra o cursor e então, se quiser, poderá especificar uma máscara de edição.

Exemplo:

procedure TForm1.StringGrid1GetEditMask(Sender: TObject; ACol, ARow: Integer; var Value:

String);begin if (ARow = 1) and (ACol = 1) then Value := ‘(999) 999-9999;1;_’; // Telefoneend;

O evento OnGetEditText ocorre também quando entramos no modo de edição. Neste momento podemos manipularmos o texto da célula atual (linha/coluna) e então podemos simular algo tal

como uma tabela onde opções podem ser digitadas através de números.

Exemplo:

procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer; var Value: String);begin if (ARow = 1) and (ACol = 2) then begin if StringGrid1.Cells[ACol, ARow] = ‘Ótimo’ then Value := ‘1’ else if StringGrid1.Cells[ACol, ARow] = ‘Regular’ then Value := ‘2’ else if StringGrid1.Cells[ACol, ARow] = ‘Ruim’ then Value := ‘3’; end;end;

O evento evento OnSetEditText ocorre quando saímos do modo de edi-ção. Neste momento podemos manipular a entrada e trocar por um texto equivalente. Normalmente usamos este evento em conjunto com o evento OnGetEditText.

Exemplo:

procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String);begin if (ARow = 1) and (ACol = 2) then begin if Value = ‘1’ then StringGrid1.Cells[ACol, ARow] := ‘Ótimo’ else if Value = ‘2’ then StringGrid1.Cells[ACol, ARow] := ‘Regular’ else if Value = ‘3’ then StringGrid1.Cells[ACol, ARow] := ‘Ruim’ end;end;

Page 31: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014 05

Page 32: The Club - megazine · 05 Autor: Jeferson Silva de Lima Versionamentos de Arquivos o THVCheckVersion – Parte 3 ... para dispositivos móveis. Já nosso colaborador Hamden Vogel,

outubro2014