You are on page 1of 32

julho

2013

julho

2013

04

Editorial

05

FastReport no Delphi
Autor: Luciano Pimenta

11

TConexao:

Gerenciando seu projeto em ambientes Multi-Plataforma


parte 1

Autor: Hamden Vogel

19

Android
Trabalhando com o TouchScreen

ndice

Autor: Thiago C. Montebugnoli

27

As principais mudanas Delphi XE4


Autor: Jos Antonio P. M. de Paula
julho

2013

03

Editorial
Caro Leitor!
sempre com muita alegria e satisfao que finalizamos mais uma revista aos senhores. Aproveito tambm para agradecer s pessoas que vem
acessando o contedo de nosso site, portanto a cada dia que passa estamos
adquirindo mais acessos e cadastros. Que continuemos assim!
Para este ms, nosso consultor tcnico Jos Antonio nos trouxe direto
do forno as principais novidades do Delphi XE4 com o intuito de sempre
mant-los bem informados com as ltimas tecnologias lanadas no mercado. Nosso colunista mensal Luciano Pimenta aborda o uso do gerador de
relatrios FastReport, uma boa opo para quem trabalha com o Delphi.
Podemos considerar esta ferramenta como a opo oficial para quem
utiliza as novas verses do Delphi. J nosso colaborador Hamden Voguel
desenvolveu um componente prprio para gerenciar projetos em ambientes
Multi-Plataforma, o TConexao. Nesta primeira parte ele ir nos auxiliar
na gerncia de Projetos utilizando diversos tipos de Banco de Dados. Importante salientar que este componente est disponvel gratuitamente em
nosso site. Eu continuo apresentando recursos do Sistema Android, sendo
que neste ms ensino a trabalhar com o TouchScreen, descrevendo suas
principais classes e mtodos para realizar esta tarefa. Recomendo a leitura
deste artigo para quem est comeando a desenvolver aplicativos e para
quem deseja aprimorar seus conceitos em relao a este artifcio.

Av. Prof Celso Ferreira da Silva, 190


Jd. Europa - Avar - SP - CEP 18.707-150
Informaes e Suporte: (14) 3732-1529

Internet

http://www.theclub.com.br
Cadastro: cadastro@theclub.com.br
Suporte: suporte@theclub.com.br
Informaes: info@theclub.com.br
Skype Cadastro: theclub_cadastro
Skype Suporte: theclub_linha1
theclub_linha2
theclub_linha3

www.twitter.com/theclubbr

Copyright The Club 2013


Diretor Tcnico
Marcos Csar Silva
Diagramao
Vitor M. Rodrigues
Design
Vitor M. Rodrigues
Reviso
Drielly Cristini Patrinhani
Colunistas
Joo Marcos Sakalauska
Jos Antonio P. M. de Paula
Lucas de Oliveira
Luciano Pimenta
Thiago Cavalheiro Montebugnoli
Juninho
Jeferson Silva de Lima
Impresso e acabamento:

GRIL - Grfica e Editora


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

Um forte abrao e at o ms que vem!

Reproduo

Thiago Montebugnoli - Editor Chefe


thiago@theclub.com.br

04

julho

2013

A utilizao, reproduo, apropriao, armazenamento em banco


de dados, sob qualquer forma ou meio, de textos, fotos e outras
criaes intelectuais em cada publicao da revista The Club
Megazine so terminantemente proibidos sem autorizao
escrita dos titulares dos direitos autorais.
Delphi marca registrada da Borland International,
as demais marcas citadas so registradas
pelos seus respectivos proprietrios.

FastReport no
Delphi
F

astReport um gerador de relatrios presente no Delphi desde


a sua verso 4. A partir da verso XE2, passou a ser a ferramenta
de relatrios oficial do Delphi. Com o FastReport podemos criar
poderosos relatrios pra nossas aplicaes Win32 e FireMonkey
com Delphi.

Trabalharemos para criar relatrios simples, usando dois componentes:


o frxReport e o frxDBDataSet. O primeiro o editor de relatrios, onde basta
dar um duplo clique que teremos o ambiente de criao dos relatrios (Figura
2). O segundo faz a ponte, entre os dados e o relatrio.

A ferramenta ainda possui suporte (adquirido separadamente) para .NET,


Mono e Lazarus. Possui ainda uma ferramenta chamada FastCube para anlise
de dados e construo de relatrios e grficos (OLAP), uma ferramenta de
scripts (FastScript) e o FastQueryBuilder, construtor visual de consultas SQL.
Para ver exemplos e outras informaes sobre essas ferramentas, acesse:
www.fast-report.com/pt/. Nesse artigo, vamos conhecer a verso do FastReport
que acompanha o Delphi XE4, aprendendo a criar relatrios simples, agrupados,
formataes e muitos outros exemplos.

Conhecendo a ferramenta
Ao abrir o Delphi e criar um projeto VCL Foms, podemos visualizar a aba
FastReport 4.0 (Figura 1).

Figura 2. Ambiente de desenvolvimento de relatrios (fonte: FastReport Users


Manual)

Baseado na Figura 2, destacamos a seguir as principais janelas e editores.

Figura 1. Aba do Fast Report no Delphi

1. Report designer: local onde vamos adicionar as bandas e os objetos


para o relatrio;
2. Barra de menus;
3. Barra de ferramentas: botes com as opes de carregar relatrios,
criar novos, salvar etc;
4. Barra de ferramentas do objeto: objetos que podem ser adicionados
ao relatrio;
5. Report page tabs: abas para configurao da pgina do relatrio;
6. Report tree: janela onde podemos visualizar as bandas e objetos
inseridos no relatrio;
7. Object Inspector: janela para definio das propriedades dos objetos
do relatrio;
8. Data tree: janela com os objetos de dados do relatrio. Podemos
julho

2013

05

arrastar facilmente um campo para o relatrio;


9. Rgua;
10. Status line: semelhante a uma barra de status, com informaes
sobre o objeto selecionado no relatrio.

Primeiro exemplo
Volte ao Delphi e adicione um frxDBDataSet (um frxReport tambm deve
estar no formulrio). Crie uma conexo com o banco de dados de sua preferencia (usarei no artigo, o Firebird). Usarei no artigo um Data Module para conter
os componentes de conexo com o banco, pois usaremos vrios exemplos.

Adicione os tipos: Page Header, Page Footer e Master Data. O Master Data,
abre um editor, solicitando o respectivo DataSet da aba. Selecione frxDBDataSet1. Existem duas maneiras de adicionar os campos na banda Master Data
para exibir os dados.
Arraste os campos da janela Data tree para o relatrio. Note que aps
inserir os campos, ao passar o mouse, mostrado uma seta, onde podemos
clicar e ser exibido um menu com os campos do DataSet, assim fica fcil
modificar o objeto para outro campo da consulta (Figura 5).

Vincule o frxDBDataSet com o DataSet que retorna os dados da sua consulta. D um duplo clique no frxReport para abrir o editor. Primeiramente, vamos
vincular o frxDBDataSet com o nosso relatrio, acessando o menu Report>Data.
No editor que abrir, escolha o respectivo controle de dados (Figura 3).

Figura 5. Mudando o campo do objeto facilmente

Figura 3. Vinculando o componente de dados com o relatrio

Vamos agora, adicionar as bandas do relatrio. Clique no boto Insert Band


na barra de ferramentas de objetos. Conforme vemos na Figura 4 mostrado
um menu suspenso que todas as opes de banda.

Outra maneira de adicionar os campos no relatrio escolhendo um Text


object na barra de ferramentas e adicionando o mesmo no relatrio. Ser
aberto um editor com alguns botes (Figura 6).

Figura 6. Editor do Text Object

No editor, temos abas onde vamos configurar o formato do campo,


formataes de fonte etc. o primeiro boto da aba Text, acessa outro editor,
onde podemos escolher os campos da consulta, variveis do relatrio, funes,
etc (Figura 7).
Figura 4. Adicionando bandas ao relatrio

06

julho

2013

Formatao de objetos
Como podemos ver no relatrio que criamos, o campo Salary, esta como
texto, sem a devida formatao. Para ajustar isso, abra o relatrio e d um
duplo clique no campo. No editor, acesse a aba Format e configure, conforme
a Figura 10, onde indicamos o tipo de formatao e o separador decimal
(vrgula) do formato.

Figura 7. Editor de expresses do relatrio

Podemos usar o editor do Text Object para apenas adicionar rtulos (textos) no relatrio, onde basta digitar o texto desejado na aba Text. Na barra
de ferramentas temos as funcionalidades necessrias para formatar os textos
digitados nesse objeto.
Note que ao adicionarmos um Text Object ou mesmo adicionar um campo
usando a janela Data tree, existem linhas que nos auxiliam para que os controles fiquem alinhados tanto horizontalmente, como verticalmente (Figura 8).

Figura 8. Alinhando os componentes do relatrio

Feche o relatrio. No Delphi, adicione um boto e adicione o seguinte


cdigo em seu evento OnClick:
frxReport1.ShowReport();

Figura 10. Relatrio de listagem em execuo

Nem precisamos executa o projeto novamente, basta acessar o boto


