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 o uso da CameraX ou, para casos de uso específicos, da Camera2. A CameraX e a 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.