sexta-feira, 1 de fevereiro de 2013

@selector, Blocks e NSInvocation

Olá povo,

O iOS trabalha fortemente com o padrão Delegate, onde você determina uma classe que implementa um dado protocolo e a mesma tratará algum tipo de evento. Isso nada mais é do que o padrão de projeto observer (levemente modificado). Mas em diversos pontos da API também vemos uma outra abordagem: as funções/metódos de callback. Os callbacks, bem famosos no JavaScript, são métodos ou funções que são passados como parâmetros para outros métodos, e são chamados em um dado momento da execução do método para qual ele foi passado.
Vou falar nesse post de como podemos usar os callbacks no iOS.

@selector
Uma forma de usar os callbacks é usando selectors. Digamos que você esteja implementando um método que fará uma série de processamento, e ao final você quer avisar para quem chamou esse método que o mesmo acabou. Isso seria a clássica implementação de um Observer (ou Delegate). Mas podemos fazer com selectors dessa forma:
// Precisamos pra chamar objc_msgSend
#import <objc/message.h>

- (void) executar:(SEL)funcaoDeCallback {
  NSLog(@"Um monte de processamento aqui…");
  if ([self respondsToSelector:funcaoDeCallback]){
    objc_msgSend(self, funcaoDeCallback, @"Glauber");
  }
}
O método executar tem um parâmetro do tipo SEL, que é um ponteiro para um método. Dentro do método checamos se a nossa classe (self) responderá aquele selector, ou seja, se ela declarou um método com essa assinatura. Em caso positivo, usamos a função objc_msgSend passando qual o objeto que tem o método, o selector (ou seja, o método) e um parâmetro (que eu quis passar só pra ilustrar). Então, para executarmos um selector, temos sempre que passar o target (que é o objeto que tem o método) e o selector.
Aqui nós usamos a função objc_msgSend ao invés do famoso performSelector. Isso porque com a migração para o ARC, o compilador não consegue garantir que a função existe e então exibe um Warning. Uma alternativa para essa função e continuar usando o performSelector é conforme abaixo:
#pragma clang diagnostic push
#pragma clang diagnostic ignored 
  "-Warc-performSelector-leaks"
[self performSelector:funcaoDeCallback 
  withObject:@"Glauber"];
#pragma clang diagnostic pop
E para chamar o método executar, de modo que depois ele chame o método aqui:
SEL selector = @selector(meuMetodo:);
[self executar:selector];
Digamos que o método aqui ficasse dessa forma:
- (NSString *)meuMetodo:(NSString *)texto {
    NSLog(@"O executar retornou: %@", texto);
    return @"OK";
}
Um exemplo clássico do uso de selector na API é na criação de botões para NavigationBar, onde determinamos o método que será chamado quando clicar no mesmo.
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] 
  initWithTitle:@"Esquerda" 
  style:UIBarButtonItemStyleBordered 
  target:self action:@selector(meuClick)];


NSIvocation
E como faríamos para pegar o retorno usando @selector? Temos uma outra alternativa. Utilizar a classe NSInvocation.
SEL selector = @selector(aqui:);
    
NSString *parametro = @"Glauber";
    
NSMethodSignature * assinatura = 
  [self methodSignatureForSelector:selector];
NSInvocation * invocacao = [NSInvocation 
  invocationWithMethodSignature:assinatura];
[invocacao setTarget:self];
[invocacao setSelector:selector];
[invocacao setArgument:&parametro atIndex:2];
[invocacao invoke];
    
NSString *retorno;
[invocacao getReturnValue:&retorno];
NSLog(@"Retorno: %@", retorno);
Notem que para passar o parâmetro usamos 2 para o primeiro (e único) parâmetro. Isso porque as posições 0 e 1 estão reservadas para self e _cmd respectivamente.

Blocks
A partir do iOS 4, foi disponibilizado uma outra forma de usar funções de callback, os blocks. Seu propósito é bem similar ao selector. Eles são particularmente úteis para executar ações concorrentes ou para callbacks. As principais vantagens dos blocks são: Eles permitem escrever código o código perto da sua chamada (como inner classes do Java); e permitem acessar variáveis locais. Vejamos abaixo a sintaxe e como ele funciona.
int multiplicador = 7;
int (^meuBloco)(int) = ^(int num) {
    return num * multiplicador;
};
 
