Atualizado em 19/07/17 com suporte a Android 6+
No post de hoje vou ensinar como criar um app Android onde você pode “invocar” o recurso de câmera do smartphone e depois ter acesso à foto retirada para fazer o que quiser com ela. Tenho certeza que lhe pode ser útil para algum projeto que esteja fazendo ou que vá fazer um dia.
Se você nunca fez um app antes, sugiro dar uma olhada primeiro neste tutorial que cobre todo o básico!
Há alguns anos atrás eu desenvolvi um app para uma campanha publicitária da LG, para o lançamento de um novo modelo de smartphone, acho que era o L2 ou algo assim. Esse app se chamava “Bem na Foto” e foi solicitado pela LG para ser usado nos pontos de venda visando mostrar como a câmera desse aparelho novo era boa. Ele era bem simples: o usuário abria o app, tirava uma foto, escolhia uma moldura para colocar a foto dentro e publicava nas redes sociais. Mesmo simples, me rendeu bastante dinheiro para um projeto que levei poucos dias para deixar 100%.
Vamos ver nesse post:
A tela inicial
Na tela inicial não teremos nada de especial, exceto um botão para tirar uma nova foto, sendo assim, quando você for criar seu projeto, escolha o template Basic Activity (o mesmo que usamos neste tutorial de CRUD) que já vem com um FloatingActionButton e apenas adicione uma ImageView que exibirá a foto tirada, dentro do content_main.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="br.com.luiztools.camera.MainActivity" tools:showIn="@layout/activity_main"> <ImageView android:id="@+id/imagem" android:contentDescription="Foto tirada" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintRight_toLeftOf="parent" tools:layout_constraintTop_creator="1" tools:layout_constraintRight_creator="1" tools:layout_constraintBottom_creator="1" app:layout_constraintBottom_toTopOf="parent" tools:layout_constraintLeft_creator="1" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout> |
Note que eu não escrevi todos esses código aí em cima, apenas arrastei o ImageView pro layout e usei o recurso Infer Constraints do ContraintLayout. Já no activity_main.xml, apenas mude o ícone do FloatingActionButton para ser uma câmera ao invés do envelope tradicional, como fiz no código abaixo:
1 2 3 4 5 6 7 8 9 |
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@android:drawable/ic_menu_camera" /> |
O seu layout deve se parecer com esse (a ImageView está transparente pois não tiramos foto ainda):
Na classe Java que vai responder por esta tela, chamada de MainActivity, existe um código pronto para o clique do FloatingActionButton, que por padrão apenas exibe uma SnackBar, mude-o para que chame o método correto, conforme abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { getPermissions(); } }); } |
Note que neste código chamamos um método getPermissions(); que ainda não existe e que teremos de criar no próximo passo.
Configurando permissões
Neste tutorial usaremos os recursos de câmera e de armazenamento do smartphone, logo, precisamos que o Android nos forneça tais permissões. Além disso, precisamos exigir que este app somente possa ser instalado em dispositivos com câmera. O trecho de código abaixo deve ser colocado no arquivo AndroidManifest.xml, logo acima da tag application:
1 2 3 4 5 6 |
<uses-permission android:name="android.permission.CAMERA"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission> <uses-feature android:name="android.hardware.camera" android:required="true" /> |
Até a versão 5 do Android isso é o suficiente para garantir que teremos as permissões necessárias. No entanto, a partir da versão 6 o controle de permissões mudou e agora devemos também solicitar via código, no momento em que vamos usar o recurso em questão.
Para tanto, vamos criar o método getPermissions() como abaixo (bem parecido com o que usamos no tutorial de app com GPS) e o método onRequestPermissionsResult() que será disparado automaticamente quando o usuário tiver interagido com o dialog de sistema que pedirá a permissão para ele:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private void getPermissions() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1); } else dispatchTakePictureIntent(); } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case 1: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { dispatchTakePictureIntent(); } else { Toast.makeText(this, "Não vai funcionar!!!", Toast.LENGTH_LONG).show(); } return; } } } |
Após o usuário fornecer ou não permissão ao aplicativo, ele irá disparar o evento onRequestPermissionsResult, também exibido no código anterior, que dispara o método dispatchTakePictureIntent() se tiver as permissões de uso da câmera e do SD ou um alerta, caso contrário. Da mesma forma, em usos futuros do app, já com a permissão concedida, esse passo não acontece mais, como mostrado no else do método getPermissions().
Note que este método dispatchTakePictureIntent() não existe ainda, criaremos no próximo passo.
Chamando a câmera
Para invocar a câmera do dispositivo nós temos que criar um método com a intenção de usar a câmera que, quando a foto for tirada, irá retornar à activity de origem, salvar a foto e exibir a mesma.
Ufa, é um trabalho e tanto!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
private void dispatchTakePictureIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); photoFile = File.createTempFile("PHOTOAPP", ".jpg", storageDir); mCurrentPhotoPath = "file:" + photoFile.getAbsolutePath(); } catch(IOException ex){ Toast.makeText(getApplicationContext(), "Erro ao tirar a foto", Toast.LENGTH_SHORT).show(); } if (photoFile != null) { takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { try { ImageView imagem = (ImageView)findViewById(R.id.imagem); Bitmap bm1 = BitmapFactory.decodeStream(getContentResolver().openInputStream(Uri.parse(mCurrentPhotoPath))); imagem.setImageBitmap(bm1); }catch(FileNotFoundException fnex){ Toast.makeText(getApplicationContext(), "Foto não encontrada!", Toast.LENGTH_LONG).show(); } } } |
Temos dois métodos no exemplo de código acima, o dispatchTakePictureIntent que inicializa a câmera, e salva a foto tirada no cartão SD e o evento onActivityResult que é disparado automaticamente quando a foto termina de ser tirada para exibi-la no ImageView.
Quando for mandar rodar no simulador, lembre-se de se certificar se ele possui hardware de câmera na tela de configurações dele (caso não possua, edite-o). Na imagem abaixo você confere a foto tirada, usando o modo “emulated” de câmera no simulador do Android.
Na sequência você pode colocar botões nessa tela para editar a imagem, por exemplo, ou para exclui-la. Com alguns códigos Java você pode publicá-la em redes sociais ou mesmo envia-la por email.
Mas isso fica para a sua criatividade ou para posts futuros, não é mesmo?! 😉
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
Boa noite
como faço para contratar o seu serviço
preciso criar um app para android
Bom dia Natanael, me manda um email pelo formulário de contato, para que possamos conversar melhor.
Tentei fazer seu exemplo e deu esse erro:
Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3 cmp=com.android.camera
Você deve estar rodando em um simulador com Android 6 ou superior, certo? Neste caso a forma de solicitar a permissão é diferente, não basta apenas colocar no manifesto, tem de pedir via código, no momento que for usar. Eu vou atualizar esse post assim que possível, mas no post sobre GPS fala de como pedir essa permissão em tempo real.
Isso mesmo. estou com android 6 e android 7.1. vou aguardar sua atualização. Parabéns pelo Blog. Alta qualidade
Resolvido. Confere se agora vai funcionar pra você? Refiz todo tutorial do zero, ficou até mais simples.
android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/jkkkk_FF.jpg exposed beyond app through ClipData.Item.getUri()
?
Aqui tem a solução: https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed
Luiz,
No texto você deixa claro que irar guardar a imagem no cartão SD. Caso Meu dispositivo não tenha cartão. Como será seu funcionamento ?
Onde diz Environment.getExternalStorage… troque por Environment.getDataDirectory()