Mostrando postagens com marcador XML. Mostrar todas as postagens
Mostrando postagens com marcador XML. Mostrar todas as postagens

segunda-feira, 4 de junho de 2012

Android: Dicas 7

Olá povo,

Depois de um bom tempo, um novo post da série "Dicas de Android". Espero que gostem ;)

1. Aplicando um background para toda a aplicação

Primeiro precisamos criar uma extensão do estilo que está sendo utilizado na aplicação. Você pode fazer isso criando o arquivo styles.xml na pasta values e deixando-o conforme abaixo:
<resources>
  <style name="MeuTemaPersonalizado"
  android:parent="@android:style/Theme.Black.NoTitleBar">
  <item name="android:windowBackground">
    @drawable/sua_imagem_de_bg
  </item>
  </style>
</resources>

Depois, é só utilizar o estilo criado na tag
<application
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/MeuTemaPersonalizado">

2. Deixando os nomes dos parâmetros corretos no Eclipse

No Eclipse, clique com o botão direito sobre o projeto e selecione Properties. No lado esquerdo, selecione Java Build Path, e em seguida, selecione a aba Libraries. Aqui você deve expandir a versão do Android que você está utilizando (2.3.3 por exemplo), e depois expandir o android.jar. Serão exibidos alguns subitens, entre eles, o Javadoc location. Basta clicar em Edit... e informar o local onde está a documentação do Android (até a pasta docs/reference). Depois é só clicar em Validate e pronto! Agora o Javadoc aparecerá no editor e nas classes/interfaces do Android que você herdar/implementar.


3. Bloqueando a Home key
Desde sempre dizia que não dava para bloquear a Home até que meu colega Rodrigo Barbosa precisou fazer isso e resolveu compartilhar aqui no blog. Basta colocar o método abaixo na sua Activity.
@Override 
public void onAttachedToWindow() {
  this.getWindow().setType(
    WindowManager.LayoutParams.TYPE_KEYGUARD); 
  super.onAttachedToWindow(); 
}

4. Lendo XML com a Biblioteca Simple XML 
Apesar de ser adepto do "Do it yourself", algumas pessoas preferem as facilidades das bibliotecas. Recebi essa dica de Rafael Cavalcanti, que me indicou a Simple XML Serialization que facilita a leitura de arquivos XML. Ele converte automaticamente os elementos do arquivo XML em Objetos e Atributos de uma classe utilizando apenas anotações.
Abaixo um tutorial no próprio site do desenvolvedor:
http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php

Mas para não deixar de mostrar alguma coisa aqui... Baixe o JAR da biblioteca e adicione ao Build Path do projeto.
@Root
class Carros {
  @ElementList(inline=true)
  public  List<Carro> list;
}

@Root
class Carro {
  @Element
  public String nome;
  @Element
  public String desc;
  @Element
  public String url_info;
  @Element
  public String url_foto;
}

Para ler esse XML do livro do Ricardo Lecheta era só fazer isso...

URL url = new URL(
  "http://livroandroid.com.br/"+
  "livro/carros/carros_esportivos.xml");

HttpURLConnection conexao = (HttpURLConnection) 
  url.openConnection();

Serializer serializer = new Persister();
Carros example = serializer.read(
  Carros.class, conexao.getInputStream());

for (Carro carro : example.list) {
  Log.d("NGVL", "Carro:" + carro.nome);
}


5. Personalizando Links do Android
A classe TextView tem a propriedade AutoLink (android:autoLink) que cria detecta links automaticamente no texto que está sendo exibido. Esses links podem abrir o navegador, cliente de email e o discador do aparelho. Mas e se quisermos fazer um link para abrir uma Activity nossa?
A classe Linkify permite criar links baseados em uma expressão regular.
TextView txt1 = (TextView)findViewById(R.id.textView1);
txt1.setText("Testando esse CEP 55555-4444");
// Zip code dos EUA
Pattern pattern = Pattern.compile(
  "\\d{5}([\\-]\\d{4})?");

String scheme = "glauber://";
Linkify.addLinks(txt1, pattern, scheme);
Notem que foi passado o esquema glauber:// Para fazer com que uma Activity nossa responda a chamadas a esse "protocolo" é só adicionar uma na declaração da Activity no AndroidManifest.xml
<activity
  android:name="ActivityQueSeraChamada"
  android:label="@string/app_name" >
  <intent-filter>            
    <action 
       android:name="android.intent.action.VIEW"/>
    <category 
      android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="glauber"/>
  </intent-filter>
</activity>
Para obter o valor do link clicado, basta usar getIntent().getDataString(). Isso retornará "glauber://55555-4444"

6. TableLayout e a propriedade layout_span
Estava querendo fazer um exemplo de TableLayout similar ao abaixo na minha última aula do TECDAM:
Uma característica do TableLayout é que cada linha é uma TableRow e cada coluna é um componente, que no caso acima, são TextViews, EditText e Checkboxes. Eu utilizei para a segunda coluna, a propriedade android:stretch_columns que informa quais colunas devem ser esticadas.
Porém, se você observar, a linha de cima tem 2 colunas, enquanto que a de baixo tem 3, foi aí que achei a propriedade android:layout_span (que é bem similar a do HTML) e informa quanto uma coluna irá expandir e criar uma nova coluna. Vejam como ficou o código do layout acima.
<?xml version="1.0" encoding="utf-8"?>
<TableLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical"
  android:stretchColumns="1,2" >

  <TableRow>
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Nome" />

    <EditText
      android:id="@+id/editText1"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_span="2" />
  </TableRow>

  <TableRow>
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Avisos por:" />

    <CheckBox
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Email" />

    <CheckBox
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Telefone" />
  </TableRow>
