Olá povo,
A ERI, evento promovido pela Sociedade Brasileira da Computação, através da Secretaria Regional de Pernambuco, acontece nos dias 3 e 4 de Maio, no auditório do CTG/UFPE.
O Centro de Informática (CIn) da UFPE em parceria com a Secretaria da Regional de Pernambuco da Sociedade Brasileira de Computação (SBC) vai promover a I Escola Regional de Informática de Pernambuco (I ERI-PE). O evento traz à tona debates sobre os temas atuais na área de TIC, com palestras, minicursos e apresentação de trabalhos acadêmicos. As atividades serão apresentadas nos dias 3 e 4 de Maio, das 8h às 17h30, no auditório do Centro de Tecnologia e Geociências (CTG), no Campus da UFPE e serão abertas ao público.
Ao todo, 10 trabalhos acadêmicos de diversas instituições de ensino superior serão apresentados no ERI-PE. A lista está disponível para download aqui. Entre as universidades que vão participar, estão: a UFPE, UFRPE e UPE de Recife, UPE de Caruaru e UFRPE de Garanhuns. A programação pode ser vista aqui. Os interessados em participar do evento devem se inscrever aqui.
A Escola Regional de Informática da SBC já atua em outras regiões do país. A ideia é fazer edições anuais do evento, a fim de fomentar a pesquisa e a dinâmica entre indústria e academia. Nessa primeira edição em Pernambuco, já estão confirmadas as palestras do Professor José Gilson de Almeida Teixeira Filho, pesquisador da UPE/UFPE: “Melhores Práticas e Maturidade em Planejamento Estratégico de SI/TI”; do consultor Rodrigo Elia Assad, especialista em novas tecnologias e segurança: “Computação em Nuvem: Por que surgiu?” e a palestra do Professor Ricardo Alexandre Afonso, da Universidade Federal de Alagoas (UFAL), “Mercado de trabalho: Onde você estava quando o futuro passou?”. Além das palestras, já está confirmado o minicurso de “Desenvolvimento de aplicativos para smartphones e tablets Google Android”, ministrado pelo Mestre em Engenharia de Software pelo C.E.S.A.R., Nelson Glauber de Vasconcelos Leal.
Fonte: http://www2.cin.ufpe.br/site/lerNoticia.php?s=1&c=94&id=521
Os slides da minha apresentação estão disponíveis aqui.
4br4ç05,
nglauber
quinta-feira, 3 de maio de 2012
segunda-feira, 16 de abril de 2012
Animações no Android 3+
Olá povo,
Essa semana, meu colega Felipe Vasconcelos me lembrou de um problema existente na API de animações do Android 2 (que falei nesse post).
Digamos que você tenha um botão em uma posição x/y, e que esse botão seja posicionado em outro lugar através de uma animação (uma TranslateAnimation pra ser mais exato). Se você clicar nesse botão após a animação, você não conseguirá. Isso se deve a uma característica (ou bug?) da API de animação do Android até o 2.x, que só altera o aspecto da View, mas não seu conteúdo interno. Isso quer dizer que, apesar do botão mudar a posição visualmente, ele continua na posição antiga. Ou seja, se você clicar no botão na posição anterior à animação, o evento de clique ocorrerá.
Para corrigir esse problema, o Google introduziu uma nova API de animação no Android 3.0, que traz ainda o benefício de acompanhar cada passo da animação, coisa que não acontecia na antiga API.
Para entender melhor o problema e a nova API, vamos criar um exemplo bem simples. Abaixo temos o arquivo XML que define a tela da nossa aplicação:
Já no segundo método, estou usando a nova API de animações. A classe ValueAnimator é a chave dessa API, e ela tem basicamente: um valor inicial e final; um target, que é um objeto View; a duração em milissegundos; e um AnimatorUpdateListener. Esse último objeto é chamado N vezes para realizar a animação, onde N depende do valor final e sua respectiva duração. É nesse objeto que devemos alterar a propriedade da View que queremos animar, que no nosso caso, é a propriedade Y do botão.
No final do método vemos se o botão desceu, em caso positivo, solicitamos que a animação seja feita ao contrário com o método reverse; caso contrário, mandamos animar para a posição desejada.
Para observar o "bug" da antiga API de animação, clique no primeiro botão e ele descerá. Em seguida, clique no botão e você notará que nada acontecerá. Agora tente clicar onde o botão estava... Magicamente o botão voltará para a posição original.
Em resumo, se você precisar interagir com uma View que sofreu uma animação, ou ainda precisar obter informações internas sobre elas, você terá que contornar essa limitação da API antiga de animações. Mas se você estiver pensando em desenvolver para tablets Android ou ainda para os novos aparelhos com Android 4 ICS, utilize a nova API de animações.
4br4ç05,
nglauber
Essa semana, meu colega Felipe Vasconcelos me lembrou de um problema existente na API de animações do Android 2 (que falei nesse post).
Digamos que você tenha um botão em uma posição x/y, e que esse botão seja posicionado em outro lugar através de uma animação (uma TranslateAnimation pra ser mais exato). Se você clicar nesse botão após a animação, você não conseguirá. Isso se deve a uma característica (ou bug?) da API de animação do Android até o 2.x, que só altera o aspecto da View, mas não seu conteúdo interno. Isso quer dizer que, apesar do botão mudar a posição visualmente, ele continua na posição antiga. Ou seja, se você clicar no botão na posição anterior à animação, o evento de clique ocorrerá.
Para corrigir esse problema, o Google introduziu uma nova API de animação no Android 3.0, que traz ainda o benefício de acompanhar cada passo da animação, coisa que não acontecia na antiga API.
Para entender melhor o problema e a nova API, vamos criar um exemplo bem simples. Abaixo temos o arquivo XML que define a tela da nossa aplicação:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<Button
android:id="@+id/btnAnimacaoAntiga"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClickAnimarAntigo"
android:text="Animar 2.x" />
<Button
android:id="@+id/btnAnimarNova"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClickAnimarNovo"
android:text="Animar Honeycomb" />
</LinearLayout>
A tela é bem simples, apenas dois botões que serão animados até a posição 200 na coordenada Y. O primeiro animará utilizando a API antiga, e o segundo a nova. Abaixo, temos a Activity da aplicação:
public class AnimationHoneycombActivity
extends Activity {
private boolean b1Desceu;
private boolean b2Desceu;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onClickAnimarAntigo(View v){
int yInicial, yFinal;
if (b1Desceu){
yInicial = 200;
yFinal = 0;
} else {
yInicial = 0;
yFinal = 200;
}
TranslateAnimation animacao =
new TranslateAnimation(0, 0, yInicial, yFinal);
animacao.setDuration(2000);
animacao.setFillAfter(true);
v.startAnimation(animacao);
b1Desceu = !b1Desceu;
}
public void onClickAnimarNovo(final View v){
ValueAnimator animacao =
ValueAnimator.ofFloat(0, 200);
animacao.setTarget(v);
animacao.setDuration(2000);
animacao.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(
ValueAnimator animation) {
Float valor = (Float)
animation.getAnimatedValue();
v.setY(valor);
}
});
if (b2Desceu){
animacao.reverse();
} else {
animacao.start();
}
b2Desceu = !b2Desceu;
}
}
No primeiro método, utilizamos a API antiga de animações. Utilizei uma flag para determinar se o botão desceu, para determinar a posição inicial e final na coordenada Y. Em seguida, criei uma TranslateAnimation, defini a duração, e em seguida, com o método setFillAfter determinei que ao terminar a animação, o botão deve permanecer onde a animação terminou. Por fim, mandei animar o botão.Já no segundo método, estou usando a nova API de animações. A classe ValueAnimator é a chave dessa API, e ela tem basicamente: um valor inicial e final; um target, que é um objeto View; a duração em milissegundos; e um AnimatorUpdateListener. Esse último objeto é chamado N vezes para realizar a animação, onde N depende do valor final e sua respectiva duração. É nesse objeto que devemos alterar a propriedade da View que queremos animar, que no nosso caso, é a propriedade Y do botão.
No final do método vemos se o botão desceu, em caso positivo, solicitamos que a animação seja feita ao contrário com o método reverse; caso contrário, mandamos animar para a posição desejada.
Para observar o "bug" da antiga API de animação, clique no primeiro botão e ele descerá. Em seguida, clique no botão e você notará que nada acontecerá. Agora tente clicar onde o botão estava... Magicamente o botão voltará para a posição original.
Em resumo, se você precisar interagir com uma View que sofreu uma animação, ou ainda precisar obter informações internas sobre elas, você terá que contornar essa limitação da API antiga de animações. Mas se você estiver pensando em desenvolver para tablets Android ou ainda para os novos aparelhos com Android 4 ICS, utilize a nova API de animações.
4br4ç05,
nglauber
sábado, 14 de abril de 2012
iOS: Lendo XML
Olá povo,
Neste post vou mostrar como ler arquivos XML em aplicações iOS. O XML que estou lendo, eu baixei no site do livro Google Android do Ricardo Lecheta (eu tento, mas não deixo o Android :)
O modelo do estrutura do XML que iremos ler é apresentado abaixo:

Como você deve ter observado, o carro descrito no XML tem os atributos nome, desc, url_info e url_foto. Vamos criar uma classe que representará esse objeto em código.
Vamos agora a implementação da tela (lembrando que só estamos mostrando os métodos alterados):
Feito isso, precisamos fazer alguns ajustes no NIB. Abra o NGViewController.xib e remova a UIView e adicione uma UITableViewController. Ligue esse componente ao Outlet da propriedade view, e o dataSource e delegate.
Além disso, alterei também a propriedade "Top Bar" para "Navigation Bar". Feito isso, podemos rodar nossa aplicação. O resultado é apresentado abaixo.
Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber
Neste post vou mostrar como ler arquivos XML em aplicações iOS. O XML que estou lendo, eu baixei no site do livro Google Android do Ricardo Lecheta (eu tento, mas não deixo o Android :)
O modelo do estrutura do XML que iremos ler é apresentado abaixo:
<?xml version="1.0" encoding="utf-8"?>
<carros>
<carro>
<nome>Modelo do Carro</nome>
<desc>Descrição do Carro</desc>
<url_info>http://www.ferrari.com/</url_info>
<url_foto>http://www.livroandroid.com.br/</url_foto>
</carro>
...
</carros>
Crie um novo projeto no Xcode utilizando o template "Single View Application". Agora acesse o site do livro e baixe o XML, em seguida adicione-o ao seu projeto no Xcode na pasta "Supporting Files". A estrutura do projeto (no final) deve ficar conforme abaixo:
Como você deve ter observado, o carro descrito no XML tem os atributos nome, desc, url_info e url_foto. Vamos criar uma classe que representará esse objeto em código.
// Carro.h --------------------------------------- #import <Foundation/Foundation.h> @interface Carro : NSObject @property (strong, nonatomic) UIImage *image; @property (strong, nonatomic) NSString *nome; @property (strong, nonatomic) NSString *descricao; @property (strong, nonatomic) NSString *urlInfo; @property (strong, nonatomic) NSString *urlFoto; @end // Carro.m --------------------------------------- #import "Carro.h" @implementation Carro @synthesize nome, descricao, urlFoto, urlInfo, image; @endVou alterar o AppDelegate da aplicação para ficar desta forma:
// NGAppDelegate.h -------------------------------
#import <UIKit/UIKit.h>
@class NGViewController;
@interface NGAppDelegate :
UIResponder <UIApplicationDelegate>
@property (strong, nonatomic)
UIWindow *window;
@property (strong, nonatomic)
UINavigationController *navigationController;
@end
// NGAppDelegate.m -------------------------------
#import "NGAppDelegate.h"
#import "NGViewController.h"
@implementation NGAppDelegate
@synthesize window = _window;
@synthesize navigationController;
- (BOOL)application:
(UIApplication *)application
didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions{
self.window = [[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]];
NGViewController *viewController =
[[NGViewController alloc] initWithNibName:
@"NGViewController" bundle:nil];
navigationController =
[[UINavigationController alloc]
initWithRootViewController:viewController];
self.window.rootViewController =
self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
// Outros métodos não alterados
@end
Nesse template, a aplicação é criada com uma UIViewController simples. Vamos utilizar uma UITableViewController que listará os dados lidos através de um objeto da class NSXMLParser. Sua utilização é bem similar ao SAX do Java, e teremos que implementar um protocolo NSXMLParserDelegate, e a medida que as tags são lidas os métodos desse protocolo são chamados.
#import <UIKit/UIKit.h>
#import "Carro.h"
@interface NGViewController : UITableViewController
<NSXMLParserDelegate>{
NSMutableArray *carros;
Carro *carro;
NSString *currTag;
}
@end
Um NSMutableArray armazenará os objetos Carro criados a partir da leitura do XML. Durante o parser do XML, precisamos saber que tag está sendo lida, para tal setarmos o atributo currTag com tag que corresponde a propriedade da classe Carro. Por exemplo, quando o parser encontrar uma tag "carro", criamos um objeto Carro, já quando encontramos uma tag "nome", devemos definir o a propriedade nome do objeto carro. Por fim, ao encontrar a tag "/carro" devemos adicionar o objeto carro na lista.Vamos agora a implementação da tela (lembrando que só estamos mostrando os métodos alterados):
#import "NGViewController.h"
#import "Carro.h"
@implementation NGViewController
- (void)viewDidLoad{
[super viewDidLoad];
self.title = @"Carros";
carros = [[NSMutableArray alloc] init];
// Pega o caminho do arquivo XML
NSString *xmlPath = [[NSBundle mainBundle]
pathForResource:@"carros" ofType:@"xml"];
// Obtém os bytes do arquivo
NSData *xmlData =
[NSData dataWithContentsOfFile:xmlPath];
// Cria o objeto que fará o parser do XML
NSXMLParser *xmlParser =
[[NSXMLParser alloc] initWithData:xmlData];
[xmlParser setDelegate:self];
[xmlParser parse];
}
// Métodos chamado quando NSXMLParser
// inicia a leitura de uma TAG
-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
currTag = elementName;
// Se achou a TAG carro, crie um novo objeto
if ([currTag compare:@"carro"] == NSOrderedSame){
carro = [[Carro alloc] init];
}
}
// Método chamado quando vai ler o
// conteúdo da TAG
-(void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string {
if ([currTag compare:@"nome"] ==
NSOrderedSame && carro.nome == nil){
carro.nome = string;
} else if ([currTag compare:@"desc"] ==
NSOrderedSame && carro.descricao == nil){
carro.descricao = [string
stringByTrimmingCharactersInSet:
[NSCharacterSet
whitespaceAndNewlineCharacterSet]];
} else if ([currTag compare:@"url_info"] ==
NSOrderedSame && carro.urlInfo == nil){
carro.urlInfo = [string
stringByTrimmingCharactersInSet:
[NSCharacterSet
whitespaceAndNewlineCharacterSet]];
} else if ([currTag compare:@"url_foto"] ==
NSOrderedSame && carro.urlFoto == nil){
carro.urlFoto = [string
stringByTrimmingCharactersInSet:
[NSCharacterSet
whitespaceAndNewlineCharacterSet]];
}
}
// Método chamado quando termina de ler uma tag
-(void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
// Se a tag lida for "carro", adiciona à lista
if ([elementName compare:@"carro"]==NSOrderedSame){
[carros addObject:carro];
}
}
// Método de UITableViewController
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [carros count];
}
-(NSInteger)numberOfSectionsInTableView:
(UITableView *)tableView {
return 1;
}
-(UITableViewCell *)tableView:(UITableView *)
tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
Carro *_carro = [carros objectAtIndex:indexPath.row];
cell.textLabel.text = _carro.nome;
cell.detailTextLabel.text = _carro.urlInfo;
return cell;
}
-(void)tableView:(UITableView *)tableView
didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
// Obtém o carro da linha selecionada
Carro *_carro = [carros objectAtIndex:indexPath.row];
// Abre o site do carro selecionado
NSURL *url = [NSURL URLWithString:_carro.urlInfo];
[[UIApplication sharedApplication] openURL:url];
}
@end
O código acima está comentado e parte relacionada com a UITableViwController foi detalhada nesse post aqui.Feito isso, precisamos fazer alguns ajustes no NIB. Abra o NGViewController.xib e remova a UIView e adicione uma UITableViewController. Ligue esse componente ao Outlet da propriedade view, e o dataSource e delegate.


