22
Page 1 of 22 Artigos 05/06/2017 file:///V:/Restrito/Revistas/201701/revi1701.aspx

ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Embed Size (px)

Citation preview

Page 1: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Page 1 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 2: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Android – Conhecendo e utilizando o CardView

Caro leitor, Primeiramente desejo a você um ótimo início de ano, com muita saúde, paz e sucesso profissional. Inicio este ano

redigindo um artigo sobre a plataforma Android, sendo que neste abordarei o uso do widget “CardView”. Após analisar a documentação oficial, o cardview herda da classe “FrameLayout”, permitindo a exibição de informações dentro dos cartões, tendo uma aparência coerente nas diversas plataformas, podendo ser configurado sombras e bordas arredondadas. Na Figura 01 podemos conferir a estrutura da classe “android.support.v7.widget.CardView”.

Figura 01: Estrutura Hierárquica.

O “CardView” utiliza a propriedade de elevação (cardElevation) na versão “Lollipop” (Android 5.0, API nível 21) e posteriores. Para as versões mais antigas a implementação do efeito de sombra seria de forma emulada ou programática.

Principais Atributos

Nas próximas linhas irei descrever os atributos mais utilizados, confira os detalhes na Tabela 01.

Nome do atributo do XMLDescrição

android.support.v7.cardview:cardBackgroundColor Cor de fundo do CardView, podendo ser um valor na forma “#rgb”, “#argb”, “ rrggbb”, etc ...