NSLog("%d", meuBloco(3));
No código acima, declaramos um bloco que retorna um int, chama-se meuBloco e recebe um inteiro como parâmetro. O nome do parâmetro (num) é definido na inicialização do block. Se quisermos declarar um método que recebe um block que retorna void e recebe uma NSString por exemplo:
- (void)meuMetodoRecebeUmBlock:(void (^)(NSString *texto))bloco{
    NSLog(@"Faça alguma coisa");
    bloco(@"Terminou");
}
E para chamar:
[self meuMetodoRecebeUmBlock:^(NSString* texto){
    NSLog(@"meuMetodoRecebeUmBlock disse: %@", texto);
}];

Outra grande vantagem de utilizar os blocks em relação ao selector, é que não se faz necessário passar o target para o método. Podemos chamá-lo diretamente sem ter a referência do objeto que tem o método. Além disso, conseguimos pegar um retorno de um método mais facilmente do que com o @selector.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

Fontes: 
http://iphonedevblog.com/code/how-to-use-nsinvocation/
http://pragmaticstudio.com/blog/2010/7/28/ios4-blocks-1

sábado, 26 de janeiro de 2013

YouTube API para Android

Olá povo,

No dia 22/01/2013 a Google anunciou uma API para adicionar vídeos do YouTube nas aplicações Android. Os requisitos mínimos para utilização da API é que o dispositivo rode Android 2.2 ou superior e ter a aplicação do Youtube versão 4.2.16 ou mais recente instalada (por isso, não consegui rodar no emulador). Outro detalhe importante é que os vídeos poderão ser executados tanto em Activities diretamente ou em Fragments.
E nesse primeiro post de 2013 vou mostrar como dar os primeiros passos.

Gerando a chave de acesso
A primeira coisa que devemos fazer é registrar nossa aplicação no site Google APIs Console. Para termos acesso aos mais diversos serviços do Google, precisamos criar um projeto (se você já não tiver um). Para tal, clique em Create new Project. Agora acesse a opção API access depois clique no botão Create new Android key (se você não tiver uma), no popup que for exibido clique em Create. Será gerada uma chave no padrão SHA-1 similar a essa que usaremos no nosso código: AIzbSyCyoMobvh72ZrSv4xQddOLzDOlaLqCcILU

Baixando a API
Uma vez que temos a chave de acesso, baixe a API clicando aqui. Descompacte o arquivo zip e adicione o arquivo libs/YouTubeAndroidPlayerApi.jar à pasta libs do seu projeto Android. Ainda não criou o projeto? Tá esperando o quê? :)

Hello World Youtube!
Sem muitas delongas, adicione a permissão de Internet no AndroidManifest.xml e deixe sua Activity como abaixo:

import android.os.Bundle;

import com.google.android.youtube.player.*;
import com.google.android.youtube.player.YouTubePlayer.*;

public class MainActivity 
  extends YouTubeBaseActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    YouTubePlayerView youTubeView = 
      new YouTubePlayerView(this);

    setContentView(youTubeView);
    youTubeView.initialize(
      "AIzbSyCyoMobvh72ZrSv4xQddOLzDOlaLqCcILU", 
      new OnInitializedListener(){

      @Override
      public void onInitializationFailure(
        Provider provider,
        YouTubeInitializationResult result) {
      }

      @Override
      public void onInitializationSuccess(
        Provider provider,
        YouTubePlayer player, 
        boolean wasRestored) {
          if (!wasRestored){
            player.cueVideo("9bZkp7q19f0");
          }
      }
    }); 
  }
}
Nossa classe herda de YouTubeBaseActivity e no método onCreate instanciamos uma YouTubePlayerView que é quem exibirá o vídeo. Com esse objeto chamamos o método initialize passando a chave de acesso que geramos anteriormente além de uma instância da interface OnInitilizedListener. Essa interface tem dois métodos: um para quando a inicialização foi feita com sucesso; e outra para quando houve algum problema.
No método onInitializationSuccess usamos o objeto YouTubePlayer para inicializar o vídeo usando o método cueVideo passando o id do vídeo do YouTube. O parâmetro wasRestored é pra saber se o vídeo está sendo restaurado do ponto onde parou.