4br4ç05,
nglauber
domingo, 8 de abril de 2012
Artigo "Android 4: Ice Cream Sandwich" (parte 2)
Olá povo,
A revista Java Magazine, traz em sua edição de número 102, a segunda (e última) parte da matéria sobre a nova versão da plataforma Android. O texto foi escrito por mim e pelo meu colega Bruno Vinícius.
Nesta segunda parte do artigo, apresentamos mais recursos da nova versão do Android. Abordamos a integração com serviços de agendamento de tarefas, novos componentes de interface gráfica e a vasta gama de possibilidades multimídia e de comunicação trazidas por essa nova versão da plataforma.
Espero que vocês gostem.
4br4ç05,
nglauber
A revista Java Magazine, traz em sua edição de número 102, a segunda (e última) parte da matéria sobre a nova versão da plataforma Android. O texto foi escrito por mim e pelo meu colega Bruno Vinícius.
Nesta segunda parte do artigo, apresentamos mais recursos da nova versão do Android. Abordamos a integração com serviços de agendamento de tarefas, novos componentes de interface gráfica e a vasta gama de possibilidades multimídia e de comunicação trazidas por essa nova versão da plataforma.
Espero que vocês gostem.
4br4ç05,
nglauber
terça-feira, 3 de abril de 2012
iOS: Internacionalização
Olá povo,
Espero que vocês gostem. Qualquer dúvida, deixem seus comentários.
Aqui vai o primeiro vídeo-post aqui do blog: Internacionalização no iOS.
Espero que vocês gostem. Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber
sábado, 31 de março de 2012
iOS: UITableView
Olá povo,
Depois de estudar por algumas vezes a programação para a plataforma iOS, resolvi escrever uma série de posts sobre o assunto. Como eu já fiz um "Hello World" e um exemplo básico de tratamento de evento (aqui e aqui respectivamente) vou começar falando do componente UITableView. Esse componente serve para exibir informações em uma lista.
Vamos começar criando um novo projeto no XCode. Selecione File > New > New Project... Na janela que for exibida, selecione Empty Application. Depois preencha os campos conforme a imagem abaixo.
Clique em Next e depois selecione em que diretório deseja salvar o projeto e clique em Create. Será criado um projeto vazio com a estrutura mostrada abaixo:
Neste momento o projeto não faz nada, então vamos criar a primeira tela que exibirá uma lista de nomes. Para tal, clique com o botão direito sobre o projeto e selecione New File...
Selecione UIViewController subclass e clique em Next. No campo Class, preencha com ListagemViewController e em Subclass of, coloque UITableViewController. Clique em Next e em seguida, Create.
Criada a classe que representará a primeira tela da aplicação vamos alterar o NGAppDelegate para instanciar nossa tela. No arquivo .h adicione a propriedade do tipo UINavigationController. Ela servirá para abrirmos uma outra tela e já controlar o fluxo entre elas.
Neste ponto, você já pode mandar rodar a aplicação, mas não teremos nada para listar. Então vamos a implementação da nossa listagem. No arquivo ListagemViewController.h, declare um array chamado nomes, conforme abaixo:
O último método que alteramos foi o tableView:cellForRowAtIndexPath, ele cria um objeto UITableViewCell para cada item da lista. Para evitar a criação de muitos objetos ele tenta reaproveitar linhas que não estejam mais visíveis na tela. Isso é feito no método dequeueReusableCellWithIdentifier:CellIdentifier. Caso não haja uma linha pra reciclar, criamos uma UITableViewCell com o estilo padrão (UITableViewCellStyleDefault). Em seguida, alteramos o texto da UITableViewCell (que internamente contém um UILabel) utilizando o nosso array de nomes. Para obter a posição a ser exibida, utilizamos a propriedade row o parâmetro indexPath. Pronto! Basta rodar nossa aplicação, e o resultado deverá ficar como abaixo:
Vamos criar uma tela para exibir o item selecionado. Botão direito no projeto, New File... Selecione UIViewController subclass (como fizemos anteriormente) e clique em Next. O nome da classe será DetalheViewController e será subclasse de UIViewController. Marque a opção With XIB for user interface. Clique em Next e depois em Create.
Abra o DetalheViewController.xib e arraste um Label para a tela e faça os ajustes de posicionamento e tamanho que desejar. Em seguida, vamos criar o IBOutlet para esse label: clique com o botão direito sobre o botão, e em Referencing Outlets clique em New Referencing Outlet e arraste para o arquivo .h.
Será exibido um popup para preencher o nome do nosso Outlet. Preencha com txtDetalhe e clique em Connect. Em seguida, vou adicionar a propriedade "texto" para essa tela que será atribuída pela tela de listagem. Quando clicarmos em um item da lista, criaremos uma instância de DetalheViewController a atribuiremos essa propriedade com o item selecionado da lista. O arquivos .h e .m deverão ficar como abaixo (lembrando só listamos o que foi modificado).
Podemos notar que o navigation controller já coloca um botão para voltarmos para a tela anterior.
Em breve devo colocar mais posts sobre iOS. Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber
Depois de estudar por algumas vezes a programação para a plataforma iOS, resolvi escrever uma série de posts sobre o assunto. Como eu já fiz um "Hello World" e um exemplo básico de tratamento de evento (aqui e aqui respectivamente) vou começar falando do componente UITableView. Esse componente serve para exibir informações em uma lista.
Vamos começar criando um novo projeto no XCode. Selecione File > New > New Project... Na janela que for exibida, selecione Empty Application. Depois preencha os campos conforme a imagem abaixo.