android.support.v7.cardview:cardCornerRadius Configuração de dimensão de canto. As unidades disponíveis são: px (pixels), dp (pisels independentes, sp (pixels dimensionados com base na fonte), etc ...

android.support.v7.cardview:cardElevation Elevação para o widget, seguindos as mesmas unidades descritas acima.

android.support.v7.cardview:cardMaxElevation Elevação máxima, também seguindos as unidades supracitadas.

android.support.v7.cardview:cardPreventCornerOverlapPreenchimento ao CardView na versão v20, assim evitando interseções entre o conteúdo do

Page 2 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 3: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Tabela 01.

Principais Construtores

Teremos três principais construtores, sendo o:

CardView (Context context):Passamos por parâmetros apenas o Contexto da aplicação.

CardView (Context context, AttributeSet attrs):Informaremos por parâmetros o Contexto e valor da classe “AtributeSet”, que seria uma coleção de atributos, como os encontrados associados a uma tag em um documento XML.

CardView (Context context, AttributeSet attrs, int defStyleAttr):No momento da criação usaremos os mesmos parâmetros citados anteriormente adicionados do atributo inteiro “defStyleAttr”que nada mais é do que um identificador para o atributo.

Principais Métodos

Teremos inúmeros métodos referentes à classe “CardView”. Abaixo destaquei alguns referentes ao retorno e outros de Atualização. Os famosos (Gets e Sets) da programação orientada a objetos. Ver Tabela 02.

Tabela 02.

Importando a Biblioteca

cartão e os cantos arredondados. Usaremos os valores booleanos: “true” ou “false”

android.support.v7.cardview:cardUseCompatPadding Adicione o espaçamento a partir da versão  API21, conseguindo uma compatibilidade para versões anteriores. Usaremos atributos booleanos para esta tarefa.

android.support.v7.cardview:contentPadding Seria o interior entre as bordas do Cardview. Usaremos valores de pixels descritos anteriormente.

android.support.v7.cardview:contentPaddingBottom/android.support.v7.cardview:contentPaddingLeft/android.support.v7.cardview:contentPaddingRight/android.support.v7.cardview:contentPaddingTop

Espaço interior entre a borda inferior, lateral esquerda, direita e superior respectivamente. Podemos utilizar como medidas os pixels descritos nos itens acima.

Nome do MétodoDescrição

getCardElevation() Retorna um valor numérico a elevação do CardView.

getContentPaddingBottom() Retorna um valor numérico do tipo inteiro do preenchimento interno antes da borda inferior do CardView. Temos os métodos getContentPaddingLeft(),  getContentPaddingRight() e getContentPaddingTop() que possuem praticamente a mesma característica relacionada à região esquerda, direita e superior.

setCardElevation(float elevation) Setar, atualizar a elevação do CardView.setContentPadding(int left, int top, int right, int bottom)

Define o preenchimento entre as bordas do CardView.

setRadius(float radius) Seta o raio de canto do widget.

Page 3 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 4: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

O primeiro passo para realizar a importação seria a criação de um novo projeto no Android Studio. Para isto clique em “File/New/New Project ...” Fiquem a vontade para inserir o nome como desejar, no meu caso foi criado com o nome “Cardview”. Para nossos testes também foi escolhido o SDK (API 23: Android 6.0 - Marshmallow).Após a criação do projeto, localize o arquivo “build.gradle (Module: app)”, o qual foi gerado automaticamente. Entre as chaves { } do item “dependencies” insira o código abaixo e logo após clique no link “Sync Now” para reindexar o projeto com a bilioteca “CardView”. Confira os detalhes no código e na Imagem 02 abaixo.

Exemplo:

dependencies {

. . .

compile 'com.android.support:cardview-v7:24.1.1'

. . .}

Figura 02: Adicionando a referência do CardView.

Criando um exemplo prático

Localize o arquivo de layout, por exemplo: “res/layout/activity_main.xml” o qual deverá estar provavelmente em branco, apenas com o componente de Layout. Para adicionar o componente “CardView” basta adicioná-lo com o auxílio da TAG “android.support.v7.widget.CardView” e dentro do mesmo iremos implementar uma “ImageView” junto com dois “TextViews”. Teremos o layout idêntico ao da Figura 03.

Page 4 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 5: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Figura 03: Layout Proposto.

Abaixo teremos o XML correspondente com explicações das principais partes do código.

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="datasmart.cardview.MainActivity">

Para que o “CardView” funcione dentro de algum elemento de layout como um “RelativeLayout” ou um “LinearLayout”, o mesmo tem que conter a seguinte linha de identificação destacada acima.

<android.support.v7.widget.CardViewandroid:id="@+id/card_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_gravity="center"android:paddingBottom="10dp"android:paddingTop="10dp"app:cardBackgroundColor="@color/cardview_light_background"app:cardCornerRadius="4dp">

Teremos algumas configurações padrões como “android:id”, altura e largura do layout, entre outras configurações. Faremos algumas configurações pertinentes da classe “CardView” como por exemplo o “app:cardBackgroundColor” para alterar a cor de fundo e “app:cardCornerRadius” para definir o arredondamento de canto do componente.

Page 5 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 6: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

<LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">

<TextViewandroid:id="@+id/txtTitulo"android:layout_width="match_parent"android:layout_height="wrap_content"android:maxLines="1"android:paddingBottom="10dp"android:paddingLeft="20dp"android:paddingStart="20dp"android:paddingTop="5dp"android:text="DataSmart - Avaré/SP"android:textColor="@android:color/black"android:textSize="20dp"android:textStyle="bold"/>

<ImageViewandroid:id="@+id/imgBanner"android:layout_width="match_parent"android:layout_height="120dp"android:layout_below="@id/txtTitulo"android:scaleType="centerCrop"android:src="@drawable/imagem"/>

<TextViewandroid:id="@+id/description"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/imgBanner"android:layout_centerVertical="true"android:layout_margin="4dp"android:gravity="center"android:maxLines="3"android:padding="10dp"android:text="Empresa que desenvolve softwares na área comercial para

diversas plataformas."android:textColor="@color/button_material_dark"android:textSize="13dp" />

</LinearLayout>

Já para o componente “ImageView” apenas definimos a Imagem, o layout e o tipo de escala. Para os componentes TextView não fazemos nada de diferente, apenas informamos um texto simples para uma fácil compreensão. É importante informar que ambos os componentes estão localizados dentro de um “LinearLayout” e o mesmo situado no interior de um “CardView”, o qual irá agrupá-los para uma melhor visualização.

</android.support.v7.widget.CardView></LinearLayout>

Podemos conferir o nosso aplicativo em run-time na Imagem 04.

Page 6 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 7: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Figura 04: Aplicativo em funcionamento.

ConclusõesNeste artigo foram descritos todos os principais atributos, métodos e construtores do componente “CardView”. Foi

desenvolvido também um pequeno exemplo de como devemos utilizá-lo corretamente. Este tipo de widget é facilmente encontrado em praticamente todos os novos aplicativos, assumindo assim um papel importante de organização e distribuição de dados dentro de um layout. Fica a dica aí pessoal, um grande abraço e até o mês que vem!

Referências

https://developer.android.com/training/material/lists-cards.html?hl=pt-br

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. Como

experiências profissionais mais recentes, possui em seu currículo sua atuação no Centro de Processamento de Dados da Prefeitura Municipal de Itaí-SP e atualmente compõe a equipe da Coordenadoria Tecnologia da Informação no IFSP – Instituto Federal do

Page 7 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 8: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Estado de São Paulo em Avaré. Além disso, é colunista mensal da Revista The Club Megazine e é consultor Técnico do The Club. Possui as seguintes certificações: MCP - Microsoft Certified Professional, MCTS - Microsoft Certified Technology Specialist,

MCAD - Microsoft Certified Application Developer e  MCSD - Microsoft Certified Solution Developer.

XML – Manipulando arquivos Blob externamente - carregando pra um banco de

dados descendente de TStream – parte I

Houve uma necessidade minha, e também um misto de curiosidade, de como implementar em um dataset um suporte a campos do tipo blob, e mais que isso, fazer isso de forma externa, onde estes tipos fossem salvos em um dado arquivo pra depois serem acessados por um objeto de TDBGrid através do evento OnDrawColumnCell, tornando disponível estes dados para leitura – até mesmo reproduzindo os caracteres de quebra de linha, por meio da função (API Kernel32 do Windows) DrawText.

Veja que interessante, agora temos um banco de dados que suporta blobs. E é aqui que a nossa brincadeira começa. Vamos começar o artigo com base na manipulação do tipo blob, em especial nesta primeira parte ao tipo ftMemo, e na próxima parte (continuação) será com suporte a gráficos (tipo ftGraphic). Tudo isso será manipulado através de arquivos XML, de onde ficarão armazenados estes dados.

Para que isto funcione de forma correta, temos que seguir alguns passos. O primeiro passo é definir uma coluna da tabela que fará o papel da chave primária, ou seja, são dados que não poderão se repetir. Porque isso? Porque nosso XMLprecisará de um valor único pra referenciar nossa tabela! Ele precisa referenciar cada campo que houve gravação do campo blobdele, e através do método locate ele vai carregar esta informação e “printar” na tela, tudo isso de forma automática. Feito isso, esta coluna será a comunicação tanto pra leitura quanto pra gravação destes dados pro XML e o nosso componente descendente de TDataSet, e vice-versa. Esta coluna de chave primária deverá ser atribuída a propriedade pública chamada BlobFieldFlag. E vamos aperfeiçoar nosso componente THVDataSet para dar suporte então ao tipo blob!Primeiros passos:

1. Definir um campo do tipo ftMemo e um outro campo que fará o papel da chave primária. Se isso não for feito, somente a primeira ocorrência do XML encontrado será levada em consideração;

2. Especificar na propriedade BlobFieldFlag o nome do campo que fará o papel da chave primária. Este campo é do tipo string, portanto o nome do campo deverá ser preenchido corretamente para não dar erros;

3. Especificar na propriedade BlobValue o conteúdo do blob (no nosso caso, nesta primeira parte, do tipo ftMemo) para o registro indicado;

4. Atribuir este BlobValue ao campo do tipo ftMemo;5. Salvar a tabela e pronto!

Parece meio grande, mas não é, segue um exemplo abaixo, basta fazer uma vez pra memorizar, lembrando sempre que o componente precisará do campo que foi indicado como chave primária.

procedure TfrmMain.Button7Click(Sender: TObject);begin HVDataSet1.Edit; HVDataSet1.BlobFieldFlag := 'CAMPO_CHAVE_PRIMARIA'; HVDataSet1.BlobValue := Memo1.Text; TBlobField(HVDataSet1.FieldByName('CAMPO_MEMO')).AsString := HVDataSet1.BlobValue; HVDataSet1.Post;end;

Page 8 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 9: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Figuras 01 e 02 – Exemplo de gravação de um tipo Blob no nosso THVDataSet e o XML contendo os dados salvos (serializados) deste campo, um a um, através da tag “MemoContents” (tela obtida pelo aplicativo Notepad++).

O XML que será criado, lido e salvo utilizará uma técnica própria de serialização desenvolvida pela classe open-source TXMLSerializer (empresa DragonSoft). Além disso, desenvolvi uma outra classe descendente de TDataSet chamada TCollectionDataSet, que manipulará coleções como dataset´s, porque criei um tipo baseado em coleção pra fazer interface com o XML, sendo acessado pelos métodos tradicionais de um dataset, principalmente como o locate (sobreescrevi ele).Então na prática este processo de serialização do XML funcionará assim:

1. Criar uma instância do tipo TBlobMetaDatas (descendente de TCollection);2. Carregar este objeto criado através da classe TXMLSerializer, lendo primeiramente o XML correspondente, e

populando este objeto através da função LoadObject (TXMLSerializer);3. Criar uma instância de TCollectionDataSet (descendente de TDataSet);4. Especificar na propriedade pública Collection (TCollection) o objeto criado no item 1 blobMetaDatas;5. Setar a propriedade Active de TCollectionDataSet  como true. Neste ponto a tabela está carregada com sucesso,

como uma tabela normal, a única diferença é que a fonte de dados é uma coleção, qualquer tipo baseado em TCollectionele vai carregar, desde que implementado corretamente.

6. Finalmente, persistir os dados no XML, que será a serialização, porque todos os dados contidos no dataset

Page 9 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 10: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

TCollectionDataSet serão automaticamente mapeados e gravados no XML, através da sua sintaxe e tag´s padrões de funcionamento.

Desenvolvi uma aplicação de exemplo, apenas pra mostrar como funciona este procedimento. Segue um print abaixo:

Figura 03 – Exemplo de implementação da classe TCollectionDataSet.

Page 10 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 11: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Acima vemos a utilização das classes TBlobMetaDatas, TXMLSerializer e TCollectionDataSet. Neste fonte é mostrado o processo de desserialização. O fonte do processo de serialização é mostrado abaixo:

Page 11 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 12: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

with TXMLSerializer.Create(nil) do begin XMLSettings.WellFormated := true; StorageOptions := [soIncludeObjectLinks, soSortProperties]; SpecialClasses := [scTCollection]; SaveObject(blobMetaDatas, 'BlobMeta'); SaveToFile('C:\test.xml'); end;

Com esta implementação, podemos carregar os dados no XML e salvar eles de volta. Na verdade o nosso THVDataSetfará a mesma coisa. Utilizando basicamente este mesmo processo, com algumas adaptações deste fonte acima parcialmente mostrado, nosso componente poderá se comunicar com o XML de persistência dos formatos blob. Segue abaixo os passos necessários para implementação do suporte a tipos Blob em uma classe descendente de TDataSet:

1. Sobreescrever a função AllocRecordBuffer: Esse método (função – retorno PChar), declarada virtual na classe TDataSet, é para as classes derivadas implementar; alocando memória pro buffer do registro. Nesta função sobreescrita pelo nosso componente é adicionada a seguinte função – AllocateBlobPointers – que tem a finalidade de alocar ponteiros que apontarão para outra área de memória – e esta outra área de memória que conterá os blobs – criando um objeto de TMemoryStream pra cada campo blob encontrado e  movendo um ponteiro pra esse TMemoryStream na posição (offset) do registro. E pra que isso? Porque o espaço pro buffer é fixo, enquanto que os blobs não possuem tamanho fixo – eles podem variar – o espaço alocado para um blob pode ser de qualquer tamanho, e por isso a ideia de utilizar ponteiros é muito melhor nesse sentido, pois eles só apontam pra memória desejada onde estão localizados os blobs;

2. Sobreescrever a função CreateBlobStream: Similar ao item 1, este método também deve ser implementado nas classes filhas (que descendem de TDataSet), para que nosso THVDataSet possa ler e gravar os dados no campo blobespecificado. Essa função tem esta finalidade, de prover esta interface de comunicação entre o stream e o dataset. Foi definido no THVDataSet que o stream descende de TMemoryStream, embora seja possível utilizar qualquer tipo de stream.

Basicamente é isso; o resto é a comunicação com o XML via serialização e desserialização pra obter os dados na forma de um dataset comum, localizando, recuperando, editando, inserindo e excluindo conteúdos blob desta forma. Toda lógica está lá implementada, não sendo necessária esta preocupação de manipular os XML´s manualmente – o próprio componente se responsabilizará de criar e gerenciar eles no momento apropriado – não sendo necessária a intervenção do desenvolvedor pra este propósito.

Page 12 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 13: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Figuras 04,05 e 06 – Telas de um outro exemplo, de visualização dos campos blob no modo tradicional (apenas mostrando o valor “(memo)” mas sem realmente mostrar mais nada), o conteúdo salvo no XML, e a visualização tratada pelo evento OnDrawColumnCell de um objeto de TDBGrid;

seguem as telas nesta ordem, respectivamente.

Vamos mostrar abaixo alguns códigos-fonte relacionados às últimas telas mostradas acima.

Page 13 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 14: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Conclusão

Esta foi apenas um esboço sobre o tema, digo isso porque ele é muito vasto; essa interação do THVDataSet com o XMLatravés das classes TXMLSerializer e TCollectionDataSet requer uma atenção “mais demorada” e aprofundada sobre este tema, explicando mais de perto os processos de transformação de dados (objetos) em XML e vice-versa (serialização/desserialização); o funcionamento da classe TCollectionDataSet; o funcionamento dos tipos TBlobMetaData e TBlobMetaDatas (baseados em TCollectionItem e TCollection, respectivamente) e mostrando todas estas estruturas comunicando entre si. O que damos foi um “overview”, mais à nível de introdução, embora mostramos principalmente nos prints um “resumo da ópera” de todo o processo.

Com isso o desenvolvedor terá um banco de dados que tenha suporte a blobs. Isso não se acha na internet facilmente –pelo menos não encontrei que seja free; apenas um, mas na verdade nem nos demos isso foi mostrado (a nível de gravação) e sim apenas chamando “loadfromfile” pra cada registro, de modo superficial e sem garantia de que funciona na persistência, na minha opinião.

Portanto, essa minha ideia não se achará em nenhum lugar na internet, este conceito de suporte a tipos blob via XML. É inovador e único. E freeware.

Bons estudos e até a próxima, que será a continuação, para suporte desta vez a tipos gráficos!!

Listando e excluindo elementos com o Ionic e o PochDB

Introdução

Page 14 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 15: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

No artigo anterior nós aprendemos como configurar o PouchDB em uma aplicação Ionic e como criar e atualizar um registro no banco. Nesta segunda parte você aprenderá como recuperar todos os registros do banco e armazená-los para um acesso mais rápido e também como realizar a exclusão de um elemento.

Promises

Antes de colocar a mão na massa é importante entender o conceito de promises, pois elas são os alicerces do assincronismo do JavaScript. Vamos imaginar que nossa lista de contatos tenha um milhão de contatos e você queria recuperar todos os registros de uma só vez. Em um sistema web ou desktop esse procedimento levaria alguns milissegundos e não afetaria a performance do sistema e do computador como um todo, mas por mais evoluído que estejam os smartphones atualmente, a sua performance ainda é limitada e esse processo pode demorar alguns segundos e fazendo com que o sistema operacional provavelmente feche o aplicativo ou exiba um alerta que ele parou de responder. É aí que entram as promises, que como o próprio nome sugere é uma promessa que alguma tarefa será executada em segundo plano e algum valor será retornado (mesmo que esse valor seja nulo).

Construindo um cache

Como você viu, não é uma opção ficar solicitando ao banco de dados a todo momento os nossos contatos, portanto nós iremos construir um sistema de cache que nada mais é que uma variável que irá armazenar todos os registros. Antes de iniciarmos você deve alterar o valor inicial da variável _contatos do nosso provider (a mudança está em negrito):

@Injectable()

export class ContatosProvider {

private _database;

private _contatos = null;

Feito a alteração vamos criar o método responsável por atualizar a nossas listagem de contatos sempre que algum registro for adicionado, removido ou alterado:

handleChange(change) {

// Parte 1

let contatoModificado = null;

let indiceContatoModificado = null;

// Parte 2

this._contatos.forEach((contato, indice) => {

Page 15 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 16: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

if(contato._id === change.id) {

contatoModificado = contato; indiceContatoModificado = indice;

}

});

// Parte 3

if(change.deleted) {

this._contatos.splice(indiceContatoModificado, 1);

} else {

// Parte 4

if(contatoModificado) {

this._contatos[indiceContatoModificado] = change.doc;

// Parte 5

} else {

this._contatos.push(change.doc);

}

}

}

Para manter o padrão recomendado, assim como nos outros métodos, iremos manter o nome em inglês.Logo no início do método (Parte 1) declaramos as variáveis que irão armazenar o contato que foi alterado e a posição que ele ocupa em nosso cache. Após isso iteramos sobre o cache (Parte 2) até encontrar o contato que deverá ser manipulado e armazenamos ele e a sua posição nas variáveis criadas anteriormente.O if(change.deleted)irá verificar se a propriedade deleted  foi adicionada ao contato (Parte 3) indicando que ele deve ser removido e caso exista irá remove-lo também de do cache. Caso contrário (Parte 4) ele verificará se o contato foi encontrado durante a iteração, caso positivo, significa que o contato será alterado, se não (Parte 5) ele deverá ser adicionado ao cache.

Criando o método getAll

O primeiro passo está pronto, vamos finalmente construir o método responsável por recuperar todos os registros do banco de dados:

Page 16 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 17: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

getAll() {

// Parte 1

if(this._contatos) {

return Promise.resolve(this._contatos);

}

// Parte 2

return new Promise(resolve => {

// Parte 3 this._database.allDocs({

include_docs: true

// Parte 4

}).then((result) => {

this._contatos = [];

let docs = result.rows.map((row) => {

this._contatos.push(row.doc);

});

// Parte 5

resolve(this._contatos);

// Parte 6

this._database.changes({

live: true,

since: 'now',

include_docs: true

// Parte 7

}).on('change', (change) => {

this.handleChange(change);

Page 17 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 18: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

})

// Parte 8

}).catch((error) => {

console.log(error);

});

});

}