O exemplo que fizemos aqui usamos a YouTubeView, mas você pode fazer o mesmo utilizando a classe YouTubeFragment (e YouTubeSupportFragment para dispositivos anteriores ao Android 3).

Mais informações sobre a YouTube API você pode ter aqui.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

domingo, 30 de dezembro de 2012

Android Google Maps v2

Olá povo,

No último dia 03/12/2012  a Google disponibilizou a versão 2.0 da API do Google Maps para Android que trouxe diversas melhorias em relação a versão anterior. O problema é que tudo mudou completamente :) Desde a geração da chave de acesso até as classes envolvidas. A parte boa disso tudo, é que essa API funciona desde a versão 2.2 do Android e trouxe melhorias significativas \o/
Nesse post vou mostrar como dar os primeiros passos nessa nova API.

Gerando a chave de acesso

Quem já fez algum exemplo sabe que precisamos de uma chave de acesso para utilizar o serviço de mapas. Para ter acesso aos mais diversos serviços do Google, precisamos criar um projeto no site do Google APIs Console. Uma vez que o projeto foi criado, acesse a opção Services e habilite o serviço Google Maps Android API v2. Uma vez habilitado o serviço, acesse a opção API access depois clique no botão Create new Android key, no popup que for exibido clique em Create.
Será gerada uma chave no padrão SHA-1 que usaremos no nosso código similar a essa: AIzbSyCyoMobvh72ZrSv4xQddOLzDOlaLqCcILU

Biblioteca dos serviços do Google Play
A segunda etapa é baixar o pacote de serviços do Google Play. Abra o Android SDK Manager e baixe o Google Play services.
Quando terminar o download, será criado o seguinte diretório [android-sdk/extras/google/google_play_services/libproject] que contém as classes necessárias para usar a nova API. Precisamos importar esse projeto para dentro do Eclipse, pois nosso projeto irá referencia-lo.
Acesse a opção File | New project... Em seguida, selecione Android | Android Project From Existing Code e então selecione o diretório citado anteriormente. O projeto será importado para o Eclipse e agora podemos começar nosso projeto de exemplo.

Projeto de exemplo
Crie um novo projeto Android e vamos começar a brincadeira :) A primeira coisa é referenciar o projeto  do Google Play Service dentro do nosso projeto. Para isso, clique com o botão direito sobre o projeto e selecione  Properties. Na janela que for exibida, selecione Android do lado esquerdo. Clique no botão Add... e selecione google-play-services_lib e então clique em Ok.
Estamos prontos pra começar, deixe o AndroidManifest.xml como abaixo.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android=
  "http://schemas.android.com/apk/res/android"
  package="ngvl.testegmaps_v2"
  android:versionCode="1"
  android:versionName="1.0" >

  <uses-sdk
    android:minSdkVersion="9"
    android:targetSdkVersion="15" />

  <uses-permission android:name=
    "android.permission.INTERNET" />
  <uses-permission android:name=
    "android.permission.ACCESS_NETWORK_STATE"/>
  <uses-permission android:name=
    "android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name=
    "com.google.android.providers.gsf.permission.READ_GSERVICES" />

  <uses-feature
    android:glEsVersion="0x00020000"
    android:required="true" />

  <application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
      android:name="ngvl.testegmaps_v2.MainActivity"
      android:label="@string/app_name" >
      <intent-filter>
        <action android:name=
          "android.intent.action.MAIN" />
        <category android:name=
          "android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

    <meta-data android:name=
      "com.google.android.maps.v2.API_KEY"
      android:value="suaApiKeyAqui!!!!!!" />
  </application>
</manifest>

Vamos comentar alguns detalhes do arquivo. Começamos utilizando as permissões de INTERNET e WRITE_EXTERNAL_STORAGE para podermos baixar os mapas e fazer cache dos mesmos no cartão de memória. Um requisito dessa nova API é que o aparelho deve suportar OpenGL que declaramos logo em seguida. A última configuração importante que fizemos aqui é a tag meta-data que declaramos a chave do Google Maps que geramos na primeira etapa desse post.
Agora deixe o arquivo de layout conforme abaixo.
<fragment
  xmlns:android=
    "http://schemas.android.com/apk/res/android"
  android:id="@+id/map"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:name=
    "com.google.android.gms.maps.SupportMapFragment" />