de preview no prprio ambiente do relatrio para visualizar as modificaes.
E se precisssemos modificar a formatao de um objeto (fonte, cor etc) de
acordo com algum parmetro que existisse, como faramos? bastante simples.
Acesse o objeto que deseja modificar a formatao. D um duplo
clique para acessar o editor e a aba Highlight. Na opo Condition, digite:
<frxDBDataset1.SALARY> > 50000. Marque em Font a opo Bold. O boto Color, indica a cor do texto quando a condio do objeto for verdadeira.
Voc tambm pode usar o boto para acessar o editor de expresses para
montar a mesma manualmente. De acordo com o configurado antes, o campo
Salary do relatrio deve aparecer em vermelho quando o valor desde for maior
que 50.000,00. Veja na Figura 11 nosso exemplo em execuo.

Execute a aplicao e visualize o relatrio em funcionamento (Figura 9).

Figura 11. Formatao condicional do relatrio

Caso voc deseje mudar a formatao de todo a linha, para que no


precise fazer manualmente a configurao de cada objeto, basta selecionar
todos os objetos e acessar a propriedade HighLight>Condition e fazer a mesma
configurao anterior.
Figura 9. Relatrio de listagem em execuo

Outra formatao bastante usada a de indicar a quantidade de pginas e


julho

2013

07

a pgina atual do relatrio. O FastReport possui variveis que retornam essas


informaes e que so fceis de serem utilizadas.
Acesse a aba Variables do Data tree e arraste para o formulrio um Date
e um TotalPages. Para customizar esses variveis, basta dar um duplo clique e
digitar o texto que deseja. Veja na Figura 12 o rodap do relatrio (modifiquei
o mesmo para mostrar mais de uma pgina).

Figura 12. Formatao do rodap do relatrio

Nota: Para adicionar um texto, basta digit-lo no editor, tomando


o cuidado de no remover a varivel.

Arquivos de relatrios
Para voc que estava acostumado com o Rave Reports, vai notar uma
diferena interessante no FastReport. Cada relatrio um arquivo FR3. No
Rave, o arquivo RAV era de projeto e dentro poderamos ter vrios relatrios.
No FastReport, cada FR3, apenas um relatrio. Salve o arquivo e modifique o cdigo do boto para o seguinte cdigo:

Cdigo 02

select EMPLOYEE.first_name,
EMPLOYEE.last_name,
employee.salary, DEPARTMENT.
department
from EMPLOYEE
inner join DEPARTMENT on
DEPARTMENT.dept_no = EMPLOYEE.
dept_no
order by DEPARTMENT.department

Crie um novo frxReport (se desejar, em outro formulrio). Caso seja carregado o relatrio anterior, crie um novo. No esquea de modificar o Data
do relatrio. Caso tenha criado um novo formulrio, atende para o nome do
formulrio no editor de seleo de DataSets (Figura 3).
De um duplo clique no Master Data e vincule com o DbDataSet. Adicione
uma banda Group Header. Um editor aberto para configurarmos a banda do
grupo. Vamos indicar que o agrupamento se dar pelo campo Department.
Podemos ainda indicar uma expresso para o agrupamento.
Nas opes de agrupamento, podemos configurar para que a cada grupo,
seja mostrado em uma nova pgina ou drill-down, onde clicamos sobre o grupo
para mostrar os dados.

Cdigo 01

frxReport1.
LoadFromFile(Listagem.fr3);
frxReport1.ShowReport();

Nota: caso o FastReport no encontre o arquivo, nenhum erro


mostrado, apenas o relatrio no exibe nenhum registro.

Dica: caso voc escolha a opo drill-down ao executar o relatrio,


apenas os grupos sero mostrados. necessrio clicar em cima do mesmo
para exibir os registros.

Arraste a banda para que fique acima da Master Data. Veja na Figura 13
o relatrio agrupado em execuo.

Usamos o mtodo LoadFromFile para carregar o arquivo de relatrio no


frxReport. Isso nos d a facilidade de termos apenas um controle e vrios
relatrios, sendo carregados de acordo com a nossa necessidade.

Agrupamento
Outro exemplo muito usado em relatrios o de agrupamento de dados.
Neste exemplo, vamos fazer a mesma listagem anterior, com a diferena que
vamos retornar todos os empregados, agrupados pelo seu departamento.
Veja o SQL da consulta:

08

julho

2013

Figura 13. Relatrio agrupado

Somatrio

Master/detail

Sempre que usamos agrupamento de dados em um relatrio, precisamos


somar um determinado campo ou contar a quantidade de registros. Vamos
aproveitar esse relatrio para fazer esse exemplo. Adicione uma banda GroupFooter no relatrio.

Outro exemplo muito comum em relatrio e de mostrar os dados master e os details. Exemplo clssico: nota fiscal, os dados referente ao cliente
(comprador) podemos considerar o master, e as informaes dos produtos
da nota, so o detail.

Adicione um Text Object e acesse o boto Insert Aggregate da aba Text.


No editor, vamos configurar um campo que ter seu valor somado, que no
caso Salary. Veja na Figura 14 como ficou a configurao.

Essas duas fontes de dados, precisam ter um relacionamento. Primeiro,


teremos duas consultas separadas, uma para a master e outra para detail.
Nesse exemplo, estou pesquisando os empregados (Employee) e o histrico
de salrios (Salary_History). Precisaremos, portanto de dois frxDBDataSet (um
para cada consulta).
O relacionamento, faremos nos componentes de consulta, o que bastante simples. Adicione um DataSource e faa a ligao com a consulta master.
No componente (ClientDataSet, Query, Table etc) com os dados da consulta
detail, acesse a propriedade MasterSource e escolha o DataSet anterior. Em
MasterFields, acesse o editor e configure o campo de relacionamento (nesse
caso Emp_no).
Crie um novo relatrio e adicione as seguintes bandas: Page Header,
Master Data, Detail Data e Page Footer. Na Master Data voc deve configurar o frxDBDataSet que tem os dados do mster (no exemplo, Employee) e
consequentemente, para o Detail Data, os dados do detail (Salary_History).
Veja na Figura 16 a disposio dos campos nas duas bandas.

Figura 14. Configurando o somatrio do grupo

Faa a formatao no campo, semelhante ao campo Salary. Rode o relatrio


e note que temos o somatrio por grupo. Mas e se quisermos o valor total
no final do relatrio? Basta adicionar uma banda ReportSummary e fazer a
mesma configurao anterior.
Veja na Figura 15 o relatrio com somatrio do grupo e somatrio total.

Figura 16. Relatrio master/detail

Execute o relatrio e veja que os dados esto agrupados de acordo com


o nome do empregado.

Exportao de relatrios
Uma caracterstica interessante que observei no FastReport a quantidade
de opes de tipos de exportaes para os relatrios. Veja na Figura 17 a aba
de exportaes do FastReport.

Figura 15. Somatrios no relatrio

Dica: temos uma opo bem interessante na configurao do somatrio.


Caso precisssemos mostrar um somatrio acumulado por grupos, basta
marcar a opo Running total na janela Insert Agreggate (Figura 14). Assim, a
cada final de grupo, teramos um somatrio acumulado.
Figura 17. Tipos de exportaes do FastReport
julho

2013

09

Para exportar, por exemplo, um relatrio para HMTL, basta adicionar no


formulrio um frxHTMLExport. Acesse a IDE de desenvolvimento e execute
o preview do relatrio. Note que um boto foi adicionado para exportar o
relatrio.
Para cada tipo de exportao, um item de menu ser adicionado no preview
do relatrio (Figura 18).

Figura 19. Editor para envio por e-mail do relatrio


Figura 18. Opes de exportao no preview do relatrio

Na verso comercial do FastReport, diferente da verso que acompanha


o Delphi, existe a opo de envio do relatrio por e-mail. Ao clicar no componente, um editor ser mostrado. Nele, na aba E-mail configuramos para quem
o relatrio ser enviado, assunto, corpo do e-mail e escolhendo o formato
desejado (Figura 19).
Na aba Account, configuramos o e-mail de envio, ou seja, as nossas configuraes de e-mail para o envio do mesmo.

Concluses
Vimos nesse artigo como trabalhar com o FastReport, a nova ferramenta
para gerar relatrios no Delphi. Existem muitas outras possibilidades para
criarmos relatrios profissionais para suas aplicaes Delphi, assim, no prximo artigo veremos como criar grficos, cross-tab, trabalhar com templates
(semelhante herana) etc.
Um grande abrao a todos e at a prxima!

Sobre o autor
Luciano Pimenta
Luciano Pimenta (NOVO DOMINIO: www.lucianopimenta.com) desenvolvedor Delphi/C#
para aplicaes Web com ASP.NET, Windows com Win32 e Windows Forms com .NET. Palestrante
da 4 edio 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 fbrica de softwares em Porto Alegre-RS.
Autor de mais de 90 artigos e de mais de 600 vdeos aulas publicadas em revistas e sites
especializados, alm de treinamentos presenciais e multimdias. consultor da FP2 Tecnologia
(www.fp2.com.br) onde ministra cursos de programao e banco de dados.

www.lucianopimenta.net

10

julho

2013

TConexao: Gerenciando

seu projeto em ambientes MultiPlataforma parte 1