Para entender melhor o método vamos por partes. A primeira parte é a responsável por verificar se o cache já existe, caso positivo ele retorna o cache como resultado. O método terá esse comportamento sempre que ele for chamado a partir da segunda vez, pois na primeira vez a variável que armazena o cache estará nula e o aplicativo irá continuar a processar o método criando o cache.A segunda parte é a responsável por criar uma promise que irá retornar os contatos quando o processamento dentro dela for concluído.Na terceira parte nós solicitamos os registros que são enviados para a quarta parte que irá extrair as informações e adicionar ao cache.A quinta parte é a responsável por “resolver” a promise, ou seja, enviar ao código que solicitou os contatos a informação vinda do banco de dados. É a partir daqui que o aplicativo irá fechar a mensagem de carregando que iremos implementar mais adiante e exibirá a listagem com os contatos.A partir da sexta etapa o código continua a ser processado sem travar a aplicação, assim o usuário pode utilizar a aplicação enquanto a promise continua a ser executada. A sexta parte é quem diz ao PouchDB que todas as modificações devem ser sincronizadas em tempo real (propriedade live) com o método informado na parte 7, ou seja, o método handleChange criado anteriormente. A parte 8 será executada se algo der errado durante o processo. No nosso caso irá exibir uma mensagem no console informando qual o erro.