Aqui temos uma grande mudança: a classe MapView e MapActivity não são mais utilizadas. Em seu lugar entrou a classe MapFragment, mas como quis deixar a compatibilidade com versões anteriores do Android, usei a SupportMapFragment. Isso nos permite utilizar mapas em fragments, o que só era possível anteriormente através de APIs de terceiros, uma vez que tínhamos que herdar de MapActivity. Outro detalhe é que o nome do pacote também mudou, agora é com.google.android.gms.maps.
Por fim, vamos ao código da Activity.

package ngvl.testegmaps_v2;

import android.os.Bundle;
import android.support.v4.app.*;

import com.google.android.gms.common.*;
import com.google.android.gms.maps.*;
import com.google.android.gms.maps.model.*;

public class MainActivity extends FragmentActivity {

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
 
    SupportMapFragment fragment =
     (SupportMapFragment)getSupportFragmentManager()
       .findFragmentById(R.id.map);
    GoogleMap map = fragment.getMap();

    LatLng latLng = new LatLng(-23.561706,-46.655981);
    map.addMarker(new MarkerOptions()
      .position(latLng)
      .icon(BitmapDescriptorFactory.fromResource(
        R.drawable.ic_launcher))
      .title("Av. Paulista")
      .snippet("São Paulo"));
  
    configuraPosicao(map, latLng);
  }

  private void configuraPosicao(
    GoogleMap map, LatLng latLng) {

    map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
    map.animateCamera(
     CameraUpdateFactory.newLatLngZoom(latLng, 17.0f));
  }
}
Nossa classe herda de FragmentActivity da API de compatibilidade do Google que falei aqui. Em seguida, obtemos a referência para o MapFragment, e a partir dele obtemos a instância do GoogleMaps (que substitui o MapView). A classe GeoPoint foi substituída pela classe LatLng que representa uma coordenada geográfica. Com essa informação, adicionamos um ponto no mapa, passando uma imagem que servirá de marcador no mapa, um título e um resumo, que serão exibidos ao clicar na imagem no mapa. O método configuraPosicao, define o tipo de mapa para satélite e o zoom para 17 (os valores variam de 2 a 21, sendo 21 mais próximo possível).
Se rodarmos nossa aplicação, em um aparelho 2.3 ficará similar a figura abaixo:
Uma das novidades dessa versão da API do GoogleMaps é poder visualizar o mapa em relevo, mostrando, por exemplo, os prédios do local selecionado no mapa. Modifique o método configuraPosicao para ficar como abaixo:
private void configuraPosicao(
  GoogleMap map, LatLng latLng) {
  map.moveCamera(
   CameraUpdateFactory.newLatLngZoom(latLng, 15));
  map.animateCamera(
   CameraUpdateFactory.zoomTo(10), 2000, null);

  CameraPosition cameraPosition = 
    new CameraPosition.Builder()
      .target(latLng)   
      .zoom(17)     
      .bearing(90)
      .tilt(45)
      .build();
  map.animateCamera(
    CameraUpdateFactory.newCameraPosition(
      cameraPosition));
}
Notem que não usamos o modo satélite aqui (não ficou muito bom quando usei :) mas posicionamos a câmera de uma maneira diferente. Além da Latitude/Longitude e do Zoom, setamos o bearing (rotação do mapa em 90 graus) e o tilt (inclinação em 45 graus). O resultado pode ser visto abaixo.
Bem pessoal, espero que tenham gostado e qualquer dúvida, deixem seus comentários.
Mais detalhes em: https://developers.google.com/maps/documentation/android/

 4br4ç05,
nglauber

quarta-feira, 26 de dezembro de 2012

Android ExpadableListView

Olá povo,

Esse post estava no rascunho a algum tempo e resolvi publica-lo finalmente :) Vou mostrar como utilizar a lista agrupada do Android, a ExpandableListView. Como o próprio nome diz, ela contém uma lista de elementos que ao serem clicados exibem uma sublista. Como exemplo, teremos uma lista de estados, e ao clicar no nome do estado, exibiremos suas respectivas cidades.
Abaixo temos o código da Activity da aplicação.

