domingo, 21 de agosto de 2011

Galeria de mídia do Android

Olá povo,

Neste post vou mostrar como tirar uma foto com a aplicação de câmera, salvar a imagem em um diretório temporário e em seguida adiciona-la à galeria de mídia do aparelho. E por último, vou mostrar como carregar uma imagem da galeria.
Vamos iniciar mostrando como chamar a aplicação de câmera.
public void tirarFotoClick(View v) {
  String nomeFoto = DateFormat.format(
    "yyyy-MM-dd_hhmmss", new Date()).toString();

  caminhoFoto = new File(
    Environment.getExternalStoragePublicDirectory(
      Environment.DIRECTORY_PICTURES),
    nomeFoto);

  Intent it = new Intent(
    MediaStore.ACTION_IMAGE_CAPTURE);  
  it.putExtra(MediaStore.EXTRA_OUTPUT, 
    Uri.fromFile(caminhoFoto));
  startActivityForResult(it, 0);
}
A aplicação de câmera é chamada através da ação ACTION_IMAGE_CAPTURE passado no construtor da Intent. Um detalhe interessante aqui é o parâmetro EXTRA_OUTPUT, ele serve para indicar um caminho para a foto ser salva. Sem ele, a imagem será salva com tamanho e qualidade inferior a que a câmera realmente tirou.

caminhoFoto é um atributo do tipo File.
A chamada do método startActivityForResult indica que queremos tratar o resultado da activity que estamos chamando. Esse tratamento é feito no método onActivityResult.
protected void onActivityResult(
  int requestCode, int resultCode, Intent data) {
  super.onActivityResult(
    requestCode, resultCode, data);

  if (resultCode == RESULT_OK && requestCode == 0) {
    ImageView img = (ImageView)
      findViewById(R.id.imageView1);
   
    // Obtém o tamanho da ImageView
    int targetW = img.getWidth();
    int targetH = img.getHeight();
    
    // Obtém a largura e altura da foto
    BitmapFactory.Options bmOptions =
      new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(
      caminhoFoto.getAbsolutePath(), bmOptions);

    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;
    
    // Determina o fator de redimensionamento
    int scaleFactor = Math.min(
      photoW/targetW, photoH/targetH);
    
    // Decodifica o arquivo de imagem em 
    // um Bitmap que preencherá a ImageView
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;
    
    Bitmap bitmap = BitmapFactory.decodeFile(
      caminhoFoto.getAbsolutePath(), bmOptions);
    img.setImageBitmap(bitmap);
  }
}
Quando chamamos a câmera, no startActivityForResult, passamos o valor 1 como segundo parâmetro, esse valor representa o código da requisição (requestCode). Esse valor é retornado no método onActivityResult para saber se o resultado que estamos tratando é daquela requisição. No if que fizemos, além do requestCode, checamos o resultCode. O resultCode descreve o resultado da operação, que neste caso é tirar a foto. Se o resultado for RESULT_OK, é porque uma foto foi tirada.
[EDITADO em 12/05/2013] 
A foto que é tirada com as câmeras atuais requer muita memória para ser alocada. Sendo assim, verificamos o tamanho da ImageView para carregar a imagem deste tamanho.
O atributo caminhoFoto que foi atribuído no método tirarFotoClick é utilizado para carregar a imagem e atribuir em um ImageView. Agora vamos ver como salvar essa imagem na galeria de mídia.
public void salvarFotoClick(View v) {
  if (caminhoFoto != null && caminhoFoto.exists()) {
    MediaStore.Images.Media.insertImage(
      getContentResolver(), 
      caminhoFoto.getAbsolutePath(), 
      caminhoFoto.getName(), "");

    Toast.makeText(this, 
      "Imagem adicionada a galeria.",
      Toast.LENGTH_SHORT).show();
  }
}
A classe MediaStore.Images.Media tem um "método mágico" que permite que possamos inserir um Bitmap na galeria. Essa operação é feita graças a um ContentProvider disponibilizado pela aplicação de galeria. O próximo passo será como abrir a galeria de mídia para selecionarmos uma imagem.
public void galleryButtonClick(View v) {

  Intent intent = new Intent(
    Intent.ACTION_GET_CONTENT);
  intent.setType("image/*");
  startActivityForResult(intent, 2);
}
O código acima abrirá a aplicação de galeria. Notem que agora passamos o valor 2 para o startActivityForResult. Da mesma forma que fizemos para tirar foto, usaremos o método onActivityResult para tratar a imagem selecionada.
// adicione esse código no onActivityResult
if (resultCode == RESULT_OK && requestCode == 2) {
  Uri selectedImage = data.getData();
  String[] filePathColumn = { 
    MediaStore.Images.Media.DATA };

  Cursor cursor = getContentResolver().query(
    selectedImage, filePathColumn, null, null, null);
  cursor.moveToFirst();

  int columnIndex = cursor.getColumnIndex(
    filePathColumn[0]);
  String filePath = cursor.getString(columnIndex); 
  cursor.close();

  Bitmap yourSelectedImage = 
    BitmapFactory.decodeFile(filePath);

  imageView1.setImageBitmap(yourSelectedImage);
}
A galeria retorna o endereço da imagem selecionada através de um objeto Uri que é obtido em data.getData(). Com essa informação podemos acessar o ContentProvider da galeria para obter o caminho da imagem. Com esse caminho passamos para a classe BitmapFactory que retornará um objeto Bitmap para preencher a imageView.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

19 comentários:

Saulo Calderon disse...

Valeu, Glauber.
Vou já testar isso no projetinho que nos passou hoje.

Saulo Calderon
ADS Unibratec Noite.

Rafael disse...

Caro:

E como eu poderia enviar essa imagem para um computador? Tem como salvar em um banco, e exportar via JSON? Tem como enviar via FTP? Qual método você sugere?

Nelson Glauber disse...

Oi Rafael,

Para enviar para outro computador eu sugiro através de upload de arquivo via HTTP mesmo. Infelizmente não coloquei sobre isso aqui ainda.

4br4ç05,
nglauber

Anônimo disse...

ADRIANO []
Cara me diz uma coisa como eu faço para salvar uma imagem no banco atraves do caminho da imagem e recupera-la em uma listview?

Estou desesperado atras disso.


Abraços

Nelson Glauber disse...

Oi Adriano,

Eu prefiro salvar as imagens no cartão de memória (que é maior) do que no banco de dados que ficará na memória do aparelho. Mas vamos a sua resposta...
Para salvar imagens no banco, basta criar um campo do tipo BLOB.

CREATE TABLE MinhasImagens(_id INTEGER PRIMARY KEY, imagem BLOB)

E na hora de inserir, passa o array de bytes dessa imagem no método put do objeto ContentValues.

contentValues.put("imagem", bytes);

Para ler, você usa o método getBlob da classe Cursor, que é retornada quando você faz a consulta no banco:

byte[] bytesImg = c.getBlob(c.getColumnIndex("imagem"));

abr4ç05,
nglauber

Anônimo disse...

Opa Nelson vlw pela dica cara parabéns ai pelo blog.

Mas então cara se eu fosse salvar a imagem no sd mudaria muita coisa ?

Se for salvar a imagem no banco direto, não seria melhor salvar o caminho da imagem no banco , como eu faço isso, me da uma luz ai please !!!

Abraços .

Nelson Glauber disse...

Oi Adriano,

Nesse post as imagens já são salvas na galeira de mídia do Android que fica no SD card (normalmente em DCIM/Camera.

4br4ç0s,
nglauber

GiGi disse...

profêê.. é esse o post que falei... não entendi o "passo a passo" kk o que faz esse ultimo codigo do requestCode no onActivityResult :) explica ai por favor :) thanks []'s

Nelson Glauber disse...

Oi Giselle,

O código acima deve ser é chamado no onActivityResult da sua Activity. Ele começa checando se foi selecionada alguma imagem da galeria, o parâmetro "requestCode" é igual ao valor passado no método startActivityForResult, e o requestCode indica se o usuário selecionou uma imagem (RESULT_OK) ou não.

Em seguida, obtemos o caminho da imagem no banco de dados da Galeria através do método getData do objeto data que é uma Intent recebida como parâmetro do método onActivityResult. Esse método retorna uma Uri, que é um caminho no formato content://com.android.gallery/id/1.

Depois de obter o caminho da imagem no banco, faço uma query no banco de dados da galeria para pegar o caminho real da imagem, ou seja, onde ela está localizada no cartão de memória.

Por fim, eu pego esse caminho e carrego um Bitmap para mostrar em um ImageView.

Qualquer dúvida, deixa um comment aqui.

4br4ç05,
nglauber

GiGi disse...

valeww Obrigada mais uma vez professor :) tudo funcionando agora :) vlwwwwww mesmo... :)

Natanael disse...

Primeiramente gostaria de agradecer pelo tutorial, li vários, incluindo o do site DevelopersAndroid, mas o seu foi o único que compreendi bem.
Olha, você tem algum tutorial mostrando como exibir as fotos tiradas e salvas no SD card ? ou Como exibir a foto logo após ela ser tirada ?
Obrigado pelo espaço e atenção.

Nelson Glauber disse...

OI Natanael,

Infelizmente não :(

4br4ç05,
nglauber

Unknown disse...

Eu quero pegar essa imagem e enviar pro web service, sei q tem q converter para um array de byte. como fazer.

Nelson Glauber disse...

Oi Tiago,

Dá uma olhada nesse outro post aqui...

http://nglauber.blogspot.com.br/2012/10/upload-de-arquivos-no-android.html

4br4ç05,
nglauber

Anônimo disse...

Olá, você poderia postar o layout deste exemplo? Agradeço.

Nelson Glauber disse...

Oi Anônimo,

O layout só tem um ImageView e dois botões... Nada de mais...

Você consegue ;)

4br4ç05,
nglauber

teste disse...

ola td bem? como faço para adicionar o caminho da imagem no meu db do android, pois ja tenho uma tabela criada ai queria so implementar a imagem. Obrigado.
Antes de criar e tabela eu queria inserir tanvbem aqui a imagem.
Como converter para o meu exemplo?
public void salvarRegistro() {
Pessoa pessoa = new Pessoa();
pessoa.nome = etNome.getText().toString();
pessoa.endereco = etEndereco.getText().toString();
pessoa.telefone = etTelefone.getText().toString();

Nelson Glauber disse...

Oi Josué,

Basicamente você pegaria o caminho da foto...
pessoa.foto = caminhoFoto.getAbsolutePath();

Não sei se entendi bem a dúvida...

4br4ç05,
nglauber

Anônimo disse...

excelente artigo... é muito me ajudou