Прикрепляем картинки к заметке. Получаем изображение из галереи и камеры. Обновляем БД.

Урок #26

Урок 26 — Прикрепляем картинки к заметке. Получаем изображение из галереи и камеры. Обновляем БД.

Давайте научим наше приложение добавлять изображения к заметке. Как это будет происходить?

При редактировании заметки пользователь может нажать на кнопку прикрепления изображения, и получит выбор:

  1. Выбрать изображение из галереи.
  2. Сделать снимок с камеры.

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

При удалении изображения удаляем запись из таблицы и файл из хранилища. Приступим к реализации!

Диалог выбора изображения

Добавьте в меню CreateNoteActivity элемент, при нажатии на который будет показан диалог выбора изображения.

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

Добавьте метод в код CreateNoteActivity такой метод:

private void showImageSelectionDialog() {

}

И добавим вызов метода в onOptionsItemSelected():

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // ...

        case R.id.action_attach:
            showImageSelectionDialog();

            return true;

        default:
            return super.onOptionsItemSelected(item);
    }

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

Для отображения диалогов в Android обычно используют класс AlertDialog. Один из вариантов использования диалога — отображение списка. В случае со списком нам нужно заранее задать этот список в ресурсах, например, в strings.xml:

<string-array name="attachment_variants">
    <item>Выбрать из галереи</item>
    <item>Сделать снимок</item>
</string-array>

Теперь в методе showImageSelectionDialog() создадим и покажем диалог:

private void showImageSelectionDialog() {
    AlertDialog alertDialog = new AlertDialog.Builder(this)
            .setTitle(R.string.title_dialog_attachment_variants)
            .setItems(R.array.attachment_variants, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                }
            })
            .create();
    if (!isFinishing()) {
        alertDialog.show();
    }
}

Всё достаточно просто — создаём билдер диалога, в качестве единственного параметра конструктора передаём контекст.

Далее устанавливаем заголовок диалога (я заранее задал в ресурсах текст заголовка — сделайте это самостоятельно).

Потом устанавливаем список элементов и Listener, который будет использоваться при клике на элемент списка (второй параметр onClick() — это индекс элемента, на который кликнул пользователь).

Ну и создаём диалог, вызвав create(), после чего показываем его.

Перед показом важно убедиться, что Activity не завершается, иначе приложение крашнется — мы проверяем это вызовом метода isFinishing().

Создайте два новых метода:

private void pickImageFromGallery() {

}


lang=java
private void takePhoto() {

}

В методе onClick() в диалоге смотрим, что выбрал пользователь и вызываем соответствующий метод:

@Override
public void onClick(DialogInterface dialog, int which) {
    if (which == 0) {
        pickImageFromGallery();
    } else if (which == 1) {
        takePhoto();
    }
}

AlertDialog со списком вариантов
AlertDialog со списком вариантов

Выбор изображения из галереи

Выбор из галереи происходит уже знакомым нам образом:

  1. Кидаем Intent, в котором сообщаем, что хотим получить от какого-то приложения изображение. Пользователь самостоятельно выбирает, какое приложение использовать.
  2. Получаем результат в onActivityResult().

Как вы помните, при запуске Activity для получения какого-то результата нам потребуется некая константа, с помощью которой мы сможем понять в onActivityResult() откуда именно к нам пришёл результат. Зададим эту константу:

private static final int REQUEST_CODE_PICK_FROM_GALLERY = 1;

Теперь в методе pickImageFromGallery() начнём процедуру выбора изображения:

private void pickImageFromGallery() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/*");

    startActivityForResult(intent, REQUEST_CODE_PICK_FROM_GALLERY);
}

Переопределим метод onActivityResult():

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE_PICK_FROM_GALLERY
            && resultCode == RESULT_OK
            && data != null) {

        // Получаем URI изображения
        Uri imageUri = data.getData();

        if (imageUri != null) {
            try {

                // Получаем InputStream, из которого будем декодировать Bitmap
                InputStream inputStream = getContentResolver().openInputStream(imageUri);

                // Декодируем Bitmap
                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                Log.i("Test", "Bitmap size: " + bitmap.getWidth() + "x" + bitmap.getHeight());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}

Проверяем, правильно ли всё работает:

Выбор приложения для получения изображения
Выбор приложения для получения изображения

Выбор изображения с использованием приложения Photos
Выбор изображения с использованием приложения Photos

Выбрав изображение, видим в логах:

11-05 09:09:52.223 5509-5509/com.skillberg.notes I/Test: Bitmap size: 640x480

Работает!

Получаем изображение с камеры

Теперь пришло время научить приложение получать изображение с камеры.

Хотя этот процесс и похож на получение изображения из галереи, его реализация потребует некоторых дополнительных усилий.

Выглядит он так:

  1. Создаём файл, в который камера сохранит изображение.
  2. Кидаем Intent, в котором передаём этот файл.
  3. Получаем коллбэк в onActivityResult().

Получается, добавился ещё один шаг — нужно создать файл и передать его камере.

У каждого приложения есть возможность хранить свои файлы на SD-карте. Эти файлы могут быть как общедоступными, так и приватными. Пожалуй, мы выберем вариант с приватными файлами.

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

  • DIRECTORY_MUSIC
  • DIRECTORY_PODCASTS
  • DIRECTORY_RINGTONES
  • DIRECTORY_ALARMS
  • DIRECTORY_NOTIFICATIONS
  • DIRECTORY_PICTURES
  • DIRECTORY_MOVIES
  • DIRECTORY_DOWNLOADS
  • DIRECTORY_DCIM
  • DIRECTORY_DOCUMENTS

Нам вполне подойдёт вариант DIRECTORY_PICTURES.

Создаём файл:

@Nullable
private File createImageFile() {
    // Генерируем имя файла
    String filename = System.currentTimeMillis() + ".jpg";

    // Получаем приватную директорию на карте памяти для хранения изображений
    // Выглядит она примерно так: /sdcard/Android/data/com.skillberg.notes/files/Pictures
    // Директория будет создана автоматически, если ещё не существует
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

    // Создаём файл
    File image = new File(storageDir, filename);
    try {
        if (image.createNewFile()) {
            return image;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

На Android версий ниже, чем 4.4 для доступа к приватным файлам потребуется разрешение WRITE_EXTERNAL_STORAGE. Начиная с 4.4 это разрешение больше не требуется, поскольку эти файлы доступны только приложению, создавшему их.

Добавим разрешение в манифест:

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

Атрибут android:maxSdkVersion="18" говорит о том, что мы запрашиваем разрешение только для версий Android ниже 4.4.

Ещё один важный момент, связанный с файлами: мы будем передавать камере ссылку на файл в виде URI, а, как вы помните из прошлых уроков, начиная с Android 7 для этого требуется FileProvider.

Создадим FileProvider в манифесте:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.skillberg.notes.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">

    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />

</provider>

Для того, чтобы оно сработало, потребуется создать в ресурсах файл file_paths.xml:

Создание ресурса file_paths.xml
Создание ресурса file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>

    <external-path
        name="my_images"
        path="Android/data/com.skillberg.notes/files/Pictures" />

</paths>

Android/data/com.skillberg.notes/files/Pictures — это относительный путь директории на SD-карте, к которой мы предоставляем доступ камере. Полностью он выглядит, например, так: /storage/emulated/0/Android/data/com.skillberg.notes/files/Pictures.



Продолжение доступно на платных тарифах

А вместе с ним — проверка домашних заданий нашими менторами.

Это совсем недорого — всего от 440 ₽ в месяц!



ВЫБРАТЬ ТАРИФ



Вход

Войдите, чтобы пользоваться всеми преимуществами.
Это займёт всего пару секунд!

или