- У нас есть фотик и котик — работаем с камерой
- Программное включение приложения Камера
- Делаем фотографии и сохраняем результат. Простой пример
- Миниатюра и полноразмерное изображение
- Запуск камеры в нужном режиме
- Снимаем и кадрируем
- Camera and Gallery Access on Android with Kotlin
- 1-Camera Integration
- 2-Choose Photo on Gallery
- Rickyip / CameraXFragment.kt
У нас есть фотик и котик — работаем с камерой
Практически все современные телефоны и планшеты снабжаются камерами, что позволяет фотографировать любимых котиков.
Сначала сделаем небольшие приготовления. Есть класс устройств, у которых нет камер, например, электронные ридеры. Чтобы пользователи этих устройств не скачивали зря ваше приложение, которое окажется для них бесполезным, пропишем в манифесте требование наличия камеры.
Если камера в вашем приложении выполняет вспомогательную функцию, а приложение может работать и без неё, то установите значение false в предыдущем коде и проверяйте наличие камеры программно. Если камеры нет, то отключайте возможность съёмок для пользователя.
Программное включение приложения Камера
Раньше на старых телефонах можно было программно запустить из своей программы системное приложение «Камера» (в этом случае вам не понадобятся дополнительные разрешения) через намерение.
Особого смысла в этом нет, так как результат не возвращается в приложение. Но вполне подходило для быстрого запуска камеры. Впрочем, сейчас этот код может и не сработать.
А вообще у пользователя могут стоять разные приложения, способные фотографировать. Тогда у вас будет появляться диалоговое окно с выбором нужного приложения. Они все имеют в своём составе такую запись в манифесте (для общего развития):
У Гугла есть своя программа Google Камера. Запустим её, зная имя пакета.
При вызове метода getIntent() вместо new Intent() приложение запускалось сразу, иначе — выводилось диалоговое окно выбора программы из списка. Также нужно быть уверенным, что программа установлена, в примере нет кода проверки.
Делаем фотографии и сохраняем результат. Простой пример
Просто включить камеру не слишком интересно. Рассмотрим практичный пример, когда мы программно запустим приложение «Камера», а полученную фотографию сохраним в папке. Сначала сделаем простой вариант, а потом напишем более сложное приложение.
Используйте статическую константу MediaStore.ACTION_IMAGE_CAPTURE для создания намерения, которое потом нужно передать методу startActivityForResult(). Разместите на форме кнопку и ImageView, в который будем помещать полученный снимок. Этот код запускает стандартное приложение камеры. Полученное с камеры изображение можно обработать в методе onActivityResult():
Не включайте в манифест разрешение на работу с камерой, иначе получите крах приложения.
Данный код запустит приложение, работающее с камерой, позволяя пользователю поменять настройки изображения, что освобождает вас от необходимости создавать своё собственное приложение для этих нужд. Вполне возможно, что у вас будет несколько приложений, умеющих делать фотографии, тогда сначала появится окно выбора программы.
При тестировании примера на своём телефоне я обнаружил небольшую проблему — когда снимок передавался обратно на моё приложение, то оно находилось в альбомном режиме, а потом возвращалось в портретный режим. При этом полученный снимок терялся. Поэтому перед нажатием кнопки я поворачивал телефон в альбомный режим, чтобы пример работал корректно. Возможно следует предусмотреть подобное поведение, например, запретить приложению реагировать на поворот и таким образом избежать перезапуска Activity. У некоторых телефонов такой проблемы нет.
По умолчанию фотография возвращается в виде объекта Bitmap, содержащего миниатюру. Этот объект находится в параметре data, передаваемом в метод onActivityResult(). Чтобы получить миниатюру в виде объекта Bitmap, нужно вызвать метод getParcelableExtra() из намерения, передав ему строковое значение data. В примере использовался упрощённый вариант.
Миниатюра и полноразмерное изображение
Если вы укажете исходящий путь URI с помощью параметра MediaStore.EXTRA_OUTPUT в запущенном намерении, полноразмерное изображение, снятое камерой, сохранится в заданном месте. В таком случае в метод onActivityResult() не будет передана миниатюра, а итоговое намерение продемонстрирует значение null.
В следующем примере показано, как при создании снимка получать миниатюру или полноценное изображение, используя намерение. Изображение будет сохранено во внешнем хранилище под именем test.jpg.
Пример работает на устройствах до Android 6.0, потом появились различные системные ограничения и нужно писать дополнительный код.
Добавим разрешение на запись файла в хранилище.
В реальных приложениях для создания имён файлов используют текущую дату, чтобы обеспечить уникальность и не затереть существующую фотографию.
Запуск камеры в нужном режиме
Мы можем выбрать намерение, позволяющее включить камеру в нужном режиме: фотосъёмка или видео.
Снимаем и кадрируем
Рассмотрим ещё один пример с режимом кадрирования. Основная часть кода остаётся прежней. Рекомендую проверять работу с камерой на реальных устройствах, так как многие производители заменяют стандартные методы съёмки своими прошивками и драйверами. В частности, намерение с кадрированием является проблемной, и в интернете многие жалуются на отсутствие поддержки этого способа. Пример для старых устройств до Android 6.0.
Создадим простенький макет из кнопки для запуска камеры и ImageView для вывода кадрированного изображения.
Для большей красоты сделаем задний фон у ImageView с закруглёнными углами и обводкой. Для этого в атрибуте android:background мы прописали специальный стиль. Создайте папку res/drawable, а в ней файл background.xml следующего содержания:
Этот шаг не является обязательным и его можно пропустить.
При нажатии кнопки запускаем приложение Камера и ожидаем результата.
После того, как пользователь сделал нужный кадр, программа Камера возвращает результат обратно в наше приложение. Результат обрабатывается в методе onActivityResult():
Получив полноразмерное изображение, мы пытаемся откадрировать его. Для этого создадим метод performCrop(), который запускает специальное намерение, предназначенное для этих целей. В успешном случае результат снова возвращается в наше приложение, но уже с другим кодом PIC_CROP. Теперь мы имеем нужное изображение, которое можно вывести на экран.
При кадрировании мы указываем желаемые размеры (код метода ниже). Если указать слишком больше размеры (больше 400), то результат не возвращается. Попробуйте добавить ещё два параметра:
Результат работы приложения, когда запускается намерение кадрирования и итоговый результат. Желательно тренироваться на кошках.
Источник
Camera and Gallery Access on Android with Kotlin
In this article, I want to talk about how to activate the camera on Android.
1-Camera Integration
First we create an empty project in Android Studio.
After creating an empty project in Android Studio, we add a button and ImageView in our XML file. So, the last version of the XML file will be like this.
After completing the screen design, we go to the MainActivity.kt folder. We have defined intent in our setOnClickListener code block of our button. We passed the intent we defined to the takePhotoIntent variable. Then we define a provider file. Again, we create a variable and send it to the providerFile variable that we created. We write the package name of the created project into the “getUriForFile” parameter.
Note: We encode “provider” and “meta-data” in the “AndroidManifest.xml” file.
We are coding two functions. First of these functions, we create a function to import your photo file. Inside this function we define the type of photo file.
The second function is that we use the bitmap method to verify the captured photos.
What is a Bitmap? Bitmap is a frequently used expression for photos and images. It is the general expression given to the picture formats in which all the color information required to display a photograph or picture on the computer screen is given the pixel color information that makes up the screen.
2-Choose Photo on Gallery
After completing the camera integration process, we add another button in the design section to select a photo from the gallery.
Then we provide our conditions into the btnChoosePhoto.setOnClickListener code block in the MainActivity.kt file. And encode IMAGE_CHOOSE ,PERMISSION_CODE variables into the companion object code block.
We need a function to select a photo from the gallery. We are coding the function named chooseImageGallery . In it we define the variable intent and specify its type.
Note: We cannot create Static Properties and Static Function in Kotlin. Therefore, we need to use Companion Object to become a static properties. Thus, we can code the property that we want to be static inside the object.
We have defined the onRequestPermissionsResult override function for which we will make a request.
And finally we provide the condition to the onActivityResult function.
I hope my article was useful. Hope to see you in the next article. 😊
Источник
Rickyip / CameraXFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
import android.Manifest |
import android.annotation.SuppressLint |
import android.content.Context |
import android.content.pm.PackageManager |
import android.graphics.drawable.GradientDrawable |
import android.net.Uri |
import android.os.Bundle |
import android.util.Log |
import android.view.LayoutInflater |
import android.view.View |
import android.view.ViewGroup |
import android.widget.Toast |
import androidx.camera.core.* |
import androidx.camera.lifecycle.ProcessCameraProvider |
import androidx.core.app.ActivityCompat |
import androidx.core.content.ContextCompat |
import androidx.fragment.app.Fragment |
import kotlinx.android.synthetic.main.fragment_camera_x.* |
import org.opencv.android.OpenCVLoader |
import org.opencv.android.Utils |
import org.opencv.core.Mat |
import java.io.File |
import java.nio.ByteBuffer |
import java.text.SimpleDateFormat |
import java.util.* |
import java.util.concurrent.ExecutorService |
import java.util.concurrent.Executors |
import kotlin.math.abs |
typealias CornersListener = () -> Unit |
class CameraXFragment : Fragment () < |
private var preview : Preview ? = null |
private var imageCapture : ImageCapture ? = null |
private var imageAnalyzer : ImageAnalysis ? = null |
private var camera : Camera ? = null |
private lateinit var safeContext : Context |
private lateinit var outputDirectory : File |
private lateinit var cameraExecutor : ExecutorService |
override fun onAttach ( context : Context ) < |
super .onAttach(context) |
safeContext = context |
> |
private fun getStatusBarHeight (): Int < |
val resourceId = safeContext.resources.getIdentifier( » status_bar_height » , » dimen » , » android » ) |
return if (resourceId > 0 ) < |
safeContext.resources.getDimensionPixelSize(resourceId) |
> else 0 |
> |
override fun onCreateView ( inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ): View ? < |
// Inflate the layout for this fragment |
return inflater.inflate( R .layout.fragment_camera_x, container, false ) |
> |
override fun onViewCreated ( view : View , savedInstanceState : Bundle ? ) < |
super .onViewCreated(view, savedInstanceState) |
// Request camera permissions |
if (allPermissionsGranted()) < |
startCamera() |
> else < |
ActivityCompat .requestPermissions(activity !! , REQUIRED_PERMISSIONS , REQUEST_CODE_PERMISSIONS ) |
> |
// Setup the listener for take photo button |
camera_capture_button.setOnClickListener |
outputDirectory = getOutputDirectory() |
cameraExecutor = Executors .newSingleThreadExecutor() |
// cameraExecutor = Executors.newCachedThreadPool() |
> |
private fun startCamera () < |
OpenCVLoader .initDebug() |
val cameraProviderFuture = ProcessCameraProvider .getInstance(safeContext) |
cameraProviderFuture.addListener( Runnable < |
// Used to bind the lifecycle of cameras to the lifecycle owner |
val cameraProvider : ProcessCameraProvider = cameraProviderFuture.get() |
// Preview |
preview = Preview . Builder ().build() |
imageCapture = ImageCapture . Builder ().build() |
imageAnalyzer = ImageAnalysis . Builder ().build(). apply < |
setAnalyzer( Executors .newSingleThreadExecutor(), CornerAnalyzer < |
val bitmap = viewFinder.bitmap |
val img = Mat () |
Utils .bitmapToMat(bitmap, img) |
bitmap?.recycle() |
// Do image analysis here if you need bitmap |
>) |
> |
// Select back camera |
val cameraSelector = CameraSelector . Builder ().requireLensFacing( CameraSelector . LENS_FACING_BACK ).build() |
try < |
// Unbind use cases before rebinding |
cameraProvider.unbindAll() |
// Bind use cases to camera |
camera = cameraProvider.bindToLifecycle( this , cameraSelector, imageAnalyzer, preview, imageCapture) |
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider()) |
> catch (exc : Exception ) < |
Log .e( TAG , » Use case binding failed » , exc) |
> |
>, ContextCompat .getMainExecutor(safeContext)) |
> |
private fun takePhoto () < |
// Get a stable reference of the modifiable image capture use case |
val imageCapture = imageCapture ? : return |
// Create timestamped output file to hold the image |
val photoFile = File (outputDirectory, SimpleDateFormat ( FILENAME_FORMAT , Locale . US ).format( System .currentTimeMillis()) + » .jpg » ) |
// Create output options object which contains file + metadata |
val outputOptions = ImageCapture . OutputFileOptions . Builder (photoFile).build() |
// Setup image capture listener which is triggered after photo has |
// been taken |
imageCapture.takePicture(outputOptions, ContextCompat .getMainExecutor(safeContext), object : ImageCapture . OnImageSavedCallback < |
override fun onError ( exc : ImageCaptureException ) < |
Log .e( TAG , » Photo capture failed: $ |
> |
override fun onImageSaved ( output : ImageCapture . OutputFileResults ) < |
val savedUri = Uri .fromFile(photoFile) |
val msg = » Photo capture succeeded: $savedUri « |
Toast .makeText(safeContext, msg, Toast . LENGTH_SHORT ).show() |
Log .d( TAG , msg) |
> |
>) |
> |
override fun onPause () < |
super .onPause() |
isOffline = true |
> |
override fun onResume () < |
super .onResume() |
isOffline = false |
> |
private fun allPermissionsGranted () = REQUIRED_PERMISSIONS .all < |
ContextCompat .checkSelfPermission(safeContext, it) == PackageManager . PERMISSION_GRANTED |
> |
override fun onRequestPermissionsResult ( requestCode : Int , permissions : Array out String >, grantResults : IntArray ) < |
if (requestCode == REQUEST_CODE_PERMISSIONS ) < |
if (allPermissionsGranted()) < |
startCamera() |
> else < |
Toast .makeText(safeContext, » Permissions not granted by the user. » , Toast . LENGTH_SHORT ).show() |
// finish() |
> |
> |
super .onRequestPermissionsResult(requestCode, permissions, grantResults) |
> |
fun getOutputDirectory (): File < |
val mediaDir = activity?.externalMediaDirs?.firstOrNull()?. let < |
File (it, resources.getString( R .string.app_name)). apply |
> |
return if (mediaDir != null && mediaDir.exists()) mediaDir else activity?.filesDir !! |
> |
companion object < |
val TAG = » CameraXFragment « |
private const val FILENAME_FORMAT = » yyyy-MM-dd-HH-mm-ss-SSS « |
internal const val REQUEST_CODE_PERMISSIONS = 10 |
private val REQUIRED_PERMISSIONS = arrayOf( Manifest .permission. CAMERA ) |
var isOffline = false // prevent app crash when goes offline |
> |
private class CornerAnalyzer ( private val listener : CornersListener ) : ImageAnalysis.Analyzer < |
private fun ByteBuffer. toByteArray (): ByteArray < |
rewind() // Rewind the buffer to zero |
val data = ByteArray (remaining()) |
get(data) // Copy the buffer into a byte array |
return data // Return the byte array |
> |
@SuppressLint( » UnsafeExperimentalUsageError » ) |
override fun analyze ( imageProxy : ImageProxy ) < |
if ( ! isOffline) < |
listener() |
> |
imageProxy.close() // important! if it is not closed it will only run once |
> |
> |
> |
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Источник