Nesta lição, vamos discutir como controlar o hardware da câmera usando diretamente as APIs do framework.
Observação: esta página se refere à classe Camera, que foi descontinuada. Recomendamos usar CameraX ou, para casos de uso específicos, Camera2. CameraX e Camera2 oferecem suporte ao Android 5.0 (nível 21 da API) e versões mais recentes.
O controle direto da câmera de um dispositivo requer um código muito maior do que o necessário para solicitar imagens ou vídeos de aplicativos de câmera. Se você quer criar um aplicativo de câmera especializado ou algo totalmente integrado à IU do seu app, este guia mostra como fazer isso.
Confira os recursos relacionados abaixo:
Abrir o objeto "Camera"
Acessar uma instância do objeto Camera
é a primeira etapa do
processo de controlar a câmera diretamente. A maneira recomendada de acessar a câmera é abrir Camera
em uma linha de execução separada,
iniciada pelo método onCreate()
,
como é feito pelo aplicativo Câmera do Android. Essa é uma boa abordagem,
já que o acesso à câmera pode demorar um pouco e pode sobrecarregar a linha de execução de interface. Em uma implementação mais básica,
o processo de abrir a câmera pode ser transferido para o método onResume()
para facilitar a reutilização do código e manter o fluxo de
controle simples.
Chamar Camera.open()
vai gerar uma
exceção se a câmera já estiver sendo usada por outro app. Por isso, nós a colocamos
em um bloco try
.
Kotlin
private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() mCamera = Camera.open(id) true } catch (e: Exception) { Log.e(getString(R.string.app_name), "failed to open Camera") e.printStackTrace() false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) mCamera?.also { camera -> camera.release() mCamera = null } }
Java
private boolean safeCameraOpen(int id) { boolean qOpened = false; try { releaseCameraAndPreview(); camera = Camera.open(id); qOpened = (camera != null); } catch (Exception e) { Log.e(getString(R.string.app_name), "failed to open Camera"); e.printStackTrace(); } return qOpened; } private void releaseCameraAndPreview() { preview.setCamera(null); if (camera != null) { camera.release(); camera = null; } }
No nível 9 da API e mais recentes, o framework da câmera oferece suporte a várias câmeras. Se você usar a
API legada e chamar open()
sem um
argumento, vai utilizar a primeira câmera traseira.
Criar a visualização da câmera
Para tirar uma foto, geralmente é necessário que os usuários visualizem a imagem desejada antes de clicar
no obturador. Para fazer isso, você pode usar uma SurfaceView
para mostrar visualizações do que o
sensor da câmera está captando.
Classe de visualização
Para mostrar uma visualização, você precisa da classe dela. A
visualização requer uma implementação da interface android.view.SurfaceHolder.Callback
, que é usada para transmitir dados de
imagem do hardware da câmera ao app.
Kotlin
class Preview( context: Context, val surfaceView: SurfaceView = SurfaceView(context) ) : ViewGroup(context), SurfaceHolder.Callback { var mHolder: SurfaceHolder = surfaceView.holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } ... }
Java
class Preview extends ViewGroup implements SurfaceHolder.Callback { SurfaceView surfaceView; SurfaceHolder holder; Preview(Context context) { super(context); surfaceView = new SurfaceView(context); addView(surfaceView); // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. holder = surfaceView.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } ... }
A classe de visualização precisa ser transmitida para o objeto Camera
antes que a
visualização da imagem ao vivo possa ser iniciada, conforme mostrado na próxima seção.
Configurar e iniciar a visualização
Uma instância da câmera e a visualização relacionada precisam ser criadas em uma ordem
específica, com o objeto Camera em primeiro lugar. No snippet abaixo, o
processo de inicialização da câmera é encapsulado para que Camera.startPreview()
seja chamado pelo
método setCamera()
sempre que o usuário fizer alguma mudança na
câmera. A visualização também precisa ser reiniciada no método de callback surfaceChanged()
da classe de visualização.
Kotlin
fun setCamera(camera: Camera?) { if (mCamera == camera) { return } stopPreviewAndFreeCamera() mCamera = camera mCamera?.apply { mSupportedPreviewSizes = parameters.supportedPreviewSizes requestLayout() try { setPreviewDisplay(holder) } catch (e: IOException) { e.printStackTrace() } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. startPreview() } }
Java
public void setCamera(Camera camera) { if (mCamera == camera) { return; } stopPreviewAndFreeCamera(); mCamera = camera; if (mCamera != null) { List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes(); supportedPreviewSizes = localSizes; requestLayout(); try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. mCamera.startPreview(); } }
Modificar configurações da câmera
As configurações da câmera mudam a forma como a câmera tira fotos, do nível de zoom à compensação de exposição. Este exemplo muda somente o tamanho da visualização. Consulte o código-fonte do app Câmera para outros detalhes.
Kotlin
override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { mCamera?.apply { // Now that the size is known, set up the camera parameters and begin // the preview. parameters?.also { params -> params.setPreviewSize(previewSize.width, previewSize.height) requestLayout() parameters = params } // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. startPreview() } }
Java
@Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Now that the size is known, set up the camera parameters and begin // the preview. Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(previewSize.width, previewSize.height); requestLayout(); mCamera.setParameters(parameters); // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. mCamera.startPreview(); }
Configurar a orientação da visualização
A maioria dos aplicativos de câmera bloqueiam a tela no modo paisagem porque essa é a orientação
natural do sensor da câmera. Essa configuração não impede que você tire fotos no modo retrato,
porque a orientação do dispositivo é gravada no cabeçalho EXIF. O método setCameraDisplayOrientation()
permite que você mude
a forma como a visualização é mostrada, sem afetar como a imagem é gravada. No entanto, em versões do Android anteriores
ao nível 14 da API, é necessário interromper a visualização antes de mudar a orientação e a reiniciar.
Tirar uma foto
Use o método Camera.takePicture()
para tirar uma foto quando a visualização for iniciada. Você pode criar objetos de Camera.PictureCallback
e Camera.ShutterCallback
e os transmitir para Camera.takePicture()
.
Se você quer capturar imagens de forma contínua, crie um Camera.PreviewCallback
que implemente onPreviewFrame()
. Para
um resultado intermediário, você pode capturar somente frames de visualização selecionados ou configurar uma
ação atrasada para chamar takePicture()
.
Reiniciar a visualização
Depois que uma foto é tirada, é necessário reiniciar a visualização para que o usuário possa tirar outra foto. Neste exemplo, a reinicialização é feita sobrecarregando o botão do obturador.
Kotlin
fun onClick(v: View) { previewState = if (previewState == K_STATE_FROZEN) { camera?.startPreview() K_STATE_PREVIEW } else { camera?.takePicture(null, rawCallback, null) K_STATE_BUSY } shutterBtnConfig() }
Java
@Override public void onClick(View v) { switch(previewState) { case K_STATE_FROZEN: camera.startPreview(); previewState = K_STATE_PREVIEW; break; default: camera.takePicture( null, rawCallback, null); previewState = K_STATE_BUSY; } // switch shutterBtnConfig(); }
Parar a visualização e liberar a câmera
Depois que o aplicativo termina de usar a câmera, é hora de limpar os dados. Você
precisa liberar principalmente o objeto Camera
para não correr o risco de travar outros
aplicativos, incluindo novas instâncias do seu app.
Quando é preciso interromper a visualização e liberar a câmera? Ter a
superfície de visualização destruída é uma boa dica de que é hora de interromper a
visualização e liberar a câmera, conforme mostrado nestes métodos da classe Preview
.
Kotlin
override fun surfaceDestroyed(holder: SurfaceHolder) { // Surface will be destroyed when we return, so stop the preview. // Call stopPreview() to stop updating the preview surface. mCamera?.stopPreview() } /** * When this function returns, mCamera will be null. */ private fun stopPreviewAndFreeCamera() { mCamera?.apply { // Call stopPreview() to stop updating the preview surface. stopPreview() // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). release() mCamera = null } }
Java
@Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); } } /** * When this function returns, mCamera will be null. */ private void stopPreviewAndFreeCamera() { if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). mCamera.release(); mCamera = null; } }
Anteriormente no guia, esse procedimento também fazia parte do método setCamera()
. Portanto, para inicializar uma câmera, sempre é necessário interromper a
visualização primeiro.