public class ExemploExpladableListViewActivity
  extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(
      R.layout.activity_exemplo_expladable_list_view);

    ExpandableListView listview =(ExpandableListView)
      findViewById(R.id.expandableListView1);
   
    List<String> listPe = new ArrayList<String>();
    listPe.add("Caruaru");
    listPe.add("Recife");
   
    List<String> listSp = new ArrayList<String>();
    listSp.add("São Paulo");
    listSp.add("Campinas");
   
    Map<String, List<String>> dados = 
      new HashMap<String, List<String>>();
    dados.put("PE", listPe);
    dados.put("SP", listSp);
   
    listview.setAdapter(
      new MeuExpadableAdapter(dados));
  }
}

Como podemos notar, ao invés de usar uma lista simples, utilizamos um HashMap onde cada elemento será composto de uma chave que será o nome do estado e o valor será uma lista com as cidade daquele estado. O arquivo de layout contém apenas uma ExpandableListView, conforme abaixo.

<ExpandableListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/expandableListView1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Como uma ListView comum, utilizaremos um Adapter para exibir o conteúdo da lista. A diferença é que herdaremos de BaseExpandableListAdapter, onde teremos que implementar mais alguns métodos além dos que já estamos acostumados nas listas convencionais:

  • getGroup: retorna o objeto que representa o grupo de informações. No nosso caso, a sigla do estado;
  • getGroupCount: retorna quantos grupos nós teremos. Aqui estamos retornando quantas entradas (chaves) temos na nossa HashMap;
  • getGroupId: algum inteiro que sirva de identificador único para o grupo, aqui estamos retornando a posição do grupo;
  • getGroupView: retorna a View que representará visualmente o grupo na lista;
  • getChild: similar ao getItem do Adapter convencional, a diferença é que é passado o índice do grupo e do elemento que se deve retornar;
  • getChildId: identificador único do elemento do grupo;
  • getChildView: retorna a View que representará o elemento dentro do grupo. Similar ao getView do adapter convencional;
  • getChildrenCount: retorna a quantidade de elementos dentro de um grupo;
  • hasStableIds: informa se os identificadores dos grupos e dos elementos são fixos;
  • isChildSelectable: retorna true se os elementos filhos podem ser selecionados, ou false caso contrário.

class MeuExpadableAdapter 
  extends BaseExpandableListAdapter {

  private Map<String, List<String>> dados; 
  private List<String> keys;
 
  public MeuExpadableAdapter(
    Map<String, List<String>> dados){

    this.dados = dados;
    this.keys = new ArrayList<String>(
      dados.keySet());
  }
 
  @Override
  public Object getGroup(int groupPosition) {
    return keys.get(groupPosition);
  }

  @Override
  public int getGroupCount() {
    return dados.size();
  }

  @Override
  public long getGroupId(int groupPosition) {
    return groupPosition;
  }

  @Override
  public View getGroupView(int groupPosition, 
    boolean isExpanded, View convertView, 
    ViewGroup parent) {

    View v = LayoutInflater.from(
      parent.getContext()).inflate(
        android.R.layout.simple_expandable_list_item_1, 
        null);

    TextView txt = (TextView)
      v.findViewById(android.R.id.text1);

    txt.setText(keys.get(groupPosition));
    return v;
  }
 
  @Override
  public Object getChild(
    int groupPosition, int childPosition) {

    return dados.get(
      keys.get(groupPosition)).get(childPosition);
  }

  @Override
  public long getChildId(
    int groupPosition, int childPosition) {
    return 0;
  }

  @Override
  public View getChildView(int groupPosition, 
    int childPosition, boolean isLastChild, 
    View convertView, ViewGroup parent) {

    View v = LayoutInflater.from(
      parent.getContext()).inflate(
        android.R.layout.simple_list_item_1, null);
    TextView txt = (TextView)
      v.findViewById(android.R.id.text1);
    txt.setText(dados.get(
      keys.get(groupPosition)).get(childPosition));
    return v;
  }
 
  @Override
  public int getChildrenCount(int groupPosition) {
    return dados.get(keys.get(groupPosition)).size();
  }

  @Override
  public boolean hasStableIds() {
   return true;
  }

  @Override
  public boolean isChildSelectable(
    int groupPosition, int childPosition) {
    return true;
  } 
}

O exemplo acima (com uma leve modificação :) está aqui.
Qualquer dúvida deixem seus comentários.

4br4ç05,
nglauber

quarta-feira, 5 de dezembro de 2012

Curso de iOS à distância

Olá povo,