#import <UIKit/UIKit.h> @interface NGAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) UINavigationController *navegador; @endNo arquivo .m já foram criados alguns métodos pelo template do XCode, mas só vamos mexer no application:didFinishLaunchingWithOptions.
#import "NGAppDelegate.h"
#import "ListagemViewController.h"
@implementation NGAppDelegate
@synthesize window = _window;
@synthesize navegador;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)opts
{
self.window = [[UIWindow alloc] initWithFrame:[
[UIScreen mainScreen] bounds]];
ListagemViewController *lista =
[[ListagemViewController alloc] init];
navegador = [[UINavigationController alloc]
initWithRootViewController:lista];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = navegador;
[self.window makeKeyAndVisible];
return YES;
}
Nesse método instanciamos a nossa tela, e em seguida instanciamos o UiNavigationController passando a nossa tela como tela "raiz", ou seja, a principal. Depois associamos o UINavigationController ao objeto UIWindow, que representa a tela do aparelho.Neste ponto, você já pode mandar rodar a aplicação, mas não teremos nada para listar. Então vamos a implementação da nossa listagem. No arquivo ListagemViewController.h, declare um array chamado nomes, conforme abaixo:
#import <UIKit/UIKit.h>
@interface ListagemViewController :
UITableViewController {
NSArray *nomes;
}
@end
Como podemos observar, nossa classe herda de UITableViewController. Essa classe implementa dois protocolos (que em Java são interfaces) UITableViewDelegate e UITableViewDataSource. O primeiro trata de eventos disparados pela lista e o segundo define métodos que irão prover informações para a lista.
Ao abrirmos o arquivo, podemos notar que temos vários métodos implementados, por isso só vou colocar no código abaixo os métodos que teremos que modificar.
#import "ListagemViewController.h"
@implementation ListagemViewController
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = @"Listagem";
nomes = [NSArray arrayWithObjects:
@"Nelson", @"Glauber",
@"Vasconcelos", @"Leal", nil];
}
// ... um monte de métodos :)
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:
(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return nomes.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.textLabel.text =
[nomes objectAtIndex:indexPath.row];
return cell;
}
@end
No método viewDidLoad, inicializamos nossa lista de nomes e alteramos o título da tela. Já no método numberOfSectionsInTableView retornamos a quantidade de sessões que a lista terá. As sessões servem para agrupar opções de uma lista, mas no nosso caso, teremos apenas uma. No método tableView:numberOfRowsInSection retornamos quantas linhas têm cada sessão, como só temos uma, retornamos a quantidade de itens do nosso array de pessoas.O último método que alteramos foi o tableView:cellForRowAtIndexPath, ele cria um objeto UITableViewCell para cada item da lista. Para evitar a criação de muitos objetos ele tenta reaproveitar linhas que não estejam mais visíveis na tela. Isso é feito no método dequeueReusableCellWithIdentifier:CellIdentifier. Caso não haja uma linha pra reciclar, criamos uma UITableViewCell com o estilo padrão (UITableViewCellStyleDefault). Em seguida, alteramos o texto da UITableViewCell (que internamente contém um UILabel) utilizando o nosso array de nomes. Para obter a posição a ser exibida, utilizamos a propriedade row o parâmetro indexPath. Pronto! Basta rodar nossa aplicação, e o resultado deverá ficar como abaixo:

Abra o DetalheViewController.xib e arraste um Label para a tela e faça os ajustes de posicionamento e tamanho que desejar. Em seguida, vamos criar o IBOutlet para esse label: clique com o botão direito sobre o botão, e em Referencing Outlets clique em New Referencing Outlet e arraste para o arquivo .h.

#import <UIKit/UIKit.h> @interface DetalheViewController : UIViewController @property (weak, nonatomic) IBOutlet UILabel *txtDetalhe; @property (strong, nonatomic) NSString *texto; @end
#import "DetalheViewController.h"
@implementation DetalheViewController
@synthesize txtDetalhe, texto;
- (void)viewDidLoad
{
[super viewDidLoad];
txtDetalhe.text = texto;
}
Estamos quase lá. Agora volte ao arquivo ListagemController.m implemente o método tableView:didSelectRowAtIndexPath que é o método chamado quando clicamos em um item da lista.
// Adicionar import no começo do arquivo
#import "DetalheViewController.h"
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetalheViewController *detailViewController =
[[DetalheViewController alloc] initWithNibName:
@"DetalheViewController" bundle:nil];
detailViewController.texto =
[nomes objectAtIndex:indexPath.row];
[self.navigationController pushViewController:
detailViewController animated:YES];
}
O método acima instancia nosso DetalheViewController passando o arquivo *.xib (sem a extensão), em seguida atribui a propriedade texto e utiliza o navigationController (que criamos no AppDelegate) para exibir a tela. O resultado pode ser visto abaixo:

sexta-feira, 23 de março de 2012
Java Custom Annotations
Olá povo,
Depois de um bom tempo sem postar, vou falar um pouquinho sobre anotações em Java. Quem já programa em Java, já deve ter visto algo como @Override, @Deprecated, @SupressWarnings, ou algo do tipo. Estas são anotações adicionadas ao código-fonte que podem ter funcionalidade meramente indicativa ou auxiliar o compilador Java. Mas um recurso bacana das anotações é a capacidade de utilizá-las em tempo de execução para ajudar na lógica do sistema, permitindo inclusive criar bibliotecas reutilizáveis.
Um framework muito popular que é construído baseado em anotações é o Hibernate (que implementa a especificação JPA, Java Persistence API). Ele permite a persistência de qualquer tipo de objeto, desde que as classes desses objetos estejam com as devidas anotações.
Para ilustrar o uso das anotações, vou criar um mini-mini-mini Hibertnate :) Nele definiremos duas anotações: uma para determinar qual tabela a classe vai persistir seus objetos; e a segunda mapeará um atributo da classe a um campo da tabela.
Vamos começar pelas anotações:
A diferença básica de uma interface comum e uma annotation é o símbolo de @ (arroba) antes da palavra interface. Os "atributos" da anotação são métodos, que no exemplo acima é o nome da tabela no banco. As regras para os atributos/métodos é que eles só podem retornar: primitivos, Strings, enum, Class ou um array dos anteriores.
A anotação @Target indica para qual tipo de elemento java a anotação que estamos definindo irá tratar. Ele pode ser:
TYPE: para classes, interfaces, ou enums;
FIELD: para atributos;
METHOD: para métodos;
PARAMETER: para parâmetros de métodos;
CONSTRUCTOR: em construtores;
LOCAL_VARIABLE: em variáveis locais;
ANNOTATION_TYPE: em uma outra anotação;
PACKAGE: nos pacotes java.
Já a anotação @Retention indica por quanto tempo a anotação será retida. Pode assumir os valores:
RUNTIME: as anotações são armazenadas na classe e estão disponíveis em tempo de execução;
SOURCE: as anotações são armazenadas na classe, mas NÃO estão disponíveis em tempo de execução;
CLASS: as anotações NÃO são armazenadas na classe (default).
Por último, a anotação @Inherited indica que as subclasses herdarão essa anotação.
A anotação acima servirá para mapear uma classe em uma tabela do banco de dados.
Ela será utilizada apenas em classes, ficará disponível em tempo de execução e poderá ser herdada por subclasses que a usarem.
Vamos agora para a anotação que mapeará atributos em campos da tabela:
A anotação acima, é apenas para atributos, terá sua informação disponível em tempo de execução e terá o atributo column. Definimos um valor default para a coluna, para que o nome do atributo seja igual ao nome do campo da tabela, não seja preciso preencher.
Vamos agora usar essas anotações em duas classes: Pessoa e Funcionário. Essa segunda herdando da primeira:
O mais importante dessa classe é o mapeamento que fizemos da classe com o nome da tabela e dos atributos com os campos. Tudo isso só usando nossas próprias anotações.
Agora a classe Funcionario:
Agora vamos criar a classe que lerá objetos (de qualquer classe) e procurará nossas anotações para fazer uma inclusão (FAKE claro :) no banco de dados.
O código acima está todo comentado. Qualquer dúvida, deixem seus comentários... Ah mas ainda tem a classe que usa isso tudo :)
O resultado é apresentado abaixo:
SQL---->INSERT INTO TBPessoa values(str_nome='Nelson',idade='28',str_end='Rua tal')
SQL---->INSERT INTO TBFunc values(str_nome='Glauber',idade='18',str_end='Rua x',num_ctps='1234')
Como podemos observar, o mesmo repositório está persistindo duas classes distintas, baseando-se apenas nas notações que usamos nelas. Esse recurso pode trazer inúmeras vantagens para o desenvolvimento de aplicações facilitando a componentização da sua aplicação.
4br4ç05,
nglauber
PS.: Me baseei nesse post.
Depois de um bom tempo sem postar, vou falar um pouquinho sobre anotações em Java. Quem já programa em Java, já deve ter visto algo como @Override, @Deprecated, @SupressWarnings, ou algo do tipo. Estas são anotações adicionadas ao código-fonte que podem ter funcionalidade meramente indicativa ou auxiliar o compilador Java. Mas um recurso bacana das anotações é a capacidade de utilizá-las em tempo de execução para ajudar na lógica do sistema, permitindo inclusive criar bibliotecas reutilizáveis.
Um framework muito popular que é construído baseado em anotações é o Hibernate (que implementa a especificação JPA, Java Persistence API). Ele permite a persistência de qualquer tipo de objeto, desde que as classes desses objetos estejam com as devidas anotações.
Para ilustrar o uso das anotações, vou criar um mini-mini-mini Hibertnate :) Nele definiremos duas anotações: uma para determinar qual tabela a classe vai persistir seus objetos; e a segunda mapeará um atributo da classe a um campo da tabela.
Vamos começar pelas anotações:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DBTable {
public String table();
}
A diferença básica de uma interface comum e uma annotation é o símbolo de @ (arroba) antes da palavra interface. Os "atributos" da anotação são métodos, que no exemplo acima é o nome da tabela no banco. As regras para os atributos/métodos é que eles só podem retornar: primitivos, Strings, enum, Class ou um array dos anteriores.
A anotação @Target indica para qual tipo de elemento java a anotação que estamos definindo irá tratar. Ele pode ser:
TYPE: para classes, interfaces, ou enums;
FIELD: para atributos;
METHOD: para métodos;
PARAMETER: para parâmetros de métodos;
CONSTRUCTOR: em construtores;
LOCAL_VARIABLE: em variáveis locais;
ANNOTATION_TYPE: em uma outra anotação;
PACKAGE: nos pacotes java.
Já a anotação @Retention indica por quanto tempo a anotação será retida. Pode assumir os valores:
RUNTIME: as anotações são armazenadas na classe e estão disponíveis em tempo de execução;
SOURCE: as anotações são armazenadas na classe, mas NÃO estão disponíveis em tempo de execução;
CLASS: as anotações NÃO são armazenadas na classe (default).
Por último, a anotação @Inherited indica que as subclasses herdarão essa anotação.
A anotação acima servirá para mapear uma classe em uma tabela do banco de dados.
Ela será utilizada apenas em classes, ficará disponível em tempo de execução e poderá ser herdada por subclasses que a usarem.
Vamos agora para a anotação que mapeará atributos em campos da tabela:
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBField {
String colummn() default "";
}
A anotação acima, é apenas para atributos, terá sua informação disponível em tempo de execução e terá o atributo column. Definimos um valor default para a coluna, para que o nome do atributo seja igual ao nome do campo da tabela, não seja preciso preencher.
Vamos agora usar essas anotações em duas classes: Pessoa e Funcionário. Essa segunda herdando da primeira:
@DBTable(table="TBPessoa")
public class Pessoa {
@DBField(colummn="str_nome")
private String nome;
@DBField(colummn="str_end")
private String endereco;
@DBField
private int idade;
public Pessoa(String nome,
String endereco, int idade) {
this.nome = nome;
this.endereco = endereco;
this.idade = idade;
}
public String getNome() {
return nome;
}
public String getEndereco() {
return endereco;
}
public int getIdade() {
return idade;
}
}
O mais importante dessa classe é o mapeamento que fizemos da classe com o nome da tabela e dos atributos com os campos. Tudo isso só usando nossas próprias anotações.
Agora a classe Funcionario:
@DBTable(table="TBFunc")
public class Funcionario extends Pessoa {
@DBField(colummn="num_ctps")
String ctps;
public Funcionario(String nome, String endereco,
int idade, String ctps) {
super(nome, endereco, idade);
this.ctps = ctps;
}
public String getCtps() {
return ctps;
}
}
Agora vamos criar a classe que lerá objetos (de qualquer classe) e procurará nossas anotações para fazer uma inclusão (FAKE claro :) no banco de dados.
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Set;
public class Repositorio {
public void insert(Object obj) throws Throwable {
// Tenta obter nossa anotação na classe
DBTable persistable =
obj.getClass().getAnnotation(DBTable.class);
// Se tiver a anotação...
if (persistable != null){
String tabela = persistable.table();
// Map para montar o SQL campo/valor :)
HashMap<String, String> keyAndValues =
new HashMap<String, String>();
// Obtém os atributos da classe via reflection
Field[] fields = getFields(obj.getClass());
for (Field field : fields) {
// como os atributos são private,
// setamos ele como visible
field.setAccessible(true);
DBField coluna =
field.getAnnotation(DBField.class);
// Se o atributo tem a anotação
if (coluna != null){
// Verifica se está vazio pra usar o
// nome do próprio atributo
String columnName =
coluna.colummn().equals("") ?
field.getName() : coluna.colummn();
// Adiciona campo/valor no map
keyAndValues.put(
columnName, field.get(obj).toString());
}
}
// Varre o map para montar o SQL
String values = "";
Set<String> keys = keyAndValues.keySet();
for (String campo: keys) {
if (!values.equals("")) values += ",";
String valor = keyAndValues.get(campo);
values += campo +"='"+ valor +"'";
}
String sql = "INSERT INTO "+ tabela +
" values("+ values +")";
System.out.println("SQL---->"+ sql);
}
}
// Método recursivo para obter os atributos da
// class e da superclasse
public Field[] getFields(Class c){
// Se tem superclasse
if (c.getSuperclass() != null){
// Chama o próprio método para pegar os
// atributos da superclasse
Field[] superClassFields =
getFields(c.getSuperclass());
// Pega os atributos da própria classe
Field[] thisFields = c.getDeclaredFields();
// array com todos os atributos
Field[] allFields = new Field[
superClassFields.length +
thisFields.length];
// Copia os atributos da superclasse
System.arraycopy(superClassFields, 0,
allFields, 0, superClassFields.length);
// Copia os atributos da classe atual
System.arraycopy(thisFields, 0, allFields,
superClassFields.length, thisFields.length);
return allFields;
// Se não tem superclasse, retorna os
// próprios atributos
} else {
return c.getDeclaredFields();
}
}
}
O código acima está todo comentado. Qualquer dúvida, deixem seus comentários... Ah mas ainda tem a classe que usa isso tudo :)
public class Main {
public static void main(String[] args) {
Repositorio repo = new Repositorio();
Pessoa p = new Pessoa("Nelson", "Rua tal", 28);
Funcionario f = new Funcionario(
"Glauber", "Rua x", 18, "1234");
try {
repo.insert(p);
repo.insert(f);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
O resultado é apresentado abaixo:
SQL---->INSERT INTO TBPessoa values(str_nome='Nelson',idade='28',str_end='Rua tal')
SQL---->INSERT INTO TBFunc values(str_nome='Glauber',idade='18',str_end='Rua x',num_ctps='1234')
Como podemos observar, o mesmo repositório está persistindo duas classes distintas, baseando-se apenas nas notações que usamos nelas. Esse recurso pode trazer inúmeras vantagens para o desenvolvimento de aplicações facilitando a componentização da sua aplicação.
4br4ç05,
nglauber
PS.: Me baseei nesse post.
Assinar:
Postagens (Atom)