</TableLayout>


É isso pessoal, qualquer dúvida, deixem seus comentários.

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:
<?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;

@end
Vou 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.
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

segunda-feira, 28 de novembro de 2011

Lendo RSS no Android

Olá povo,

A tecnologia RSS tornou-se uma forma padrão que os sites encontraram para compartilhar suas informações. Com ele, os usuários podem visualizar seu conteúdo de forma padronizada, uma vez que utiliza o formato XML. Sendo assim, vou mostrar nesse post como ler um RSS com Android. Mas o código utilizado aqui pode ser utilizado para qualquer XML.

Vou começar criando a classe que vai representar uma notícia do RSS. Ela tem basicamente um título, um resumo da notícia e um link para notícia completa.

class Noticia {
  String titulo;
  String descricao;
  String link;
 
  public Noticia(String t, String d, String l) {
    titulo = t;
    descricao = d;
    link = l;
  }
 
  @Override
  public String toString() {
    return titulo;
  }
}

Simplifiquei ao máximo a classe aqui, não coloquei nem os GETs nem os SETs. Implementei apenas o método toString() pois o mesmo é utilizado pelo Adapter (que veremos em seguida) para exibir a notícia.

Agora vamos ver o código da Activity.
public class LeitorRSSActivity extends ListActivity {

  List<Noticia> news;
  ArrayAdapter<Noticia> adapter;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    news = new ArrayList<Noticia>();
    adapter = new ArrayAdapter<Noticia>(
    this, android.R.layout.simple_list_item_1, news);
    setListAdapter(adapter);

    // Criando AsyncTask que buscará o RSS da globo
    new RssAsyncTask().execute(
      "http://g1.globo.com/dynamo/rss2.xml");
  }

  // Método que lê o XML do RSS
  private List<Noticia> readXML(InputStream is){
    List<Noticia> noticias =
      new ArrayList<Noticia>();

    try {
      // Criando os objetos que representam o XML
      DocumentBuilderFactory factory =
        DocumentBuilderFactory.newInstance();

      DocumentBuilder builder = 
        factory.newDocumentBuilder();
      Document xmlDocument = builder.parse(is);
   
      // Cada notícia é representada pela tag <item>
      // Aqui obtemos a lista de nós com essa tag
      NodeList posts = 
        xmlDocument.getElementsByTagName("item");

      // Vamos iterar sobre a lista de itens
      String titulo = null, descricao = null, 
        link = null;

      for (int i = 0; i < posts.getLength(); i++) {
        Node post = posts.item(i);

        // Cada nó ITEM tem os filhos:
        // TITLE, DESCRIPTION e LINK
        NodeList postInfo = post.getChildNodes();
        for (int j = 0; j < postInfo.getLength(); j++){
          Node info = postInfo.item(j);

          if ("title".equals(info.getNodeName())){
            titulo = info.getTextContent();

          } else if ("link".equals(
            info.getNodeName())){
            link = info.getTextContent();
      
          } else if ("description".equals(
            info.getNodeName())){
            descricao = info.getTextContent();
          }
        }
        // Com as informações das tags, criamos o
        // objeto notícia e adicionamos na lista
        noticias.add(
          new Noticia(titulo, descricao, link));
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }
    return noticias;
  }

  // A AsyncTask realiza a comunicação em background
  class RssAsyncTask extends
    AsyncTask<String, Void, List<Noticia>>{

    ProgressDialog dialog;
 
    @Override
    protected void onPreExecute() {
      super.onPreExecute();
      // Antes de baixaro XML, mostra o dialog
      dialog = ProgressDialog.show(
        LeitorRSSActivity.this, 
        "Aguarde", "Baixando RSS");
    }
 
    @Override
    protected List<Noticia> doInBackground(
      String... params) {

      List<Noticia> lista = null;
      HttpURLConnection conexao = null;
      InputStream is = null;
   
      try {
        URL url = new URL(params[0]);
        conexao = (HttpURLConnection)
          url.openConnection();
        conexao.connect();
    
        is = conexao.getInputStream();
        lista = readXML(is);

      } catch (Throwable t){
        t.printStackTrace();
      } finally {
        try {
          if (is != null) is.close();
          if (conexao != null) conexao.disconnect();
        } catch (Throwable t){
        }
      }
      return lista;
    }
  
    @Override
    protected void onPostExecute(List<Noticia> result){
      super.onPostExecute(result);
      dialog.dismiss();
      news.addAll(result);
      adapter.notifyDataSetChanged();
    }
  }
}


Quem tiver dúvida sobre AsyncTask, dá uma olhada aqui, ou deixem seus comentários.

4br4ç05,
nglauber