O CESAR.edu está promovendo o curso à distância de Desenvolvimento de Aplicativos para iOS. As aulas serão ministradas por mim on line e ao vivo, o que permitirá maior interação com os alunos.

A plataforma iOS é uma das líderes no mercado mobile e a demanda por aplicativos para seus dispositivos vem crescendo, e consequente a busca por profissionais capacitados nessa plataforma.
Neste curso serão apresentados os conceitos fundamentais para desenvolvimento de aplicações para os dispositivos móveis da Apple: iPhone, iPad e iPod Touch. O Ambiente de desenvolvimento, a linguagem de programação Objective-C, os componentes de interface gráfica e os principais recursos destes dispositivos, serão demonstrados através de uma abordagem totalmente prática e focadas em problemas reais.

O objetivo do curso é preparar os alunos para desenvolver aplicativos para a plataforma iOS da Apple. Apresentando os principais conceitos, os alunos terão uma base sólida para criar aplicações robustas e que atendam as principais demandas do mercado. Os tópicos apresentados no curso focam em problemas reais, cobrindo os requisitos mais solicitados por aplicações desenvolvidas no mercado.

Aguardo vocês lá!

4br4ç05,
nglauber

segunda-feira, 19 de novembro de 2012

Participação na FEECBR

Olá povo,

Tive a satisfação de ser convidado para palestrar na FEECBR, uma convenção para profissionais de internet de diversas áreas. O evento foi idealizado por um grupo experiente, reconhecido pela qualidade e grau de inovação de projetos educacionais no Nordeste do Brasil. Na oportunidade, palestrantes regionais, nacionais e até internacionais estarão discutindo temas relevantes sobre tecnologia em vários segmentos.
Estarei falando sobre a criação de interfaces inteligentes para smartphones e tablets Android, mostrando os principais padrões e técnicas para obter uma usabilidade atraente, eficiente e seguindo os padrões adotados por aplicações de sucesso.

Editado em 02/12/2012
Segue abaixo os slides da minha palestra.

4br4ç05,
nglauber

segunda-feira, 5 de novembro de 2012

ActionBar no Android 2.x

Olá povo,

Quem me conhece saber que não sou muito fã de frameworks e bibliotecas, gosto de procurar ao máximo uma solução nativa da plataforma ao qual estou trabalhando. E uma coisa que me deixava muito chateado no Android era a não retrocompatibilidade da ActionBar com versões anteriores à 3.0 (quando ela foi lançada).
Felizmente esse problema acabou com a biblioteca SherlockActionBar, que traz o recurso da ActionBar para aparelhos com Android 2.3 ou inferior, e mantém os nomes de métodos iguais à plataforma nativa. E o que é melhor, quando a aplicação está rodando em versões 3.0 ou superior, o Sherlock só faz chamar a API padrão, ou seja, utiliza a ActionBar nativa.

Vou fazer um exemplo que utiliza dois recursos da ActionBar: Menus e Abas. Faça o download em http://actionbarsherlock.com e descompacte em algum local do seu computador. No Eclipse, importe o projeto library para dentro do seu workspace, para tal acesse File/New.../Project, e na janela que for exibida selecione Android/Android Project From existing code selecione o diretório onde você descompactou o Sherlock. Aparecerá além do projeto library, os demos do Sherlock, por agora só importe o projeto library.

Crie um novo projeto selecionando o Build SDK 4.1 (o Sherlock utiliza Temas e Estilos dessa versão do Android) e o Minimum Required para 2.2. Depois de criado, vá até a pasta lib do projeto e apague o arquivo android-support-v4.jar (o Sherlock já inclui esse arquivo). Agora adicione a referência ao projeto library do Sherlock, para tal, clique com o botão direito no projeto e selecione Properties.

Selecione Android, e na parte inferior direita, clique em Add... e selecione o projeto library e clique em OK. Agora, deixe a Activity do seu projeto conforme abaixo:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.actionbarsherlock.app.*;
import com.actionbarsherlock.app.ActionBar.*;
import com.actionbarsherlock.view.Menu;

