Câmera Personalizada Para Android

Mobin Munir Segue Out 19, 2018 · 6 min ler A Câmera Personalizada Android é baseada na API 2 da câmera do Android

Olá! Esta é a minha primeira história aqui no meio. Estou abordando a API Camera 2 hoje. Eu fiz muita pesquisa sobre isso antes de criar meu próprio wrapper em torno da funcionalidade da câmera 2 para aumentar a legibilidade da base de código da câmera 2 e torná-la mais fácil de usar. Eu estava tão frustrado porque o Google fez um trabalho fantástico na API, mas a base de código era muito complexa para mim, então escrevi minha própria implementação no Kotlin para que ela pudesse ser incorporada em seus projetos. Eu escrevi o wrapper de tal forma que você pode modificar ainda mais a funcionalidade nele como você vê o ajuste.Você será capaz de tirar uma foto e salvá-lo no armazenamento externo sem problemas.Eu vou tentar explicar as coisas complexas em termos mais fáceis se possível. Você pode encontrar o projeto completo com amostra no Github .

Antes de prosseguir …

Essa implementação é baseada na implementação básica do Google da API Camera2. Eu enfrentei muitos problemas com a atividade de amostra de câmera personalizada que desenvolvi. Porque não consegui que o código funcionasse de maneira ideal em todos os dispositivos.

Portanto, não há garantia de que ele funcionará da mesma forma em todos os dispositivos e pode até causar um comportamento anormal também. E também concluí que essa talvez seja a razão pela qual cada dispositivo tem um aplicativo de câmera padrão diferente, irrelevante para o nível da API, devido ao hardware da câmera.

Conteúdo:

A API do Camera 2 do Android é uma das APIs mais difíceis de se trabalhar. O objetivo deste tutorial é fornecer a você um conhecimento básico sobre como consumir adequadamente a API 2 da câmera do Android e a câmera personalizada. Para começar, você poderá criar uma câmera personalizada, tirar uma foto e fornecer ao usuário a funcionalidade de configurar o flash. Em seguida, a foto tirada será salva no armazenamento externo de forma assíncrona usando o RxJava para uma experiência de usuário tranquila. E também a classe Camera2 é deixada em aberto para aprimoramentos / modificações conforme necessário para o seu trabalho.

Pré-requisitos:

Estaremos usando o Kotlin, então você deve ter configurado o RxJava para converter e salvar fotos tiradas da câmera no armazenamento. Então, suponho que você esteja familiarizado com alguns princípios básicos da programação reativa. Mesmo se você não estiver familiarizado, deixarei algumas dicas ao longo do caminho. Então, vamos trabalhar.

Vamos começar

Primeiro de tudo, você precisará adicionar essas duas permissões no seu Manifesto Android e também perguntar em tempo de execução antes de permitir que o usuário abra sua atividade de câmera. As permissões são necessárias para usar a câmera e salvar dados no armazenamento do dispositivo.

 < uses-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" /> 
< usa permissão
android: name = "android.permission.CAMERA"
/>

Então você precisará adicionar a seguinte dependência em seu arquivo build.gradle (Module).

 implementação 'io.reactivex.rxjava2: rxandroid: 2.0.2' 

A dependência acima importará o RxJava em seu projeto e otimizará ainda mais seu código usando os recursos de extensão do Kotlin.

Próximo

Basicamente, o AutoFitTextureView é um TextureView subclasse, um componente nativo que fornece a superfície para as configurações da câmera e, adicionalmente, estendido para definir a proporção para ele. Portanto, crie um layout com ele e adicione botões para ativar / desativar o flash, tirar uma foto e girar a frente / trás da câmera. Depois disso, crie um campo para Camera2 em sua classe de atividade e, em seguida, no método onCreate, inicialize-o com uma instância para sua visualização de textura declarada no layout ou no código XML.

 // Campo declarado que deve ser inicializado mais tarde. 