Chamando o método getAll

A conexão com o banco já está pronta e todos os métodos necessários também, basta agora chama-los e para isso vamos alterar o construtor da nossa home para o código abaixo e implementar o método ionViewDidLoad que será executado quando a conexão já estiver ativa e todos os arquivos já estiverem carregados:

constructor(

public navCtrl: NavController,

private modalCtrl: ModalController,

public contatosProvider: ContatosProvider

) {

Page 18 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 19: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

}

ionViewDidLoad() {

this.contatosProvider.getAll().then((data) => {

this.contatos = data;

})

}

Você notou que existe um método then após a chamada do getAll? Isso é porque o método retorna um promise e o valor só será retornado quando ela for resolvida.

Criando uma mensagem de carregando

Se você testar a aplicação neste momento você verá que irá demorar alguns segundos para que os contatos apareçam na tela do celular (se você cadastrou alguns, é claro) e isso pode confundir o usuário, portanto vamos adicionar uma mensagem de carregando que irá impedir que o usuário utilize o sistema enquanto ele carrega as informações. Para isso vamos utilizar o LoadingController, um componente criado pela comunidade do Ionic para esse fim, mas antes será necessário importa-lo e instanciá-lo no construtor:

import { Component } from '@angular/core';

import { NavController, ModalController, LoadingController } from 'ionic-angular';

import { DetalhesPage } from '../detalhes/detalhes';

import { ContatosProvider } from '../../providers/contatos-provider';

...

constructor(

public navCtrl: NavController,

private modalCtrl: ModalController,

public contatosProvider: ContatosProvider,

public loadingCtrl: LoadingController

) {

}

Page 19 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 20: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

O próximo passo é exibir a mensagem quando a aplicação estiver carregada e remover quando as informações forem carregadas. Se você tiver alguma dúvida sobre o comportamento do componente ou quiser alterar qualquer característica, como a imagem de carregando, por exemplo, basta acessar a sua documentação no endereço https://ionicframework.com/docs/v2/api/components/loading/LoadingController/.

ionViewDidLoad() {

let loader = this.loadingCtrl.create({

content: "Carregando os contatos..."

});

loader.present();

this.contatosProvider.getAll().then((data) => {

this.contatos = data;

loader.dismiss();

}) }

Note que nós criamos uma variável definindo o texto que será exibido na mensagem e a exibimos através do método present. Para remover a mensagem utilizamos o método dismiss logo após que os contatos forem carregados.

Removendo um contato

Se você utilizar o botão remover e reiniciar o aplicativo verá que o contato ainda está lá, isso porque nós ainda não o removemos do banco. Vamos editar o nosso provider e criar o método para remover um contato do banco.

remove(contato) {

return this._database.remove(contato);

}

Simples, não? Assim como é feito para adicionar ou atualizar um contato basta uma linha para removê-lo. Agora precisamos atualizar o código que é chamado pelo botão em nossa home.

remove(contato) {

Page 20 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 21: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

this.contatosProvider.remove(contato);

}

Pronto! Agora nós temos uma aplicação funcional ligada a um banco de dados PouchDB!

Conclusão

Com este artigo nós finalmente terminamos nosso catálogo de contatos e agora possuímos uma aplicação completa e totalmente funcional. Espero que você tenha gostado e aprendido como conectar o Ionic 2 a um banco de dados NoSQL como o PouchDB. Essa é uma das tarefas mais complexas do Ionic, portanto não desista se algum erro surgir durante o desenvolvimento.

Sobre o autor

Ricardo Barbosa Crivelli, mais conhecido como Rico Crivelli, é formado como Bacharel em Sistemas de Informação e Licenciado em Computação pela Universidade Estadual do Norte do Paraná, atualmente é Técnico em TI no Instituto Federal de São Paulo – Câmpus Avaré. Tem como especialidade a linguagem PHP e o framework Symfony, apesar de adorar trabalhar com front-end e desenvolvimento mobile e possuir as certificações COBiT 4.1 Foundation e Delphi 2006 Developer.