public class ExemploSherlockActivity 
  extends SherlockFragmentActivity 
  implements TabListener {

  private Fragmento1 f1;
  private Fragmento2 f2;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(R.style.Theme_Sherlock_Light);
    setContentView(R.layout.activity_exemplo_sherlock);

    f1 = (Fragmento1)getSupportFragmentManager()
         .findFragmentById(R.id.fragmento1);
    f2 = (Fragmento2)getSupportFragmentManager()
         .findFragmentById(R.id.fragmento2);
        
    getSupportActionBar().setNavigationMode(
      ActionBar.NAVIGATION_MODE_TABS);
        
    Tab aba1 = getSupportActionBar().newTab()
               .setText("Aba 1")
               .setTabListener(this);

    Tab aba2 = getSupportActionBar().newTab()
               .setText("Aba 2")
               .setTabListener(this);        
        
    getSupportActionBar().addTab(aba1);
    getSupportActionBar().addTab(aba2);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getSupportMenuInflater().inflate(
      R.menu.activity_exemplo_sherlock, menu);
    return super.onCreateOptionsMenu(menu);
  }

  @Override
  public void onTabSelected(Tab tab, 
    FragmentTransaction ft) {

    if (tab.getPosition() == 0){
      ft.show(f1).hide(f2);
    } else {
      ft.show(f2).hide(f1);
    }
  }

  @Override
  public void onTabUnselected(
    Tab tab, FragmentTransaction ft) {}

  @Override
  public void onTabReselected(
    Tab tab, FragmentTransaction ft) {}
    
  public static class Fragmento1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater infltr, 
      ViewGroup container, Bundle savedState){

      TextView txt = new TextView(getActivity()); 
      txt.setText("Fragmento 1");
      return txt;
    }
  }
 
  public static class Fragmento2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater infltr, 
      ViewGroup container, Bundle savedState){

      TextView txt = new TextView(getActivity());
      txt.setText("Fragmento 2");
      return txt;
    }
  }
}
Como estamos trabalhando com Fragment, nossa classe herda de SherlockFragmentActivity. Implementamos a interface TabChangeListener para detectarmos quando houver a alternância entre abas. Declaramos dois fragmentos que serão nas abas, essa classes estão declaradas no final do arquivo, obviamente o ideal é que cada fragmento fique em um arquivo separado.
No onCreate setamos o tema do Sherlock, e em seguida setamos o arquivo de layout da Activity (que está logo abaixo). Depois inicializamos os fragmentos e definimos a forma de navegação da ActionBar para o modo de abas. E por fim, criamos e adicionamos as abas na ActionBar.

Abaixo temos o arquivo de layout da aplicação (res/layout/activity_exemplo_sherlock.xml):
<FrameLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/FrameLayout1"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <fragment
    android:id="@+id/fragmento1"
    android:name="ngvl.android.exemplosherlock.ExemploSherlockActivity$Fragmento1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

  <fragment
    android:id="@+id/fragmento2"
    android:name="ngvl.android.exemplosherlock.ExemploSherlockActivity$Fragmento2"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</FrameLayout>

Note que o nome da classe é composto do pacote da aplicação + o nome da classe. Como estamos usando uma Inner Class, o sinal de $ é usado. Se você definir os Fragmentos em arquivos separados (o que é o normal) coloque apenas o nome completo da classe.

E abaixo o arquivo de menu da aplicação (res/menu/activity_exemplo_sherlock.xml) ele é carregado no método onCreateOptionsMenu. Para tratar cada opção adicione o método onMenuItemSelected.
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
  <item
    android:id="@+id/menu_new"
    android:showAsAction="always|withText"
    android:icon="@android:drawable/ic_menu_add"
    android:title="Novo"/>
  <item
    android:id="@+id/menu_edit"
    android:showAsAction="ifRoom|withText"
    android:icon="@android:drawable/ic_menu_edit"
    android:title="Editar"/>
  <item
    android:id="@+id/menu_listar"
    android:showAsAction="ifRoom"
    android:icon="@android:drawable/ic_menu_my_calendar"
    android:title="Listar Itens"/>
</menu>
O primeiro item sempre aparece com ícone e texto. No segundo, o ícone sempre aparece, mas texto só aparece (na barra) se houver espaço. O último só aparecerá se houver espaço, para vê-lo, você deverá pressionar a tecla menu (ou nos dispositivo que não tenham, esse item ficará "colapsed" mas a direita).

Ao mandar executar a aplicação devemos ter uma tela similar a abaixo.

Esse post é em homenagem aos meus alunos da FA7, que me obrigaram a aprender essa biblioteca durante a aula :) Abraço a todos!

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber