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