A funcionalidade de captura de imagem é um pilar fundamental no desenvolvimento Android moderno. Seja para redes sociais, verificação de identidade ou digitalização de documentos, integrar a câmera de forma robusta exige conhecimento sobre permissões, gestão de memória e APIs de hardware. Este guia explora as melhores práticas para implementar desde chamadas simples via Intent até o uso da biblioteca CameraX.
1. Captura de Imagem via Intents do Sistema
A maneira mais rápida de implementar a captura de fotos é delegar a tarefa ao aplicativo de câmera padrão do dispositivo. Isso reduz a complexidade do código e garante uma interface familiar ao usuário.
Configuração do Intent de Captura
Para obter uma imagem em alta resolução, não basta apenas disparar o Intent; é necessário fornecer um URI onde o sistema salvará o arquivo original.
// Definição do código de requisição
private static final int CODIGO_DISPARO_CAMERA = 101;
private String caminhoFotoAtual;
private void dispararIntentCamera() {
Intent intentCaptura = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Verifica se existe um app de câmera disponível
if (intentCaptura.resolveActivity(getPackageManager()) != null) {
File arquivoFoto = null;
try {
arquivoFoto = criarArquivoDeImagem();
} catch (IOException ex) {
// Tratar erro na criação do arquivo
}
if (arquivoFoto != null) {
Uri uriFoto = FileProvider.getUriForFile(this,
"com.exemplo.app.fileprovider",
arquivoFoto);
intentCaptura.putExtra(MediaStore.EXTRA_OUTPUT, uriFoto);
startActivityForResult(intentCaptura, CODIGO_DISPARO_CAMERA);
}
}
}
2. Segurança e Compartilhamento com FileProvider
Desde o Android 7.0 (Nougat), o compartilhamento de URIs file:// é proibido por motivos de segurança. Deve-se utilizar o FileProvider para gerar URIs content://.
Configuração do provider_paths.xml
Crie um arquivo em res/xml/caminhos_arquivos.xml:
<paths>
<external-files-path name="fotos_app" path="Pictures/" />
</paths>
Declaração no AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.exemplo.app.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/caminhos_arquivos" />
</provider>
3. Processamento de Imagem e Gestão de Memória
Imagens modernas podem ter resoluções altíssimas, consumindo dezenas de megabytes se carregadas diretamente na memória RAM. Para evitar o erro OutOfMemoryError (OOM), é essencial realizar o redimensionamento antes da decodificação.
Cálculo de Amostragem (Sampling)
public static Bitmap decodificarImagemOtimizada(String path, int larguraAlvo, int alturaAlvo) {
BitmapFactory.Options opcoes = new BitmapFactory.Options();
opcoes.inJustDecodeBounds = true; // Apenas lê as dimensões, sem carregar o bitmap
BitmapFactory.decodeFile(path, opcoes);
// Calcula o fator de escala
opcoes.inSampleSize = calcularTamanhoAmostra(opcoes, larguraAlvo, alturaAlvo);
// Decodifica o bitmap com o tamanho real otimizado
opcoes.inJustDecodeBounds = false;
opcoes.inPreferredConfig = Bitmap.Config.RGB_565; // Reduz o uso de memória pela metade
return BitmapFactory.decodeFile(path, opcoes);
}
private static int calcularTamanhoAmostra(BitmapFactory.Options opcoes, int reqW, int reqH) {
int alturaOriginal = opcoes.outHeight;
int larguraOriginal = opcoes.outWidth;
int inSampleSize = 1;
if (alturaOriginal > reqH || larguraOriginal > reqW) {
final int metadeH = alturaOriginal / 2;
final int metadeW = larguraOriginal / 2;
while ((metadeH / inSampleSize) >= reqH && (metadeW / inSampleSize) >= reqW) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
4. Correção de Orientação via EXIF
Muitos dispositivos salvam a imagem em uma orientação fixa, inserindo a rotação correta nos metadados EXIF. Ignorar isso pode resultar em fotos exibidas de lado.
private Bitmap corrigirRotacao(Bitmap bitmap, String path) throws IOException {
ExifInterface exifInterface = new ExifInterface(path);
int orientacao = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
Matrix matriz = new Matrix();
switch (orientacao) {
case ExifInterface.ORIENTATION_ROTATE_90:
matriz.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matriz.postRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matriz.postRotate(270);
break;
default:
return bitmap;
}
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matriz, true);
}
5. Evolução para Câmera Customizada com CameraX
Quando a aplicação exige um controle maior sobre a interface de captura ou análise de imagem em tempo real (como leitura de QR Code), a Jetpack CameraX é a escolha ideal por ser ciente do ciclo de vida (lifecycle-aware).
Vantagens da CameraX:
- Consistência: Resolve problemas de fragmentação entre fabricantes automaticamente.
- Lifecycle: Gerencia a abertura e fechamento da câmera com base no estado da Activity/Fragment.
- Extensibilidade: Suporte nativo para HDR, Modo Noturno e Retrato em dispositivos compatíveis.
Configuração de um Caso de Uso de Visualização
private void iniciarCameraX() {
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider provider = cameraProviderFuture.get();
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
CameraSelector seletor = CameraSelector.DEFAULT_BACK_CAMERA;
provider.unbindAll();
provider.bindToLifecycle(this, seletor, preview);
} catch (ExecutionException | InterruptedException e) {
// Tratar exceções
}
}, ContextCompat.getMainExecutor(this));
}
Considerações Finais sobre Permissões
Para qualquer operação de câmera, lembre-se de solicitar as permissões em tempo de execução para dispositivos com Android 6.0 ou superior. O uso da permissão CAMERA é obrigatório, e o acesso ao armazenamento (READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE) pode ser necessário dependenod do local de salvamento escolhido para a foto.