Введение

Большинство телефонов на Android сейчас уже имеют по крайней мере одну камеру. Это позволяет разработчиками добавлять в приложения возможность создания фотографий. Самый простой способ работы с камерой заключается в использовании приложения, которое уже есть в составе операционной системы Android. Кроме того в составе Android Framework есть набор API для непосредственной работы с камерой. Это предоставляет разработчику большую гибкость в использовании камеры, но требует написания большего количества кода. 

В этой статье мы разберемся как делать фотографии на Android, используя встроенное приложение для работы с камерой. 

Camera Permission 

Если ваше приложение использует камеру, вам необходимо указать это в файле манифеста. Это делается с помощью тегов <uses-feature>

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...
</manifest>

Если работа с камерой - это обязательная часть вашего приложения, необходимо установить свойство android:required равным true. В этом случае приложение будет отображаться в Google Play только для тех телефонов, которые имеет камеру. 

Если работа с камерой - это всего лишь дополнительная функция приложения, без которой оно нормально работает, устанавливайте свойство android:required равным false. Приложение будет отображаться в Google Play для всех телефонов, но в этом случае нужно проверять наличие камеры в коде приложения. Это делается с помощью метода hasSystemFeature(PackageManager.FEATURE_CAMERA). 

Делаем фотографию с помощью intent

Android позволяет передать выполнение различных действий другим приложениям с помощью intent объектов. Таким образом мы можем делать фотографии, используя внешнее приложение. Данный процесс включает в себя три этапа: создание определенного intent объекта, запуск новой Activity, обработка полученных результатов в текущей Activity. 

Ниже вы видите пример кода:

static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }
}

Обратите внимание, с помощью метода resolveActivity() мы проверяем наличие приложения для работы с камерой. Если intent не может быть обработан каким-нибудь внешним приложением, то вызов метода startActivityForResult() приведет к аварии вашего приложения. 

Получение миниатюры 

Для обработки результата работы приложения камеры, в Activity вашего приложения нужно переопределить метод onActivityResult(). Ему будет передаваться intent, к которому прикреплен Bundle объект, который в свою очередь содержит ссылку на bitmap объект. Ссылку можно получить с помощью метода get() и ключа "data". Ниже вы видите пример кода. 

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
        mImageView.setImageBitmap(imageBitmap);
    }
}

Миниатюра изображения, получаемая таким способом, подходит только для иконки. Для получения полноразмерного изображения требуется немного больше кода. 

Получение полноразмерной фотографии

Приложение камеры сохраняет полноразмерную фотографию, если вы предоставляете ему ссылку на файл.  

Как правило, фотографии, которые делает пользователь, нужно сохранять в какой-то публичной директории на внешнем носителе, поскольку в этом случае фотографии будут доступны и другим приложениям. Подходящую директорию для хранения фотографий можно получить с помощью метода getExternalStoragePublicDirectory(), передав ему аргумент DIRECTORY_PICTURES. Для работы с этой директорией нужно установить в манифесте вашего приложения соответствующее разрешение.  

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

Разрешение на запись одновременно позволяет производить и чтение. 

Если вы хотите, чтобы фотографии были доступны только с помощью вашего приложения, то для получения директории используйте метод getExternalFilesDir(). На Android 4.3 и ниже, для записи в  эту директорию также требуется разрешение WRITE_EXTERNAL_STORAGE. Начиная с Android 4.4, разрешение уже не требуется, поскольку директория больше не доступна другим приложениям. Исходя из этого, можно объявить разрешение так:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>   

 

Обратите внимание, при удалении вашего приложения,  директория, возвращаемая методом getExternalFilesDir(), будет тоже удалена. 

Когда вы определите директорию для фотографии, вам нужно создать файл с уникальным именем и, возможно, сохранить путь к нему. Ниже приведен пример метода, который выполняет эту процедуру, используя текущую дату и время. 

String mCurrentPhotoPath;

private File createImageFile() throws IOException {
    // создание файла с уникальным именем     String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());     String imageFileName = "JPEG_" + timeStamp + "_";     File storageDir = Environment.getExternalStoragePublicDirectory(             Environment.DIRECTORY_PICTURES);     File image = File.createTempFile(         imageFileName,  /* префикс */         ".jpg",         /* расширение */         storageDir      /* директория */     );     // сохраняем пусть для использования с интентом ACTION_VIEW     mCurrentPhotoPath = "file:" + image.getAbsolutePath();     return image; }

Этот метод создает файл для фотографии и запускает приложение камеры с помощью интента. 

static final int REQUEST_TAKE_PHOTO = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    // проверяем, что есть приложение способное обработать интент
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        // создать файл для фотографии
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            // ошибка, возникшая в процессе создания файла
            ...
        }

        // если файл создан, запускаем приложение камеры
        if (photoFile != null) {
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                    Uri.fromFile(photoFile));
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
        }
    }
}

Добавление фотографии в галерею

Созданную фотографию можно сделать общедоступной с помощью системного Media Provider.  

Но обратите внимание! Если вы сохранили фотографию в директорию полученную с помощью метода getExternalFilesDir(), медиа сканер не сможет получить к ней доступ, потому что фотография будет в приватной директории вашего приложения.

Следующий метод демонстрирует как вызывать медиа сканер для того, чтобы добавить вашу фотографию в базу данных и сделать ее доступной в стандартном Android приложении "галерея".  

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Масштабирование фотографии

Работа с несколькими полноразмерными изображениями может быть сложной задачей в условиях ограниченной памяти. Но вы можете значительно уменьшить количество используемой памяти, преобразовав JPEG к соответствующему размеру View компонента. Следующий пример демонстрирует эту технику. 

private void setPic() {

    // получаем размеры View
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    // получаем размеры изображения
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // определяем коэффициент масштабирования
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // преобразуем фотографию в Bitmap объект соответствующий View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    mImageView.setImageBitmap(bitmap);
}

 

Тестовый проект можно скачать на GitHub.
По материалам документации
Вольный перевод - Pavel Bobkov.