sábado, 13 de abril de 2019

Android - Utilizando a câmera – parte 2


Caro amigo leitor, neste mês irei continuar escrevendo sobre a utilização de câmeras em dispositivos móveis, tendo como plataforma de desenvolvimento o Android Studio. No mês de Novembro, no artigo “Android – Utilizando a Câmera”, foi desenvolvido a classe base “CameraVisualizacao” a qual agrega alguns métodos e algumas funcionalidades básicas. Para este mês teremos como base o artigo do mês anterior com algumas mudanças na classe “CameraVisualizacao” e na Activity principal. A maior funcionalidade implementada será a capacidade de salvarmos a foto tirada pelo aplicativo. Então, mãos a obra.
Para isto separei este artigo com os seguintes tópicos:
- Modificação do arquivo “AndroidManifest.xml”
- Modificação do lay-out principal.
- Alteração e criação de novos métodos na classe “CameraVisualizacao”
- Implementação de novas rotinas na atividade principal “MainActivity”.

Criando o Exemplo

Seguindo como base o exemplo criado no mês passado, o primeiro passo seria a alteração do arquivo de configuração “AnroidManifest.xml”. Iremos inserir duas linhas conforme código destacado abaixo:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   
package="com.example.thiago.appcamera">

    <
uses-permission android:name="android.permission.CAMERA" />
    <
uses-feature android:name="android.hardware.camera" />
    <
uses-feature android:name="android.hardware.camera.autofocus" />
    <
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

A funcionalidade “android.hardware.camera.autofocus”, irá nos possibilitar recuperar automaticamente o foco da imagem. Já o “android.permission.WRITE_EXTERNAL_STORAGE” irá permitir a gravação da foto em nosso dispositivo no formato em que desejarmos.


Modificação do lay-out principal

No nosso lay-out principal adicionaremos um “Button” para fotografar. Ver Imagem 01.
Figura 01. Layout Principal.

O XML correspondente poderemos conferir abaixo.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:orientation="vertical"
   
android:layout_width="fill_parent"
   
android:layout_height="fill_parent"
   
android:padding="15sp"
   
>
    <
SurfaceView  android:id="@+id/area_view"
       
android:layout_width="300sp"
       
android:layout_height="0dp"
       
android:layout_gravity="center_horizontal"
       
android:layout_weight="1"
       
/>
    <
Button android:id="@+id/bt_fotografar"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content"
       
android:text="Fotografar"
       
/>
</
LinearLayout>



Alteração e criação de novos métodos na classe “CameraVisualizacao”

Implementaremos alguns novos métodos, como por exemplo o “TirarFoto” e declaramos algumas novas propriedades: a “visualizacao” e a “surfaceView”.

public class CameraVisualizacao  implements SurfaceHolder.Callback
{
   
private SurfaceHolder mHolder;
   
private Camera mCamera;
   
private SurfaceView surfaceView;
   
private boolean visualizacao = false;


   
public CameraVisualizacao(Activity atividade, int surfaceView)
    {
       
this.surfaceView = (SurfaceView) atividade.findViewById(surfaceView);
       
mHolder = this.surfaceView.getHolder();
       
mHolder.addCallback(this);

    }

Inicializaremos a variável “surfaceview” para logo em seguida invocarmos o método “getHolder”. O “surfaceCreated” é chamado imediatamente após o “SurfaceHolder” ser criado.

   
/**
     * @param
holder
    
*/
   
@Override
   
public void surfaceCreated(SurfaceHolder holder)
    {
       
mCamera = android.hardware.Camera.open();
       
mCamera.setDisplayOrientation(90);
    }

Usaremos o método “Open” e o “setDisplayOrientation” para iniciar e definir uma orientação para a câmera.


    public void iniciarVisualizacao() {
       
visualizacao = true;
       
mCamera.startPreview();
    }

Inicia o Preview da Câmera.

   
public void pararVisualizacao() {
       
mCamera.stopPreview();
       
visualizacao = false;
    }

Para o Preview da Câmera.


   
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
       
if (visualizacao) {
            pararVisualizacao();
        }

       
if (mCamera != null) {
           
try {
               
mCamera.setPreviewDisplay(mHolder);
                iniciarVisualizacao();
            }
catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Chamaremos o método “surfaceChanged” imediatamente após as alterações estruturais. Com o auxílio dos métodos “pararVisualizacao” e “iniciarVisualizacao” conseguimos parar e iniciar o preview da Câmera.

     *
     * @param
holder
    
*/
   
@Override
   
public void surfaceDestroyed(SurfaceHolder holder) {
        pararVisualizacao();
       
mCamera.release();
       
mCamera = null;
    }

Usaremos este método para liberarmos o objeto “mCamera” da memória e pararmos o preview. Importante salientar que é chamado imediatamente antes do SurfaceHolder ser destruído.


   
public void tirarFoto(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg) {
       
mCamera.takePicture(shutter, raw, jpeg);
    }

}

Deveremo tratar a ação de tirar foto, como notamos, o método “tirarFoto()” recebe três parâmetros, sendo:
Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg
O primeiro é referente a ação de tirar foto. Já o segundo e o terceiro é o retorno da imagem. Podemos recebê-la no formato RAW (segundo parâmetro) ou em JPEG (terceiro parâmetro), normalmente o modo RAW não é utilizado, por isso podemos passar o valor como null.
Implementação de novas rotinas na atividade principal “MainActivity”

O nosso controle será realizado por um “Button” e um “SurfaceView” conforme mencionado anteriormente.

import android.app.Activity;
import android.os.Environment;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;

import java.io.File;
import java.io.FileOutputStream;

Usaremos algumas bibliotecas para trabalhar com Imagens, Câmeras e Arquivos. (Conforme Seleção acima)

public class CameraActivity extends Activity implements View.OnClickListener, Camera.ShutterCallback, Camera.PictureCallback {

Precisaremos da implementação de alguns métodos específicos da Câmera, conforme mencionado anteriormente.

   
private CameraVisualizacao mCameraView;
   
private boolean emCamera;
   
private Button botaoCamera;

   
@Override
   
public void onCreate(Bundle savedInstanceState)
    {
       
super.onCreate(savedInstanceState);
        setContentView(R.layout.
activity_camera);
       
emCamera = true;
       
mCameraView = new CameraVisualizacao(this, R.id.area_view);
       
botaoCamera = (Button) findViewById(R.id.bt_fotografar);
       
botaoCamera.setOnClickListener(this);
    }

“No evento OnCreate” inicializaremos a classe “CameraVisualizacao” passando os parâmetros “Activity” e a “SurfaceView“. Faremos o uso do evento “Onclick” do “Button”.

   
public void onClick(View view)
    {
       
switch (view.getId())
        {
           
case R.id.bt_fotografar:
               
if (emCamera)
                {
                   
mCameraView.tirarFoto(this, null, this);
                }
               
else
               
{
                   
emCamera = true;
                   
botaoCamera.setText("Fotografar");
                   
mCameraView.iniciarVisualizacao();
                }
               
break;
        }
    }

No evento “onClick” analizaremos a variável booleana “emCamera” para Tirar Foto e Parar o Preview da Câmera.

   
public void onShutter() {
       
botaoCamera.setText("Câmera");
       
emCamera = false;
    }
Este é o evento responsável pela ação do clique ao tirar a foto.
    

/*
     * @param
bytes
    
* @param camera
    
*/
   
public void onPictureTaken(byte[] bytes, Camera camera) {

        Bitmap foto = BitmapFactory.decodeByteArray(bytes,
0, bytes.length);

        String arquivo =
"theclub.png";
        File sd = Environment.getExternalStorageDirectory();
        File dest =
new File(sd, arquivo);

       
try {
            FileOutputStream out =
new FileOutputStream(dest);
            foto.compress(Bitmap.CompressFormat.
PNG, 90, out);
            out.flush();
            out.close();
        }
catch (Exception e) {
            e.printStackTrace();
        }

       
mCameraView.pararVisualizacao();
    }
}

Já o evento “onPictureTaken” receberá como parâmetro uma variável do tipo “byte[]” e outra do tipo “Camera”. Criaremos um objeto “Bitmap” a qual armazenaremos em memória o conteúdo fotografado. Definiremos um nome para nossa foto (Ex: theclub.png) e com o comando “getExternalStorageDirectory” recuperaremos o diretório de nosso cartão “SD”. A classe “FileOutputStream” e a “File” fará o trabalho de gravar tudo que está na memória no diretório de nosso dispositivo móvel.
Podemos conferir o aplicativo funcionando na Imagem 02.

Figura 02: Aplicativo funcionando.


Conclusões

Além de implementar mais alguns recursos na classe “CameraVisualizacao”, pudemos aprender como fotografar e armazenar arquivos do tipo imagens em dispositivos móveis. Acredito que este exemplo servirá de base para quem desejar implantar este tipo de funcionalidade em seus sistemas.
Vou ficando por aqui. Um forte abraço e até o ano que vem!