lateinit privado var camera2
: Camera2
anular diversão onCreate (savedInstanceState: Bundle?) {
super .onCreate (savedInstanceState)
setContentView (R.layout. activity_custom_camera_ui )
camera2 = Camera2 (camera_view) // assim
}

Agora Depois de fazer isso, você precisa chamar os métodos de classe de wrapper apropriadamente em seus métodos de clique para ações pretendidas. Então, mais tarde, para iniciar a câmera e fechá-la, substitua os métodos onPause e onResume. Todo o código da placa da caldeira foi cuidado dentro desses métodos. O método close () libera eficientemente a câmera e seus recursos retidos e o método onResume iniciará ou retomará a câmera.

 rotatecamera.setOnClickListener { 
if
( camera2 .isFlashEnabled ()) {
// atualizar a interface do usuário
}
camera2 .switchCamera ()
}

captureimage.setOnClickListener {
camera2
.takePhoto {bitmap ->
Toast.makeText (v. Contexto , "Salvando Imagem" , Toast. LENGTH_SHORT ) .show ()
Converters.convertBitmapToFile ( bitmap ) { arquivo ->
Toast.makeText (v. Contexto , "Caminho da imagem salva $ { file. Path }" , Toast. LENGTH_SHORT ) .show ()
}

}
}
iv_camera_flash_on.setOnClickListener {
camera2
.setFlash (Camera2.FLASH. ON )
isso . alfa = 1f
iv_camera_flash_auto. alpha = 0.4f
iv_camera_flash_off. alpha = 0.4f
}


iv_camera_flash_auto.setOnClickListener {
iv_camera_flash_off. alpha = 0.4f
iv_camera_flash_on. alpha = 0.4f
isso . alfa = 1f
camera2 .setFlash (Camera2.FLASH. AUTO )
}

iv_camera_flash_off.setOnClickListener {
camera2
.setFlash (Camera2.FLASH. OFF )
isso . alfa = 1f
iv_camera_flash_on. alpha = 0.4f
iv_camera_flash_auto. alpha = 0.4f

}
substituir diversão onPause () {

camera2 .close ()
super .onPause ()
}

anular diversão onResume () {
camera2 .onResume ()
super .onResume ()
}

Resumo dos métodos acima está listado abaixo.

switchCamera (): Este método irá alternar sua câmera entre frente e verso. Se o dispositivo não suportar a câmera frontal, a exceção será impressa. Basicamente, um interruptor de câmera no Android requer uma reinicialização rápida para carregar as novas configurações.

 // Este método troca a lente da câmera frontal ou traseira após o reinício da câmera. 
divertido switchCamera () {
perto()
cameraFacing = if ( cameraFacing == CameraCharacteristics. LENS_FACING_BACK )
CameraCharacteristics. LENS_FACING_FRONT
mais CameraCharacteristics. LENS_FACING_BACK
Resumindo()


}

setFlash (ON / OFF / AUTO): habilita o flash para a sessão de captura atual da câmera. Ele irá verificar primeiro se o flash é suportado. Se assim for, se as lentes da câmera estiverem voltadas para frente, o flash será ativado para a sessão atual. Tem 3 valores possíveis Auto para flash automático, ON para contínuo e OFF. Por padrão, é auto.

 divertido setFlash (flash: FLASH) { 

isso . flash = flash

if ( textureView . context . packageManager .hasSystemFeature (PackageManager. FEATURE_CAMERA_FLASH )) {
quando ( cameraFacing ) {
CameraCharacteristics. LENS_FACING_FRONT -> Log.e ( "Camera2" , "Flash frontal da câmera ainda não é suportado." )
}
}

}

takePhoto ((Bitmap) -> Unit): Este método captura a imagem renderizada e a retorna para sua atividade / fragmento como um bitmap. Esse método inicia o flash, em seguida, estabiliza a exibição e verifica se a textura está disponível para renderização se, em seguida, um bitmap for retirado da exibição e a visualização for restaurada.

 Diversão takePhoto (onBitmapReady: (Bitmap) -> Unidade) {this.onBitmapReady = onBitmapReady 
lockPreview ()
}

