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

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

Предположим, вы реализуете службу погоды, которая делает глобальную карту погоды путем смешивания фотографии неба, принятого с устройства, на котором выполняется ваше клиентское приложение. Интеграция фотографий это только малая часть вашего приложения. Вы хотите делать фотографии с минимальной суетой, не изобретая камеру. К счастью, в большинство Android устройств уже установлено, по крайней мере одно приложение камеры. В этом уроке вы узнаете, как получить фотографию.

Запрос разрешения камеры

Если основной функцией приложения является получение фотографий, то ограничить его видимость на Google Play, чтобы оно было доступно только для устройств, которые имеют камеру. Для информирования, что ваше приложение зависит от наличия камеры, добавьте <uses-feature> тег в файл манифеста:

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

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

Снимок с помощью приложения камеры

Принципом Android является делегирование действий в другие приложения, послав Intent , который описывает то, что вы хотите сделать. Этот процесс включает в себя три части: Intent сам по себе, вызов для запуска внешней 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);
    }
}

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

Получение эскиза

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

Приложение Камера для Android кодирует фото в возвращаемом Intent доставленном в onActivityResult() как маленький Bitmap в дополнениях, с ключом "data". Следующий код извлекает изображение и отображает его в ImageView.

@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);
    }
}

Примечание: Этот эскиз изображение из "data" может быть хорошей иконкой, но не более. Работа с полноразмерным изображением занимает немного больше работы.

Сохранение полноразмерного фото

Приложение Камера для Android сохраняет фото в натуральную величину, если вы дадите ему файл, в который нужно сохранить. Вы должны обеспечить полное имя файла, в который приложение камеры должно сохранить фотографию.

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

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

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

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

Примечание: Файлы, сохраненные в каталогах, предоставленные getExternalFilesDir() удаляются, когда пользователь удаляет ваше приложение.

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

String mCurrentPhotoPath;

private File createImageFile() throws IOException {
    // Create an image file name
    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,  /* prefix */
        ".jpg",         /* suffix */
        storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = "file:" + image.getAbsolutePath();
    return image;
}

Данный метод можно использовать для создания файлов для фотографий, теперь вы можете создать и вызвать Intent так:

static final int REQUEST_TAKE_PHOTO = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // Ensure that there's a camera activity to handle the intent
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        // Create the File where the photo should go
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            // Error occurred while creating the File
            ...
        }
        // Continue only if the File was successfully created
        if (photoFile != null) {
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                    Uri.fromFile(photoFile));
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
        }
    }
}

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

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

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

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

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 в массив, который масштабируется в соответствии с размером представления. Следующий пример демонстрирует эту технику.

private void setPic() {
    // Get the dimensions of the View
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

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