E-mail para contato: [email protected]

Editorial

Caro amigo leitor,

É com grande satisfação que inicio mais um ano de nossa revista The Club. Que este ano de 2017 continuemos com esta maravilhosa parceria. Nossa equipe deseja um ano de muito sucesso, alegria, saúde e paz para você.

Para iniciar nossa série de artigos, nosso colaborador mensal Hamden Vogel escreveu o artigo "Manipulando arquivos Blob externamente - carregando pra um banco de dados descendente de TStream". Neste artigo ele irá implementar a classe “THVBlobStream”, que será instanciada sobreescrevendo a função “CreateBlobStream” (definida em TDataSet) utilizada para exibir conteúdos do tipo Blob, em especial aos formatos ftMemo e ftGraphic. Também demonstrará como renderizar via “DBGrid” um formato “txt” com quebra de linhas automática, dentre outros recursos contidos de dentro da classe THVDataSet." Já nosso outro colaborador Ricardo Barbosa Crivelli continua abordando assuntos sobre o tema “Ionic”, com o artigo “Listando e excluindo elementos com o Ionic e o PochDB”. Para este mês ele nos ensinará como recuperar todos os registros do banco e armazená-los para um acesso mais rápido e também como realizar a exclusão de um elemento. Para finalizar, nosso consultor técnico Thiago Cavalheiro Montebugnoli preparou o artigo “Android – Conhecendo e utilizando o CardView”. Neste mês ele descreveu os principais atributos, métodos e construtores deste importante Widget da plataforma Android. Também nos ensinou a realizar a importação desta Biblioteca junto com o desenvolvimento de exemplos práticos.

Desejo uma ótima leitura e um bom início de ano.

Abraços!

Sobre o Autor

Page 21 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx

Page 22: ILOH 9 5HVWULWR 5HYLVWDV UHYL DVS[ - The Club - O … · 2 SULPHLUR SDVVR SDUD UHDOL]DU D LPSRUWDomR VHULD D FULDomR GH XP QRYR SURMHWR QR $QGURLG 6WXGLR 3DUD LVWR FOLTXH HP ´)LOH

Hamden Vogel , Consultor TheClub

E-mail: [email protected]

Page 22 of 22Artigos

05/06/2017file:///V:/Restrito/Revistas/201701/revi1701.aspx