Então, temos a imagem capturada da câmera como um Bitmap. Agora precisamos salvar essa imagem no armazenamento do dispositivo, mas precisamos fazer isso sem comprometer a experiência do usuário. Aqui, usaremos o RxJava. Quando a foto é capturada, seu bitmap seria retornado no retorno de chamada. Então, aqui você pode fazer o seu processamento de imagem. O processo de conversão do Bitmap para o arquivo levaria algum tempo, dependendo do tamanho da imagem. Assim, certifique-se de atualizar a interface do usuário de acordo.

Use o método empregado no objeto Conversores para converter seu bitmap em Arquivo. O método de conversão exige que uma instância do seu bitmap seja convertida e compactada com um retorno de chamada para retornar o resultado como um arquivo gravado no armazenamento do dispositivo.

 // atualiza a interface do usuário para mostrar qualquer progresso no processamento de imagens. 
disposable = Converters.convertBitmapToFile ( it ) { arquivo ->
Toast.makeText (v. Contexto , "Caminho da imagem salva $ { file. Path }" , Toast. LENGTH_SHORT ) .show ()
}

Agora vamos ver o que é a mágica por trás deste método de conversão abaixo. O método RxJava observa o bitmap primário em um thread de segundo plano, no qual ocorre o processo de compactação e conversão. A assinatura, após o resultado, é descartável, já que atualmente possui recursos do sistema.

O código abaixo converte e comprime o seu bitmap para arquivo, em seguida, escreve no diretório público de armazenamento externo de fotos em pasta "Custom Camera Android".

 // Esta assinatura precisa ser descartada para liberar os recursos do sistema mantidos principalmente para o propósito. 

@JvmStatic // esta anotação é necessária para a classe de chamador escrita em Java para reconhecer este método como estático
Diversão convertBitmapToFile (bitmap: Bitmap, onBitmapConverted: (File) -> Unit): Descartáveis {
return Single.fromCallable {
compressBitmap (bitmap)
} .subscribeOn (Schedulers.io ()). observeOn (AndroidSchedulers.mainThread ())
.subscribe ( {
if
( isso ! = null ) {
Log.i ( "convertidoPicturePath" , ele. Caminho )
onBitmapConverted ( it )
}
} , {it .printStackTrace () } )
}

diversão privada compressBitmap (bitmap: Bitmap): File? {
// cria um arquivo para gravar dados de bitmap

tente {
val myStuff =
Arquivo(
Environment.getExternalStoragePublicDirectory (Ambiente. DIRECTORY_PICTURES ),
"Câmera personalizada para Android"
)
if (! myStuff.exists ())
myStuff.mkdirs ()
val picture = Arquivo (myStuff, "Mobin-" + System.currentTimeMillis () + ".jpeg" )

// Converter bitmap em array de bytes
val bos = ByteArrayOutputStream ()
bitmap.compress (Bitmap.CompressFormat. JPEG , 100 / * ignorado para PNG * / , bos)
val bitmapData = bos.toByteArray ()

// escreve os bytes no arquivo
val fos = FileOutputStream (imagem)
fos.write (bitmapData)
fos.flush ()
fos.close ()
imagem de retorno
} catch (e: IOException) {
e.printStackTrace ()
}

return null
}

Finalmente, substitua o método onDestroy de sua atividade para descartar recursos mantidos durante a conversão e seu voilà feito

 anular diversão onDestroy () { 
descartável .dispose ()
super .onDestroy ()
}

Último passo

Muito obrigado a todos que lêem isso e ainda mais àqueles que usaram isso. Deixe-me saber se você gosta disso e quaisquer tópicos que você deseja escrever sobre o Android Development. Você pode me seguir aqui para atualizações e / ou me encontrar no LinkedIn . Você pode ajudar muito estrelando e bifurcando-se no github .