Типы ячеек в RecyclerView. Обработка кликов в RecyclerView. Неявные Intent. FileProvider.

Урок 17 — Типы ячеек в RecyclerView. Обработка кликов в RecyclerView. Неявные Intent. FileProvider.

Прежде чем мы приступим к программированию, давайте сделаем следующую вещь: возьмите любой APK-файл, и положите его на SD-карту устройства.

Это можно достаточно легко сделать, используя Android Studio. Нажмите Ctrl + Shift + A (Cmd + Shift + A на macOS), в появившемся окне введите "Device File Explorer", выберите соответствующий пункт с помощью стрелок вверх или вниз, и нажмите Enter:

Окно выбора действия
Окно выбора действия

Появится вот такое окно:

Device File Explorer
Device File Explorer

В нём выберите директорию /sdcard/, нажмите на неё правой кнопкой и выберите Upload:

Загрузка файла
Загрузка файла

Далее просто выберите APK-файл, и Android Studio загрузит его на виртуальную SD-карту устройства.

Различные типы данных в RecyclerView

Менеджер файлов, как ни странно, отображает файлы. А файлы (внезапно!) могут оказаться директориями. Было бы неплохо уметь отличать директории от файлов, причем как внешне, так и, например, при обработке кликов на элемент списка.

Благо, разработчики Android позаботились о нас и добавили возможность отличать различные типы данных в RecyclerView.

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

Это не совсем практично, ведь можно было обойтись и одним файлом, но в качестве демо отлично подойдёт.

Итак, просто скопируйте лэйаут файла и вставьте его в ту же директорию res/layout, но с именем view_item_directory.xml:

Файл вёрстки ячейки директории
Файл вёрстки ячейки директории

Vector Asset

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

Нажмите правой кнопкой на директорию res/, затем выберите New -> Vector Asset:

Создание векторного изображения
Создание векторного изображения

В качестве имени введите ic_directory_92dp, затем нажмите на кнопку Icon, в появившемся окне введите "folder" и выберите иконку:

Выбор иконки для векторного изображения
Выбор иконки для векторного изображения

Напротив Size поставьте галочку Override и измените размеры на 92x92:

Размеры векторного изображения
Размеры векторного изображения

Нажмите Next, а затем — Finish. В директории res/drawable создался файл с векторным изображением:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="92dp"
    android:height="92dp"
    android:viewportHeight="24.0"
    android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" />
</vector>

То же самое проделываем с файлом, только выбираем другую иконку и называем его ic_file_92dp:

Выбор иконки для другого векторного изображения
Выбор иконки для другого векторного изображения

Теперь открываем view_item_file.xml и вносим следующие изменения в ImageView:

<ImageView
        android:id="@+id/icon_iv"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:adjustViewBounds="true"
        android:src="@drawable/ic_file_92dp" />

Я немного промахнулся с размерами, указав 92dp48 в самый раз. Благо, мы можем без проблем задать размеры прямо в ImageView.

Проделайте то же самое с view_item_directory.xml, но используйте иконку для директории.

После этого запустите приложение:

Список файлов в RecyclerView
Список файлов в RecyclerView

Хм, непорядок. Все, что видно на скриншоте — директории, а показываются файлы. Почему? Потому что мы ещё не реализовали обработку различающихся типов данных, чем сейчас и займёмся.

ItemViewType в RecyclerView

Как я сказал ранее, мы можем различать типы данных в адаптере RecyclerView. Благодаря этому мы можем создавать различные View для каждого типа данных, которые у нас есть. Откройте адаптер.

Для начала для удобства создадим две константы, которые будут отвечать за два типа данных: файл и директорию

private static final int TYPE_DIRECTORY = 0;
private static final int TYPE_FILE = 1;

Теперь переопределим метод getItemViewType(). В него в качестве параметра передается индекс элемента, по этому индексу мы получаем элемент и возвращаем соответствующую ему константу типа данных:

@Override
public int getItemViewType(int position) {
    File file = files.get(position);
    if (file.isDirectory()) {
        return TYPE_DIRECTORY;
    } else {
        return TYPE_FILE;
    }
}

Теперь нужно немного дополнить метод onCreateViewHolder(), в котором создаётся View для соответствующей ячейки. В него вторым параметром передаётся тип данных, который адаптер берёт как раз из метода getItemViewType(). В соответствии с типом данных мы будем создавать View для файла или директории:

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
    View view;
    if (viewType == TYPE_DIRECTORY) {
        view = layoutInflater.inflate(R.layout.view_item_directory, parent, false);
    } else {
        view = layoutInflater.inflate(R.layout.view_item_file, parent, false);
    }
    return new ViewHolder(view);
}

Наш случай весьма прост, и поэтому данного кода будет достаточно. В более сложных приложениях может потребоваться изменить onBindViewHolder() и, возможно, добавить ещё несколько ViewHolder-ов в случае, если лэйауты сильно отличаются. В нашем же случае лэйауты отличаются лишь картинкой, поэтому делать этого не придётся.

Запустите приложение, и увидите, что теперь директории и файлы отображаются корректно:

Отображение файлов и директорий
Отображение файлов и директорий

Обработка кликов в RecyclerView

Теперь мы должны добавить обработку кликов на элементы списка. В случае клика на директорию приложение должно открыть соответствующую директорию, а если пользователь кликнет на файл — выбрать файл, если тип файла соответствует нужному нам.

Каждый View в Android может иметь тэг. Не стоит путать его с XML-тэгами: тэг у View — это объект, ассоциированный с ним. С помощью тэга мы можем "сохранить" во View любые данные.

Конкретно в нашем случае он нужен, чтобы ассоциировать ячейку списка с конкретным файлом, который на данный момент отображается в ячейке. Зададим тэг в методе onBindViewHolder():

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    File file = files.get(position);
    holder.nameTv.setText(file.getName());
    holder.itemView.setTag(file);
}

Теперь дополним конструктор ViewHolder, добавив туда OnClickListener:

public ViewHolder(View itemView) {
    super(itemView);
    nameTv = itemView.findViewById(R.id.name_tv);
    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });
}

В методе onClick() мы получаем View, на который кликнул пользователь. В нём же мы можем получить тэг этого View, а соответственно и файл, который был установлен в качестве тэга:

File file = (File) view.getTag();

Обратите внимание, что тэг имеет тип Object, поэтому нам нужно привести его к типу File.


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

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


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


Продолжение доступно после регистрации

Все уроки на сайте доступны абсолютно бесплатно после регистрации.

Регистрация займёт меньше минуты ;)