magino que muitos desenvolvedores j experimentaram a situao de avaliar um prottipo em mais de um ambiente, talvez ao
mesmo tempo (homologao em um ambiente em uma mquina
local e entrega em outro ambiente atravs de uma build). Nestes
casos, pode um mesmo Banco de Dados satisfazer tais requisitos
de performance e regras negociais exigidas, executando comumente um
CRUD sem comprometer a rapidez do trfego de dados atualizando de
forma imperceptvel os controles projetados na camada de apresentao do
seu aplicativo at a tudo bem mas e se houver a necessidade negocial
de evolutivamente abstrair a camada de dados ? O seu aplicativo decide
qual base ele ir rodar e sem prejuzo de instabilidade este processo ser
transparente para o usurio final.
Porque no implantar uma forma otimizada de se obter eficientemente
esta caracterstica de multiplataforma? O Framework Itil j dizia: As Empresas
buscam inovar seus processos para se tornarem mais eficientes e competitivas. Inovaes que do certo transformam-se em melhores prticas. Esta
implementao j foi devidamente testada, livre, tem uma boa resposta de
execuo, acessvel a todos e por isso justifica-se em uma boa prtica de
servio. Ambiciosamente podemos entregar este valor em potencial levando
ao aumento dos resultados desejados dos clientes. Em suma, mais do que
funciona.
O cliente tem uma exigncia que crescente; ns devemos estar sempre
preparados para prontamente atend-las; podemos no caso de um entrega
rod-la localmente em um pen-drive ou em seu prprio HD com um baixo volume de dados utilizando um arquivo XML (apenas para verificar basicamente
seu funcionamento e sem muitos requisitos de software), ou execut-la em um
ambiente de desenvolvimento cliente-servidor exigindo todos os requisitos implementados at o momento, simulando realmente sua performance e demais
quesitos (disponibilidade, confiabilidade, sustentabilidade, etc) escalando
qual dataset ele ir acessar e em tempo de execuo! Portanto, imagine um
software ser independente do seu banco de dados - e aqui referido de forma
abstrata valendo-se tanto de um mero arquivo XML (constru um parser para
isso), tanto para um Sistema baseado em Arquivos ou um Sistema Gerenciador
de Banco de Dados: tem para estas trs finalidades - e finalmente o sistema
est independente de tudo: nasce a classe Tconexao.

Ns temos a seguinte misso de fazer todo o meio-de-campo entre a


camada de apresentao e a de negcio: enquanto as telas do cliente fazem as
chamadas para as classes de negcio, elas por sua vez vo se comunicar com a
nossa classe de conexo para chamar o banco de dados desejado, processando a chamada (select, update, etc) e finalmente a retornando (um dataset,
stringlist, etc) - conforme pode ser entendido pela figura abaixo:

Figura 01 - Como as camadas se comunicam com a classe TConexao. Enquanto


que uma camada chama, outra sempre a escuta e resolve.

Esta funcionalidade interessante pela praticidade, como dito anteriormente, pois o aplicativo fica livre de implementaes distintas de acesso aos
dados; apenas a nossa classe de conexes far este trabalho de carregamento e alterao deles toda visualizao produzida pelo carregamento do
componente TClientDataSet ele sempre ser alimentado pela nossa classe
portanto eles sempre andaro juntos um sempre se comunicando com o
outro e consequentemente a base em que ele acessar ser sempre desligada, esperando algum evento do usurio ou sistema, como por exemplo de
comitar um update ou carregar uma lista (para inicializao de uma tela
ou um refresh).
Abaixo pode ser visualizado o diagrama de sequncia do componente, a
fim de exemplificar o seu funcionamento na prtica; no complicado perceber
sua idia bsica de funcionamento.

Figura 02 - Diagrama de Sequncia do componente TConexao.


julho

2013

11

Agora, finalmente, o processo completo de funcionamento de acordo com


a orientao a objetos juntamente com a nossa classe:

nem tampouco as camadas de negcio (nosso exemplo o TUsuario) acessem


quaisquer parmetros de banco de dados ou sequer se comuniquem com ele:
tudo isso dever EXCLUSIVO da classe TConexao.
Portanto, basicamente o processo de criao para um novo banco finaliza-se aqui; no difcil estend-lo.

Figura 03 - Explicao da operacionalizao do funcionamento das camadas


juntamente com as regras negociais e com a classe TConexao.

Primeiros Passos com a classe TConexao:


O primeiro passo a seguir criar um data-mdulo contendo a conexo
com o banco; pelo menos nesta verso o nosso projeto no tem suporte a
metadados; pretendo em uma prxima verso automatizar a criao e deteco
de banco de dados em tempo de execuo, onde os bancos so reconhecidos
e listados mas apenas com um TDataModule ele ser criado e destrudo a
cada alterao de solicitao de bases mas isso tema para outra release
deste componente.
Por agora, devemos estar atentos de que nenhuma conexo dever estar
pr-configurada porque isso o dever da classe de conexo realizar e tampouco este data-mdulo recm-criado dever estar associado ao projeto - ele
deve ser removido dele. Lembre-se sempre de que a aplicao (referindo-se
s telas da camada de apresentao e s classes negociais) no conhece o
banco de dados a ser chamado - a classe cria o data-mdulo dinamicamente,
executa as operaes desejadas retornando ou no o seu processamento
e aps sua utilizao ela destruda: e como ficam os acessos data-aware,
que eram utilizados para acessar diretamente um banco de dados qualquer,
editando informaes at mesmo de dentro de uma clula de um TDBGrid e
consequentemente gravando e comitando a transao tudo de uma vez
s? A resposta agora o TClientDataSet - ele vai representar em memria
nosso DataSet e exibir com qualquer componente data-aware desejado sem impactar diretamente uma transao com o banco de dados - qualquer
transao ser feita agora pela classe TConexao, pois atravs dela solicitar as
transaes com o banco de dados, cuidando totalmente de sua persistncia.
Continuando, o segundo passo acrescentar neste data-mdulo um
ClientDataSet para cada classe que ser utilizada e persistida na aplicao:
como a classe de conexo e as demais utilizam a abordagem orientada a
objetos, cada ator no sistema ser realmente um objeto negocial; no nosso
exemplo existe um ator usurio, portanto foi criada uma classe TUsuario para
ele, e assim por diante.
O terceiro e ltimo passo so os mtodos (personalizados) que devero
ser criados para manipulao total de seu novo acesso ao banco de dados:
devero ser implementadas chamadas de configurao, dlls necessrias (que
devero ser levadas juntas ao executvel e mais o client do banco, se for o caso)
e demais arquivos necessrios para o seu gerenciamento, agora encapsulado
pela nossa classe TConexao.
Algumas recomendaes:

No utilizar arquivos arquivos de conexo. Fazer diretamente de
dentro da classe;

No utilizar ODBC: Fazer diretamente de dentro da classe;

No permitir que a aplicao (telas da camada de apresentao) e

12

julho

2013

O importante sempre criar um banco de dados primeiro e depois


suas implementaes, sempre estendendo a classe TConexao, criando uma
nova subclasse que ir sobreescrever os mtodos BancoExiste, FecharBanco,
CarregarBanco, RecordCount, RetornaDataSet e ObterDataSet cruciais para
operaes bsicas de funcionamento consulta, incluso, alterao e excluso
alm de outros mtodos descendentes de TDataSet (recordcount).
A seguir, um trecho do cdigo-fonte do mtodo CarregarBanco de uma
conexo para o Firebird:

Cdigo 01

function TConexaoFirebird.
CarregarBanco(const cDiretorio:
string): Boolean;
begin
Result := True;
if (cDiretorio = EmptyStr)
then
begin
fVendorLib
:=
FMyGDSVendorLib;
fLibraryName
:=
FMyDBXExpress
FirebirdLibraryName;
fPathDataBase
:=
FUsuariosFDB;
end
else if (cDiretorio =
default) then
begin
fVendorLib
:=
FMyGDSVendorLib;
fLibraryName
:=
ExtractFilePath(Application.
ExeName) +
IncludeTrailingPathDelimiter
(BD\FIREBIRD) +
FMyDBXExpressFire
birdLibraryName;
fPathDataBase
:=
ExtractFilePath
(Application.ExeName) +
IncludeTrailingPathDelimiter
(BD\FIREBIRD) + fPathDataBase;
end
else
begin
fVendorLib
:=
cDiretorio + FMyGDSVendorLib;

fLibraryName
:=
cDiretorio + FMyDBXExpress
FirebirdLibraryName;
fPathDataBase
:=
cDiretorio + fPathDataBase;
end;
Result := ( (DirectoryExists
(fPathDataBase)) and
(FileExists (fLibraryName)) );
if not Result then Exit;
dmFirebird := TdmFirebird.
Create(nil);
try
try
with dmFirebird.
SQLConnection do
begin
Connected
:= False;
VendorLib
:=
dbVendorLib;
LibraryName :=
dbLibraryName;
Params.
Values[Database] :=
fPathDataBase + FUsuariosFDB;
end;
finally
dmFirebird.SQLConnection.
Connected := True;
dmFirebird.SQLQuery.Open;
end;
except
on E: Exception do
begin
Raise EInvalidDataBase.
Create (EErroGeralBancoDeDados
+ #13#10 + E.Message);
Result := False;
end;
end;
end;

Nossa classe tem 3 bancos pr-definidos e configurados que j podem


ser prontamente utilizados em qualquer aplicao, dando incio a um desenvolvimento de cdigo mais facilitado graas a esta abstrao de conexes,
porque no ser mais necessrio criar outras conexes para o mesmo banco
em diferentes forms ou um criar um data-mdulo e associ-lo aplicao, pois
como isso tudo j foi feito, basta apenas chamar a classe de negcio (no nosso
caso de exemplo, a TUsuario).
So os Bancos:

XML
DBISAM
FIREBIRD

Segue tambm para distribuio o DDL da tabela:

CREATE TABLE USUARIOS (


CD_USUARIO
INTEGER,
NOME
VARCHAR(200),
DATANASCIMENTO DATE,
ENDERECO
VARCHAR(200),
CIDADE
VARCHAR(10),
ESTADO
VARCHAR(3),
CEP
VARCHAR(20),
CARGO
VARCHAR(200),
SETOR
VARCHAR(200),
TELEFONE
VARCHAR(20),
FOTO
BLOB SUB_
TYPE 0 SEGMENT SIZE 500,
VLR_SALARIO
FLOAT,
NR_MATRICULA
INTEGER
);

No nosso projeto foram criados campos dos tipos String, Integer, Float e
Blob. Todos os tipos sero validados de acordo com o formato suportado pelo
banco isto nem todos possuem as mesmas validaes para data, ponto
flutuante, etc. Um ponto importante a ser mencionado que todos os setters
so do tipo String isso facilita os testes de validao de tipo de dados antes
de sua converso para o seu formato original.
Alguns campos so obrigatrios, requeridos nos setters e retornando um
campos de mensagens de retorno (FCodigoMensagem e FMensagem).
Uma amostra do fonte da propriedade da data de nascimento (mtodo
write):
Cdigo 02

procedure TUsuario.
setDataNascimento(const Value:
String); //obrigatrio
var
AuxDia, AuxMes, AuxAno,
fValue: string;
begin
fValue := Value;
fValue := Conexao.
GetDateFormat(Value);
if not (Biblioteca.
EmptyDate(fValue)) then
julho

2013

13

begin
if not Biblioteca.
IsValidDate(fValue) then
begin
FCodigoMensagem
:=
CodMensagemCampoInvalido;
FMensagem
:=
Campo Data Nascimento
Invlido!;
FDataNascimento
:=
;
end
else
begin
FCodigoMensagem
:=
CodMensagemNormal;
FMensagem
:=
EmptyStr;
FDataNascimento
:= Conexao.
FormatDefaultDate(fValue); //
Trim(Value);
end;
end
else
begin
FDataNascimento
:=
fValue;
FCodigoMensagem
:=
CodMensagemCampoEmBranco;
FMensagem
:=
Campo Data Nascimento em
Branco!;
Alterou
:=
False;
end;
end;

, CIDADE = +
QuotedStr(Cidade) + , ESTADO =
+ QuotedStr(Estado) +
, CEP = + QuotedStr(CEP)
+ , CARGO = +
QuotedStr(Cargo) + , SETOR =
+ QuotedStr(Setor) +
, TELEFONE = +
QuotedStr(Telefone) + ,
VLR_SALARIO = + (Conexao.
FormatFloat(Salario)) +
, NR_MATRICULA
= + (Conexao.
FormatInteger(Matricula)) +
WHERE NOME = +
QuotedStr(Nome);
Conexao.Executar(Query);
end
Explicao: A classe TConexao, atravs de GetConexao, e utilizando o
type tBDConexao (type tBDConexao = (bdConexaoXML, bdConexaoDBISAM,
bdConexaoFirebird) far a instanciao com a subclasse desejada de acesso
ao banco, e a melhor de todas as coisas que em runtime os bancos podem
ser trocados selecionando, alterando, etc com a maior versatilidade!
Em suma, toda vez que uma classe quiser acessar um banco, ela na verdade
acessa uma instncia da classe de conexo (TConexao), onde esta por sua vez
referenciar-lo em memria. Toda a interface de comunicao e operacionalizao ser realizada somente atravs dela, estando disponvel at ser destruda.
Abaixo, uma seleo de alguns mtodos importantes utilizados na classe
TConexao - detalhe para os mtodos ObterDataSet, CarregarFoto e GetConexao (sintaxe: Conexao := Conexao.GetConexao(fbdConexao)) - que so os
mtodos mais importantes do processo de comunicao entre os objetos de
negcio e dados.
Cdigo 04

Agora, um exemplo do cdigo-fonte padro para verificar se existe um


registro e em caso positivo atualiz-lo:
Cdigo 03

Query := SELECT * FROM


USUARIOS WHERE NOME = +
QuotedStr(Nome);
if Conexao.RecordCount(Query)
> 0 then //atualiza
begin
Query := UPDATE
USUARIOS SET ENDERECO =
+ QuotedStr(Endereco)
+ , DATANASCIMENTO =
+ QuotedStr(Conexao.
FormatDate(DataNascimento)) +

14

julho

2013

procedure TConexao.
ObterDataSet(const myDataSet:
TDataSet; var myClientDataSet:
TClientDataSet);
var
fDataSet: TDataSet;
i {, k}: integer;
FName: string;
begin
if not Assigned(myDataSet)
then Exit;
if not Self.BancoInstanciado
then
begin
Application.MessageBox(PCha
r(Format(EBancoNaoInstanciado,
[FUsuariosFDB])), ETituloErro,
MB_OK + MB_ICONERROR);

Abort;
end;
fDataSet :=
TClientDataSet(myDataSet);
fDataSet.First;
if not myClientDataSet.Active
then
myClientDataSet.
CreateDataSet
else
myClientDataSet.
EmptyDataSet;
myClientDataSet.
DisableControls;
try
while not fDataSet.Eof do
begin
myClientDataSet.Insert;
for i := 0 to fDataSet.
FieldCount - 1 do
begin
// myClientDataSet.
Fields[i].Assign(fDataSet.
Fields[i]);
myClientDataSet.
FieldDefs.Items[i].DataType :=
(fDataSet.FieldDefs.Items[i].
DataType);
myClientDataSet.
FieldDefs.Items[i].DisplayName
:= (fDataSet.FieldDefs.
Items[i].DisplayName);
case myClientDataSet.
FieldDefs.Items[i].DataType of
ftString:
myClientDataSet.Fields[i].
AsString := Self.
DefaultDateFormat2(fDataSet.
Fields[i].AsString);
ftDate:
myClientDataSet.Fields[i].
AsString := Self.
DefaultDateFormat2(fDataSet.
Fields[i].AsString);
ftFloat:
myClientDataSet.Fields[i].
AsFloat := Biblioteca.
ArredondaComDecimais( fDataSet.
Fields[i].AsFloat, 2);
else
myClientDataSet.
Fields[i].AsString :=
fDataSet.Fields[i].AsString;
end;

end;

myClientDataSet.Post;
fDataSet.Next;
end;
finally
myClientDataSet.
EnableControls;
end;
end;
procedure TConexao.
ObterDataSet(const myDataSet:
TDataSet; var myStringList:
TStringList);
var
fDataSet: TDataSet;
i: integer;
begin
if not Assigned(myDataSet)
then Exit;
fDataSet :=
TClientDataSet(myDataSet);
fDataSet.First;
if not Assigned(myStringList)
then myStringList :=
TStringList.Create;
while not fDataSet.Eof do
begin
myStringList.Add(fDataSet.
Fields[0].AsString);
fDataSet.Next;
end;
end;
function TConexao.
RecordCount(Query: String):
integer;
begin
if Self is TConexaoDBISAM
then
Result :=
TConexaoDBISAM(Self).
RecordCount(Query)
else if Self is TConexaoXML
then
Result := TConexaoXML(Self).
RecordCount(Query)
else if Self is
TConexaoFirebird then
Result :=
TConexaoFirebird(Self).
RecordCount(Query);
end;
function TConexao.
julho

2013

15

RecordCount(const myDataSet:
TDataSet): integer;
begin
Result :=
(TDataSet(myDataSet).
RecordCount);
end;
function TConexao.
RetornaDataSet(Query: String):
TDataSet;
begin
if Self is TConexaoDBISAM
then
Result :=
TConexaoDBISAM(Self).
RetornaDataSet(Query)
else if Self is TConexaoXML
then
Result := TConexaoXML(Self).
ExecutaSelect(Query)
else if Self is
TConexaoFirebird then
Result :=
TConexaoFirebird(Self).
RetornaDataSet(Query);
end;
function TConexao.
CarregarFoto(const MyFile:
string; const myDataSet:
TDataSet; const MyFieldName:
string = FOTO): Boolean;
function
GetTableNameFromXMLSyntax(const
mySQLXML: string): string;
begin
if System.
Pos(AnsiUpperCase(where),
AnsiUpperCase(mySQLXML)) > 0
then
Result := trim(Before(af
ter(AnsiUpperCase(mySQLXML),
FROM), WHERE));
end;
var
S: TFileStream;
fClientDataSet:
TClientDataSet;
auxTableName: string;
auxFilterFields: string;
begin
if Self is TConexaoXML then
begin
if (Self.FiltroXML = )
then Exit;

16

julho

2013

auxTableName :=
GetTableNameFromXMLSyntax(Self.
FiltroXML);
auxTableName :=
auxTableName + .xml;
fClientDataSet
:= TConexaoXML(Self).
CarregarBanco(auxTableName);
auxFilterFields :=
Before(after(AnsiUpperCase(Self.
FiltroXML),WHERE), ORDER);
fClientDataSet.Filter :=
auxFilterFields;
fClientDataSet.Filtered
:= True;
try
fClientDataSet.Close;
fClientDataSet.Open;
if not (fClientDataSet.
state in dsEditModes) then
fClientDataSet.Edit;
TBlobField(fClientDataSet.
FieldByName(MyFieldName)).
LoadFromFile(MyFile);
fClientDataSet.Post;
fClientDataSet.
SaveToFile(TConexaoXML(Self).
CaminhoXMLAtual, dfXML);
except
on E: Exception do
begin
Raise EInvalidDataBase.
Create(EErroGeralBancoDeDados +
#13#10 + E.Message);
Result := False;
end;
end;
end
else
if Self is TConexaoDBISAM
then
begin
S := TFileStream.
Create(MyFile ,fmOpenRead);
with (myDataSet) do
begin
try
try
Edit;
TBlobField(Fie

ldByName(MyFieldName)).
LoadFromStream(S);
Post;
except
on E: Exception do
begin
Raise
EInvalidDataBase.
Create(EErroGeralBancoDeDados +
#13#10 + E.Message);
Result := False;
end;
end;
finally
S.Free;
end;
end;
end
else if Self is
TConexaoFirebird then
begin
fClientDataSet :=
dmFirebird.ClUsuario;
try
fClientDataSet.Close;
fClientDataSet.Open;
fClientDataSet.Refresh;
if not (fClientDataSet.
state in dsEditModes) then
fClientDataSet.Edit;
TBlobField(fClientDataSet.
FieldByName(MyFieldName)).
LoadFromFile(MyFile);
fClientDataSet.Post;
fClientDataSet.
ApplyUpdates(0);
except
on E: Exception do
begin
Raise EInvalidDataBase.
Create(EErroGeralBancoDeDados +
#13#10 + E.Message);
Result := False;
end;
end;
end;
end;
function TConexao.
GetConexao(const typeConexao:

tBDConexao): TConexao;
begin
if Assigned(Result) then
closeConexao;
case typeConexao of
bdConexaoDBISAM:
Result
:= TConexaoDBISAM.create;
bdConexaoXML:
Result
:= TConexaoXML.create;
bdConexaoFirebird: Result
:= TConexaoFirebird.create;
end;
end;

Ns s precisamos tambm criar uma classe negocial no nosso caso


a classe de usurios TUsuario que far a ponte entre a camada de apresentao com ela, provendo por meio do encapsulamento funcionalidades
bsicas negociais disponveis para os mdulos de apresentao requeridos
no interessante modularizar? Alteraes em apenas um lugar e todas
as chamadas automaticamente atualizadas abstrao gera produtividade se
bem empregada, como demonstra esta nossa construo.
Abrir um conjunto de dados (select) agora ficou mais simples de escrever - somente uma chamada de mtodo (no caso, AbreTabelaUsuarios)
necessria. Evoluindo o processo, temos inmeras vantagens:

Implementao da Orientao a Objetos: como citado acima, a
facilidade em reutilizao de cdigo em mdulos, outros sistemas, etc;

Abstrao realizada pelas classes de conexo: no preciso (e nem
deve) executar quaisquer comandos SQL ou acessar quaisquer base de dados
diretamente na camada de apresentao;

Encapsulamento de DataSets: essa nossa infraestrutura padronizou a nomenclatura bsica de execuo de SQLs, onde cada sentena uma
constante passada como parmetro em um mtodo de obter dataset do objeto
TConexao. Logo abaixo segue um exemplo destacado deste item:
Cdigo 05

function TUsuario.
AbreTabelaUsuarios: TDataSet;
begin
Result := Conexao.RetornaData
Set(sqlSelectAllFieldsOrderByNo
me);
end;
Abaixo mostrado o mtodo
SetDataBase, onde solicitada
a troca de um banco. Note que
o nome dos bancos fazem parte
julho

2013

17

de um tipo chamado tBDConexao,


como citado acima, que
referenciado como um parmetro
constante:
type tBDConexao =
(bdConexaoXML, bdConexaoDBISAM,
bdConexaoFirebird);
procedure TForm1.
cbSelecionaBDChange(Sender:
TObject);
begin
case cbSelecionaBD.ItemIndex
of
0: Usuario.
SetDataBase(bdConexaoXML);
1: Usuario.
SetDataBase(bdConexaoDBISAM);
2: Usuario.
SetDataBase(bdConexaoFirebird);
end;
Usuario.
CarregaUsuarios(ClUsuario);
mskCarregaNomes.Text :=
ClUsuarioNome.AsString;
mskCarregaNomesExit(Self);
end;
Nossa classe TConexao tambm possui mtodos de formatao especficos nossos prprios FormatDate, FormatFloat, dentre outros. Isso porque o
Firebird aceita o formato yyyy-mm-dd enquanto que o XML, por exemplo,
aceita somente o formato dd/mm/yyyy. J o FormatInteger formata o XML
para o formato (com aspas simples) meu nmero enquanto que o Firebird
aceita tanto o nmero puro (sem aspas) quanto com aspas simples. J o banco
DBISAM aceita o formato numrico somente sem aspas. Portanto, a classe far
o intercmbio de formatos distintos de acordo com a instncia apropriada.
Uma coisa interessante o mtodo GetNextId: ele tem a finalidade de
recuperar o prximo valor de um cdigo, por exemplo, para inseri-lo como uma
chave-primria, ou apenas um valor normal, etc; so vrias as possibilidades
de utilizao. Ele faz o select de um Max(coluna)+1, para retornar sempre o
prximo item sequencial de um registro. Um detalhe especial como o processo
feito para o nosso banco XML ele faz um Max em um campo agregado
de um TClientDataSet em criado em tempo de execuo!
Cdigo 06

function TConexaoFirebird.
GetNextId(const fFieldName:
string; const fTable: string):
string;

18

julho

2013

var
fId: string;
begin
fId :=
TCustomSQLDataSet(Self.Retorna
DataSet(Format(sqlMaxIndexFrom
FieldNameFormat1, [fFieldName,
fTable]))).Fields[0].AsString;
if (fId = ) then fId :=
1;
Result := fId;
end;

Finalmente, o mtodo CarregaUsuarios alimenta um objeto TClientDataSet de retorno, onde como j mencionado anteriormente armazenar dados
apenas read-only: a aplicao jamais sensibilizar-los diretamente na base
de dados. Todo este acesso somente a classe TConexao realizar e de forma
abstrata. E isso um dos pontos mais interessantes deste projeto: imagina
trocar um banco de dados com sua aplicao rodando, ou envi-la ao cliente
em uma demonstrao do produto com algumas funcionalidades disponveis
e na verso paga alterar o banco de dados para uma nova base de dados mais
robusta e profissional; esta funcionalidade tornar sem dvida sua aplicao
isenta de implementaes amarradas de dentro da camada de apresentao
(mtodo tradicional de programao), trazendo mais produtividade em termos
de manuteno e desenvolvimento, e consequentemente mais agilidade e
praticidade durante todo o ciclo de vida da aplicao.
Com isso, finaliza-se a primeira parte deste artigo. Na prxima revista
falaremos sobre a implementao de um parser SQL para XML, utilizao
do componente de minha autoria para visualizao e validao de dados,
interagindo por debaixo dos panos o TMaskeditHV (explicado em edies
anteriores), explicao sobre importantes mtodos da nossa classe e finalmente o desenvolvimento de nosso programa de exemplo utilizando esta nossa
classe, em uma simulao real de sua implementao, obtendo e alternando
bases de dados distintas em tempo de execuo. At a prxima, e boa leitura!

Sobre o autor
Hamden Vogel
Analista de Sistemas ps-graduado em Engenharia de Software pela
UPIS e Programador Delphi com larga experincia desde 2000, tem desenvolvido e vendido softwares em Delphi para a frica e Estados Unidos,
alm do mercado nacional. Colaborou com dicas e componentes para sites
especializados em Delphi. Tambm desenvolve em outras linguagens como
C/C++, ASP, PHP e .NET.

suporte@theclub.com.br

Android
Trabalhando com o TouchScreen

m dos recursos mais poderosos e fascinantes no desenvolvimento utilizando o sistema android a possibilidade de
poder interceptar eventos na tela com o toque do dedo, o
TouchScreen. Temos classes especficas para este trabalho,
a minha idia neste ms descrever as principais funcionalidades destas classes junto com seus mtodos criando exemplos didticos e
de grande utilidade no dia-a-dia.

Eventos de Entrada (Input Events)


Existem vrias maneiras de interceptar os eventos de interao do usurio
com o aplicativo. Ao considerar eventos dentro de sua interface de usurio,
a abordagem capturar os eventos dentro da regio denominada View,
fornecendo os meios para faz-lo. Quando compomos um layout, fazemos
o uso de quantas Views forem necessrias, neste caso, podemos observar
vrios mtodos de retorno de chamada pblica que parecem teis para os
denominados eventos de InPut(entrada) e OutPut(sada). Esses mtodos so
chamados pelo Android quando a respectiva ao ocorre no objeto em questo. Por exemplo, quando um componente (um boto) tocado, o mtodo
onTouchEvent() acionado no objeto. No entanto, a fim de interceptar isso,
voc deve estender a classe e substituir o mtodo. No entanto, ampliando
cada objeto View, a fim de lidar com um evento como esse no seria prtico.
por isso que a classe View tambm contm uma coleo de interfaces
aninhadas com retornos de chamada que poderemos utilizar com uma maior
facilidade. Estas interfaces, chamadas de Listeners (ouvintes), so o seu
bilhete para capturar a interao do usurio com a interface do aplicativo.
Temos a possibilidade de estender uma classe View, a fim de construir por
exemplo um componente personalizado. Outra situao estender a classe
Button para fazer algo mais sofisticado. Neste caso, poderemos definir os
comportamentos de eventos padro para sua classe usando os manipuladores
de eventos, os (Event Handlers)

para serem implementados, sendo:


onKeyDown (int, KeyEvent) - Chamado quando apertamos alguma tecla.
onKeyUp (int, KeyEvent) - Chamado quando soltamos alguma tecla.
onTrackballEvent (MotionEvent) - Chamado quando ocorre um evento
de movimento.
onTouchEvent (MotionEvent) - Chamado quando ocorre um evento de
movimento da tela com o toque do dedo.
onFocusChanged (boolean, int, Rect) - Chamado quando recebemos ou
perdemos o foco.
Demonstrei os mtodos acima a fim de apresentar os manipuladores
existentes. Daremos importncia ao evento OnTouchEvent.

Entendendo a classe MotionEvent


A classe base para suporte TouchScreen a classe MotionEvent onde
passado para as Views atravs do mtodo onTouchEvent () (abordado
anteriormente) podendo tambm ser sobrecarregado . Vejamos na Imagem
01 a hierarquia desta classe para maiores informaes.

Manipuladores de Eventos (Event Handlers)


De acordo com a prpria documentao do Android, quando estamos
construindo um componente a partir da classe View teremos vrios mtodos

Figura 01: Estrutura da classe MotionEvent.

A classe MotionEvent contm inmeros mtodos e constantes como


julho

2013

19

por exemplo: informaes das coordenadas X, Y e capturas de movimentos.

Como foi discutido anteriormente, devemos utilizar a classe View para


implementarmos o mtodo OnTouchEvent().

Principais mtodos e constantes


- GetAction(): Este mtodo recebe a ao que foi executada, fornecendo
as seguintes constantes para determinar a ao.
ACTION_DOWN: Ao que ocorre quando um toque foi iniciado contendo
o local de partida inicial do movimento.
ACTION_UP: Esta constante acionada quando um gesto pressionado
tenha terminado, contendo a localizao final, bem como todos os pontos
intermedirios desde o ltimo movimento.
ACTION_MOVE: Ao que ocorre entre as constantes ACTION_DOWN e
ACTION_UP. (A movimentao em si)
Todas as constantes abordadas acima retornam um valor inteiro.
- GetY(): Este mtodo retorna um valor do tipo Float das coordenadas
Y (linha vertical). Este valor pode ser transformado para inteiro.
- GetX():Este mtodo retorna um valor do tipo Float das coordenadas
X (Linha horizontal). Este valor pode ser transformado para inteiro.

Classe CriarImagem.java
Antes de iniciarmos a codificao desta classe ser necessrio adicionar
uma imagem qualquer em nosso aplicativo. Basta copiar e colar no diretrio
res/drawable-hdpi. Ver Imagem 03.

Exemplo 1 - Movimentando uma Imagem com o toque do


dedo
Nos exemplos deste artigo iremos envolver todos os conceitos aprendidos
anteriormente sendo que neste primeiro criaremos uma imagem dinamicamente. Esta imagem ser movimentada com o dedo e usando o TouchScreen
na tela do dispositivo. Para isto descreverei todos os passos daqui pra frente.
Inicie uma aplicao do Zero clicando em File/New/Android Project...
criando uma classe para estender (extends) para a interface View. Clique
sobre o pacote e com o boto direito escolha New/Class. nesta classe onde
criaremos todos os mtodos responsveis pela movimentao da Imagem em
questo. Ver Figura 02.
Figura 03: Imagem adicionada no projeto.

Observao Importante:
Esta imagem dever ser do tipo .png ou jpg possuindo o nome escrito
especificamente em caixa baixa para assim evitar problemas de compilao.
Logo em seguida importaremos as bibliotecas que sero utilizadas ao
decorrer do desenvolvimento.
Cdigo 01

package pct.Android_
OnTouchEvent;

Figura 02: Criando a classe CriarImagem.java.

20

julho

2013

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.
drawable.Drawable;
import android.util.
AttributeSet;
import android.view.MotionEvent;

import android.view.View;
public class CriarImagem
extends View
{
private Drawable imagem;
private int x, y, largura,
altura;
private boolean Clicou;
public int movimento = 12;
A classe principal CriarImagem herdar da classe View. Utilizaremos
algumas variveis privadas, sendo uma do tipo Drawable responsvel por
trabalhar com a imagem, outras do tipo int onde definimos a largura, altura,
as coordenadas X, Y e uma booleana indicando se clicamos ou no na imagem.
Cdigo 02

public CriarImagem(Context
context, AttributeSet attrs)
{
super(context, attrs);
imagem =
context.
getResources().getDrawable(R.
drawable.theclub);
largura = imagem.
getIntrinsicWidth();
altura = imagem.
getIntrinsicHeight();
x = 150;
y = 300;
setFocusable(true);
}
No mtodo Construtor passamos dois parmetros: o Context o Contexto
da aplicao e o AttributeSet que significa uma coleo de atributos que podem ser utilizados dentro deste mtodo. O mtodo context.getResources().
getDrawable() ir recuperar a imagem, os mtodos getIntrinsicWidth() e
getIntrinsicHeight() iro retornar a largura e altura respectivamente, x e y
sero as coordenadas para posicionamento da imagem em relao a tela do
dispositivo. J o mtodo setFocusable() definir se a view ir ou no receber
o foco enquanto estiver em modo TouchScreen. Deixaremos como true
Cdigo 03

@Override
protected void onDraw(Canvas
canvas)
{
super.onDraw(canvas);
imagem.setBounds(x,y,x+largura
,y+altura);
imagem.draw(canvas);
}

Para desenhar a imagem foi necessrio reescrever o mtodo OnDraw()


tendo como parmetro a classe Canvas. Passamos as coordenadas X e Y e
a largura e altura utilizando o mtodo setBounds() e redesenhamos com o
draw() usando a classe Canvas. Este mtodo disparado sempre quando
necessitamos redesenhar a imagem.
Cdigo 04

public boolean
onTouchEvent(MotionEvent
motionEvent)
{
this.x = (int)motionEvent.
getX();
this.y = (int) motionEvent.
getY();

switch(motionEvent.
getAction())
{

case MotionEvent.ACTION_
DOWN:

{

Clicou = imagem.
copyBounds().contains(x,y);

break;

}


case MotionEvent.ACTION_
MOVE:

if (Clicou)

{

this.x = x (largura/2);

this.y = y (altura/2);

break;

}




case MotionEvent.ACTION_
UP :

{

Clicou = false;

break;

}
}

invalidate();
return true;
}
Este o principal evento que iremos trabalhar. Ele tem como parmetro
de entrada a classe MotionEvent e retorna um valor booleano. No primeiro
momento iremos atribuir s variveis globais X e Y as respectivas coordenadas com os mtodos getX() e getY(). Faremos um Case para comparar
julho

2013

21

as Constantes do mtodo GetAction(), o mesmo que recebe a ao que


foi executada, fornecendo constantes para determinar a ao. Na constante
ACTION_DOWN com o mtodo copyBounds() indicamos que clicamos na
imagem informando suas coordenadas. A constante ACTION_MOVE, faz o ato
de mover em si, movimentamos a figura fazendo o clculo das coordenadas
menos o tamanho dividido por dois, dando uma impresso de movimento. Por
final retiramos o foco com a constante ACTION_UP. Resumindo, este mtodo
ir movimentar a Imagem conforme o toque do dedo.

Configuraes no AndroidManifest.xml
Nas configuraes do arquivo AndroidManifest.xml no faremos
praticamente nada de mirabolante, apenas setamos o uso da Activity
Android_OnTouchEventActivity que ser comentado logo a seguir. Ver em
seguida a listagem completa.

por exemplo: a cor cinza


que preencher o fundo onde
movimentaremos a imagem. Ver
cdigo abaixo:.
<?xml version=1.0
encoding=utf-8?>
<resources>
<string
name=hello>Trabalhando com o
evento OnTouchEvent!</string>
<string name=app_name>The
Club - Android</string>
<color
name=cinza>#D0D0D0</color>
</resources>

Cdigo 04

<?xml version=1.0
encoding=utf-8?>
<manifest xmlns:android=http://
schemas.android.com/apk/res/
android
package=pct.Android_
OnTouchEvent
android:versionCode=1
android:versionName=1.0>
<uses-sdk
android:minSdkVersion=8 />
<application android:icon=@
drawable/icon android:label=@
string/app_name>
<activity
android:name=.Android_
OnTouchEventActivity
android:label=@string/app_
name>
<intent-filter>
<action
android:name=android.intent.
action.MAIN />
<category
android:name=android.intent.
category.LAUNCHER />
</intent-filter>
</activity>

Criando o Lay-Out
O aplicativo possuir um LinearLayout onde conter um TextView e
um componente chamado desenho que faz referncia classe CriarImagem.
Ver Imagem 04.

Figura 04: Componentes utilizados.

A Imagem 05 nos d uma noo do Lay-out.

</application>
</manifest>
Configuraes no Strings.xml
Este arquivo ir definir algumas
constantes que sero abordadas
ao decorrer do projeto, como

22

Figura 05: Lay-Out do exemplo 1.


julho

2013

O XML correspondente:
Cdigo 05

<?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
>
<TextView
android:layout_width=fill_
parent
android:layout_height=wrap_
content
android:text=@string/
hello
/>
<pct.Android_OnTouchEvent.
CriarImagem
android:id=@+id/desenho
android:layout_width=fill_
parent
android:layout_height=fill_
parent
android:background=@
color/cinza
/>
</LinearLayout>

Cdigo 06

package pct.Android_
OnTouchEvent;
import android.app.Activity;
import android.os.Bundle;
public class Android_
OnTouchEventActivity extends
Activity
{
@Override
public void onCreate(Bundle
savedInstanceState)
{
super.
onCreate(savedInstanceState);
setContentView(R.
layout.criarimagem);
}

}
O SetContentView() ser o mtodo que ir chamar a tela de nosso
aplicativo. Ver Imagem 06.

Exemplo em Run-Time

Devemos dar uma ateno especial na linha de cdigo abaixo:

<pct.Android_OnTouchEvent.
CriarImagem />

Percebam que interessante, conseguimos chamar diretamente uma classe


(no caso uma View) de dentro do XML, fantstico!

Codificando o exemplo 1
Seguindo a lgica, a atividade Android_OnTouchEventActivity que
codificaremos a seguir, possuir uma chamada para o lay-out criarimagem.
xml que dentro do mesmo teremos a classe CriarImagem.java, na qual est
localizada toda a codificao de nosso exemplo.

Figura 06: Movimentando a Imagem The Club.

Exemplo 2 Pintando e escrevendo na Tela com o toque


do dedo
Este segundo exemplo ir pintar e desenhar na tela de nosso aplicativo
com o toque do dedo. Aprenderemos tambm a trabalhar com a classe Paint,
julho

2013

23

Path entre outras. Os passos so idnticos ao do exemplo anterior, por isto


detalharei um pouco menos para ficar menos cansativo abordando apenas
os conceitos inditos.
Chega de conversa e vamos ao trabalho!

Classe PintarTela.java
Utilizaremos algumas bibliotecas adicionais para desenvolver este exemplo, veja abaixo:
Cdigo 07

package pct.Android_
OnTouchEvent;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.
AttributeSet;
import android.view.MotionEvent;
import android.view.View;
Temos a classe android.graphics.Paint sendo especfica para pintura,
estilo, cor. Ela tambm oferece base para desenhar geometrias, texto e bitmaps. J a classe android.graphics.Path permite realizar contornos e formas
geomtricas como: segmentos de linhas retas, curvas quadrticas e cbicas.
Cdigo 08

public class PintarTela extends


View
{
private Paint paint = new
Paint();
private Path path = new
Path();

Esta classe tambm herdar da classe View e utilizar dois objetos, do


tipo Paint e Path.
Cdigo 09

public PintarTela(Context
context, AttributeSet attrs)
{

super(context, attrs);

24

paint.setAntiAlias(true);
julho

2013


paint.setStrokeWidth(6);

paint.setColor(Color.BLUE);

paint.setStyle(Paint.Style.
STROKE);

paint.setStrokeJoin(Paint.
Join.ROUND);
}
Temos o mtodo setAntiAlias() que serve para suavizar as bordas de
nosso toque, o setStrokeWidh() o tamanho da linha, o setColor() para
definir a cor, o setStyle() o estilo e o setStrokeJoin() um tratamento
especfico para quando as linhas se juntarem.
Cdigo 10

@Override
protected void onDraw(Canvas
canvas)
{

canvas.drawPath(path,
paint);
}
Mtodo que ir desenhar usando as configuraes definidas anteriormente
na classe Paint.
Cdigo 11

@Override
public boolean
onTouchEvent(MotionEvent
motionEvent)
{

float eventX =
motionEvent.getX();

float eventY =
motionEvent.getY();

switch (motionEvent.
getAction())

{

case MotionEvent.
ACTION_DOWN:

path.
moveTo(eventX, eventY);

return true;

case MotionEvent.
ACTION_MOVE:

path.
lineTo(eventX, eventY);

break;

default:

return false;

}

invalidate();
return true;

Usaremos apenas as constantes ACTON_DOWN para dar incio do movimento de acordo com as coordenadas definidas fazendo o uso do mtodo
MoveTo() e a ACTION_MOVEpara adicionar uma linha a partir do ltimo
ponto especificado pelas coordenadas (x, y).

Configuraes no AndroidManifest.xml
Adicionaremos algumas configuraes bsicas neste arquivo j comentado
mais acima, veja o trecho:

Cdigo 12

...
<activity android:name=.
Android_OnTouchEventActivity_2
android:label=@string/app_
name>
<intent-filter>
<action
android:name=android.intent.
action.MAIN />
<category
android:name=android.intent.
category.LAUNCHER />
</intent-filter>
</activity>
...

Criando o Lay-Out
Teremos um LinearLayout onde conter um TextView e um componente chamado desenho que faz referncia classe Pintartela. Ver Imagem 07.

O XML correspondente:
Cdigo 13

<?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
>
<TextView
android:layout_width=fill_
parent
android:layout_height=wrap_
content
android:text=@string/
hello
/>
<pct.Android_OnTouchEvent.
PintarTela
android:id=@+id/desenho
android:layout_width=fill_
parent
android:layout_height=fill_
parent
/>
</LinearLayout>

Codificando o Exemplo 2
Seguindo os passos que j foram descritos acima foi necessrio criar
uma classe estendendo de uma Activity para invocarmos o XML contendo a
classe PintarTela. O mtodo SetContentView() ir chamar a tela de nosso
aplicativo. Ver Imagem 08.
Abaixo cdigo completo.
Cdigo 14

package pct.Android_
OnTouchEvent;
import android.app.Activity;
import android.os.Bundle;
public class Android_
OnTouchEventActivity_2 extends
Activity
{
@Override
public void onCreate(Bundle
savedInstanceState)
Figura 07: Lay-Out do exemplo 2.
julho

2013

25

super.
onCreate(savedInstanceState);
setContentView(R.
layout.pintartela);
}
}

Exemplo em Run-Time

Referncias

http://developer.android.com/reference/android/view/MotionEvent.html
http://developer.android.com/reference/android/view/View.html

Concluses
Uma das formas de interceptar os eventos de interao do usurio com o
aplicativo capturar estes eventos dentro de uma regio chamada View.
a partir desta classe que discorremos o artigo deste ms. Trabalhamos com o
evento OnTouchEvent(), que o evento que capta o toque do dedo na tela do
dispositivo e a classe MotionEvent, classe base para suporte para esta tarefa
junto com mtodos e constantes. No primeiro exemplo procurei demonstrar
como movimentar uma imagem com o TouchScreen, j no segundo como
pintar e escrever na tela do aplicativo. Esta uma das inmeras formas de usar
os recursos TouchScreen na programao Android.
Espero que tenham gostado! Deixo aqui um abrao e nos vemos no ms
que vem!

Sobre o autor
Thiago Cavalheiro Montebugnoli
adora aprender novas tecnologias. Formado pela Faculdade de Tecnologia de Botucatu
SP (FATEC), j desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco
de Dados SQL Server e Firebird. Atualmente trabalha no Centro de Processamento de Dados da
Prefeitura Municipal de Ita-SP colunista mensal da Revista The Club Megazine e consultor
Tcnico do The Club. Possui as seguintes certificaes: MCP - Microsoft Certified Professional,
MCTS - Microsoft Certified Technology Specialist, MCAD - Microsoft Certified Application Developer
e MCSD - Microsoft Certified Solution Developer.

thiago@theclub.com.br
Figura 08: Desenhando e Pintando em Run-Time.

26

julho

2013

As principais mudanas
Delphi XE4

Delphi XE4 j est disponvel para download, e conta com


vrias novidades que lhes apresentarei neste artigo.
A primeira delas, est na excluso da RAD Studio
que na verso anterior trazia quatro IDEs que podamos
utilizar para desenvolver o Delphi, C++, PHP e .NET. No XE4
o Delphi XE4 Prism. Com isso a RAD Studio ir ficar com Delphi, C++Builder
e HTML5 Builder.

Aplicaes para iPhone e iPad e os controles de interface


do iOS.
Agora podemos desenvolver aplicaes usando um componente de calendrio para escolhermos uma data em um aplicativo iOS como mostra a figura 01.

Entre as principais mudanas no Delphi XE4, esto:



Desenvolvimento de aplicaes multidispositivos para iOS, Windows
e Mac.

Desenvolvimento de aplicaes para iPhone e iPad.

Controles de interface do iOS.

Rpida criao de prottipos.

Suporte aos bancos de dados IBLite e SQLite iOS.

Suporte cliente multicamadas para DataSnap, web services e
bancos de dados corporativos.

Acesso a mais bancos de dados em mais dispositivos e com maior
facilidade na FireDAC.

Plataforma de Aplicaes FireMonkey FM3.

Figura 01: Adicionando o componente TcalendarEdit

Selecionando o componente TcalendarEdit, no Object Inspector, abriremos a aba Events clicando duas vezes no espao vazio ao lado de onchange.
Escreveremos o cdigo da seguinte forma:

Explicarei ento, rapidamente cada um destes novos recursos.


Cdigo 01

Desenvolvimento de aplicaes multidispositivos para iOS,


Windows e Mac.
As vantagens de desenvolver aplicaes multidispositivos, significa que no
precisaremos manter projetos de desenvolvimento separados para criar uma
aplicao de modo nativo em mltiplas plataformas (iOS, Windows e Mac).
O desenvolvimento de aplicaes verdadeiramente nativas permite que
criemos aplicaes nativas livres de scripts, que tiram o mximo proveito das
capacidades e desempenho dos dispositivos subjacentes. Resultando em
aplicaes mais rpidas e ricas que agradaro ainda mais seus usurios finais.

procedure TForm25.
CalendarEdit1Change(Sender:
TObject);
begin
ShowMessage(FormatDateTime(d
ddddd, CalendarEdit1.Date));
end;
Podemos tambm usar um componente de boto com estilos diferentes
em um aplicativo iOS como mostra a figura 02.
julho

2013

27

Figura 04: Adicionando itens no ComboBox.

Essa ferramenta conta com diversos recursos, porm, no entraremos


em detalhes neste artigo.
Agora veremos como usar o componente TWebBrowser em um aplicativo
iOS.
Utilizaremos os seguintes componentes:
TToolBar
TButton
TEdit
A aplicao vai ficar como a figura 05:

Figura 02: Estilos do boto.

Usando componentes combobox para escolher itens de uma lista em um


aplicativo iOS como mostra a figura 03.

Figura 03: Adicionando ComboBox na aplicao

Depois de adicion-lo, podemos observar o componente ComboBox no


form, clicando com o boto direito do mouse no componente ComboBox e
selecionando itens do editor, abri-r uma janela como mostra a figura 04.

28

julho

2013

Figura 05: Componentes adicionados.

Antes de implementar manipuladores de eventos, primeiramente implantaremos um mtodo comum para abrir uma pgina web com base na
propriedade Text do Tedit.
Cdigo 02

procedure OpenURL; next to {


Private declarations }
type
Tform34 = class(TForm)

ToolBar1: TToolBar;

Button1: TButton;

Edit1: TEdit;

WebBrowser1:
TwebBrowser;

private

Procedure OpenURL;

Public
end;
Aperte CTRL + SHIFT + C para criar o procedimento.

Rpida criao de prottipos


O Delphi XE4 fornece um fluxo de trabalho aperfeioado e otimizado,
que automatiza a distribuio e a depurao tanto no simulador quanto no
dispositivo. Crie com rapidez prottipos de suas aplicaes com layouts para
cada tipo de dispositivo, passando com agilidade da prototipagem produo.
Integre o feedback rapidamente, distribuindo seu prottipo diretamente no
dispositivo de destino, usando objetos e cdigos reais do framework.

Suporte aos bancos de dados IBLite e SQLite iOS


O Delphi XE4 inclui suporte a banco de dados iOS local para SQLite e InterBase embutido via dbExpress, FireDAC e IBX. Voc ainda recebe uma licena
de distribuio gratuita e ilimitada do IBLite para iOS

Suporte a cliente multicamadas para DataSnap, web services


e bancos de dados corporativos
O Delphi XE4 inclui conectividade integrada a banco de dados de classe
corporativa ou ISV, computao nas nuvens e middleware. Voc conta com
suporte integrado a SQL Server, Oracle, Sybase, DB2, InterBase, SQL Anywhere,
SQLite, MySQL e muitos outros bancos de dados embutidos e de servidor, assim
como servios nas nuvens, incluindo Windows Azure e Amazon.

Cdigo 03

Procedure TForm34.OpenURL;
Begin
WebBrowser1.Navigate(Edit1.
Text);
end;

A linha WebBrowser1.Navigate(Edit1.text); iremos programar conforme


o nome componente.
No evento OnChange do edit digite OpenURL;
E no evento KeyDown do edit digite o seguinte cdigo.
Cdigo 04

Procedure Tform34.
Edit1KeyDown(Sender; bar Key:
Word;
var KeyChar: char; Shift:
TshiftState);
Begin
If (key = vkReturn) then
Begin
OpenURL;
Button1.SetFocus;
end;
end;
Pronto agora s testar e usar a criatividade para melhorar a interface
da aplicao.

Acesse mais bancos de dados em mais dispositivos e com


maior facilidade com a FireDAC
A FireDAC d a voc acesso direto, nativo e de alta velocidade do Delphi a
InterBase, SQLite, MySQL, SQL Server, Oracle, PostgreSQL, DB2, SQL Anywhere,
Advantage DB, Firebird, Access, Informix, DataSnap e muitos outros bancos
de dados mobile e corporativos, locais ou embutidos. Migre com facilidade
do BDE e de outras tecnologias.

Plataforma de Aplicaes FireMonkey FM3


O Delphi XE4 tira mximo proveito do FireMonkey FM3, a plataforma
de ltima gerao para construo de aplicaes multidispositivos verdadeiramente nativas a partir de uma nica base de cdigo. Voc codifica uma
nica vez e o framework aperfeioa sua aplicao para mltiplas plataformas
de destino. Componentes visuais do a voc blocos de construo extensveis
e reutilizveis para que se obtenha um desenvolvimento mais rpido e altamente manutenvel.

Novo Compilador
Para oferecer suporte aos dispositivos mveis foram criados dois novos
compiladores, um para o simulador do iOS e outro para o dispositivo fsico
(ARM), j que as arquiteturas so distintas.Desta forma agora temos:




Win32 compiler (DCC32)


Win64 compiler (DCC64)
Mac compiler (DCCOSX)
iOS Simulator compiler (DCCIOS32)
iOS ARM compiler (DCCIOSARM)

A novidade est nos dois ltimos, os quais seguem um desenho totalmente


novo, baseados em um padro chamado LLVM (http://llvm.org). Podemos
julho

2013

29

entender o LLVM como um conjunto de mdulos e ferramentas reutilizveis


para compiladores, utilizados amplamente por diversos compiladores nativos,
entre eles o prprio Xcode, nativo da Apple.

faa uso de construes como TStringBuilder e TStringHelper para a manipulao de strings. Um trecho de cdigo utilizando estas classes para ilustrar:

Mudanas no Delphi Language


Para suportar dispositivos mveis, o novo compilador impe algumas
mudanas de linguagem. Na verdade, mais do que mudanas, trata-se de uma
grande evoluo da linguagem Delphi. No futuro, estes e outros avanos sero
portados tambm para os compiladores Win32, Win64 e OSX.

Automatic Reference Counting (ARC)


O ARC traz o melhor dos dois mundos: gerenciamento automtico de
memria sem a necessidade de um garbage collection, tido como um grande
vilo de performance nas linguagem que o utilizam. Em resumo o ARC gerencia
o ciclo de vida de objetos sem que voc tenha que se preocupar com o Free.
Cdigo 05

Class procedure TMySimpleClass.


CreateOnly;
Var
MyObj: TMySimpleClass;
Begin
MyObj := TMySimpleClass.Create;
MyObj.DoSomething;
End;
No exemplo acima, o objeto MyObj ser removido da memria assim
que sair do escopo. Importante ressaltar que a sintaxe tradicional (try/finally
fazendo o Free manual do objeto) continua suportada, portanto as migraes
de cdigo esto garantidas.
Tambm est disponvel o pattern Dispose e suporte a [Weak] References.
Voc pode encontrar mais sobre isso aqui: http://edn.embarcadero.com/
article/43073.

Tipo String
Todos os tipos string existentes (AnsiString, UTF8String, RawByteString,
WideString, AnsiChar, PAnsiChar, PWideChar, OpenString, ShortString) agora
esto simplificados em um nico: String. Alm disso, as strings agora so
0-based ao invs de 1-based. Isso significa que alguns cdigos podem precisar
de reviso, mas as funes de manipulao de strings j esto preparadas
para esta mudana.
Outra importante mudana que as strings passaro a ser imutveis,
devido ao gerenciamento de memria dos dispositivos mveis. Nesta verso
do compilador tudo continua conforme anteriormente, voc receber apenas
uma warning alertando que, futuramente, construes como a exibida abaixo
no ser mais suportada:

Outras mudanas
1. Ponteiros no so suportados no mundo mobile
2. No h suporte para Inline assemby
3. Evite chamadas a APIs diretamente, esteja pronto para as novas
plataformas que viro.
4. Prefira utilizar as units cross-plataform, especialmente ao manipular
arquivos (unit IOUtils)
Informaes e recursos para pesquisa
5. Pginas principais dos produtos: RAD Studio, Delphi, C++ Builder,
HTML5 Builder
6. Documentao on line: http://docwiki.embarcadero.com/RADStudio/XE4/en
7. Tutorial Delphi para iOS: Delphi iOS Application Development
8. Link para download do trial: https://downloads.embarcadero.com/
free/rad-studio
9. Trial via Instant Trial (AppWave): http://windowsapps.com/rad-studio-trial.html?trial=1
10. Tudo o que h de novo no Delphi e C++ Builder XE4
11. O que h de novo especificamente no C++ Builder 64 bit
12. Lista de correes do Delphi e C++ Builder XE4: http://edn.embarcadero.com/article/43068
13. White Paper do Marco Cantu sobre todas as mudanas na linguagem: http://www.embarcadero.com/resources/white-papers/application-development

Concluso
Espero que tenham gostado do artigo, resumi as principais alteraes que
foram feita no Delphi XE4 que considero importante.
At a prxima.

Sobre o autor
Jos Antnio P. M. de Paula
Consultor Tcnico no The Club. Cursa o ltimo perodo da FATEC
(Botucatu) no curso de Informtica para Negcios.

str1[3]:=w;

zeca@datasmart.com.br
Portanto altamente recomendado que, para seus novos cdigos, voc

30

julho

2013

julho

2013

05

julho

2013

You might also like