Файлы ресурсов готовы, настало время изменения кода. Создайте новый класс с названием FileAdapter и измените его код таким образом:
// Класс адаптера для привязки и показа данных в RecyclerView
public class FileAdapter extends RecyclerView.Adapter<FileAdapter.ViewHolder> {
// Локальный массив для хранения файлов
private List<File> items;
// Ассоциативный массив (словарь) - массив с ключами и значениями, в отличии от обычного массива в котором ключи это числовые индексы позиции элементов, в HashMap ключом может быть объект любого класса
private HashMap<String, Bitmap> thumbs = new HashMap<>();
// Контекст в котором заружен RecyclerView к которому прикреплен этот адаптер
private Context context;
// По значению этой переменной определяется генерация миниатюр для изображений или видео
int type = 0;
// Функция для обновления списка для показа новых добавленных файлов
public void UpdateDataSet(File[] items) {
// Обновить массив, используя конвертацию обычного массива в ArrayList
this.items = new ArrayList<>(Arrays.asList(items));
// Вызвать функцию для привязки элементов из нового источника к RecyclerView
notifyDataSetChanged();
}
// Вложенный класс класса FileAdapter для хранения объектов элементов RecyclerView
static class ViewHolder extends RecyclerView.ViewHolder {
// Ссылка на TextView в котором выводится заголовок файла
TextView title;
// Ссылка на TextView для вывода размера файла
TextView size;
// Ссылка на ImageView для показа миниатюры изображения или видео
ImageView thumb;
ImageButton rename;
ImageButton delete;
// Конструктор класса для присвоения переменным соответствующих объектов из файла макета
ViewHolder(View itemView) {
// Необходимо вызвать суперкласс ViewHolder-а и передать корневой View
super(itemView);
// Получить ссылку к виджетам в корневом View
title = itemView.findViewById(R.id.fileTitle);
thumb = itemView.findViewById(R.id.thumb);
size = itemView.findViewById(R.id.size);
rename = itemView.findViewById(R.id.rename);
delete = itemView.findViewById(R.id.delete);
}
}
// Конструктор класса FileAdapter где получаются значения
// из вызываюшего кода и присваиваются локальным переменным класса
FileAdapter(Context context, File[] items, int type) {
this.items = new ArrayList<>(Arrays.asList(items));
this.context = context;
this.type = type;
}
// Функция onCreateViewHolder вызывается при первом создании объекта для хранения элементов списка
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// Получаем View объект созданный на основе написанной структуры макета в xml файле file_item.xml
View v = LayoutInflater.from(context).inflate(R.layout.file_item, parent, false);
// Создаётся новый объект класса ViewHolder и ему передаётся объект View полученный из файла макета
return new ViewHolder(v);
}
// Функция вызывается при присвоении значений переменным класса ViewHolder
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
holder.title.setText(items.get(position).getName());
holder.size.setText(String.format(Locale.ROOT, "Размер: %d Кб", (int) ((float) items.get(position).length() / (float) 1024)));
if (thumbs.containsKey(items.get(position).getAbsolutePath())) {
holder.thumb.setImageBitmap(thumbs.get(items.get(position).getAbsolutePath()));
} else {
holder.thumb.setImageDrawable(context.getResources().getDrawable(R.drawable.placeholder));
GenerateThumb gt = new GenerateThumb();
// Запустить поток для генерации миниатюр для изображений или видео
gt.execute(items.get(position).getAbsolutePath(), holder.thumb);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent fullScreen = new Intent(context, FullScreenViewActivity.class);
fullScreen.putExtra("type", type);
fullScreen.putExtra("path", items.get(position).getAbsolutePath());
context.startActivity(fullScreen);
}
});
// Установить обработчик нажатия на кнопку редактирования объекта
holder.rename.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Инициализация диалогового окна для переименования файла
RenameDialog renameDialog = new RenameDialog();
// Объект для хранения параметров для передачи в диалоговое окно переименования файла
Bundle args = new Bundle();
// Добавить название файла и позицию файла с ключами name и position в парметры
// передаваемые в диалоговое окно переименования файла
args.putString("name", items.get(position).getName());
args.putInt("position", position);
// Установить аргументы для передачи
renameDialog.setArguments(args);
// Указать ссылку на адаптер, для получения доступа к адаптеру через диалоговое окно переименования файла
renameDialog.adapter = FileAdapter.this;
// Показать диалоговое окно для переименования файла
renameDialog.show(((AppCompatActivity) context).getSupportFragmentManager(), "RenameDialog");
}
});
// Установить обработчик клика на кнопку удалить
holder.delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Заключить попытку аделния файла в блок try catch для перехвата ошибок и предотвращения сбоев в работе приложения
try {
// Объявить временную переменную для хранения выбранного для удаление файла
File f = new File(items.get(position).toURI());
// Функция File.delete() возвращает true при удалении файла без ошибок, здесь проверяется удалился ли файл, если да тогда...
if (f.delete()) {
// ... удалить объект из массива
items.remove(position);
// сообщить адаптеру что объект удален, нужно для удаления объекта из списка и анимации
notifyItemRemoved(position);
// сообщить адаптеру что объекты в указанном диапазоне изменились, нужно для корректировки новых позиции элементов
notifyItemRangeChanged(position, getItemCount());
// сообщить пользователю что файл удален
Toast.makeText(context, "Файл удален", Toast.LENGTH_SHORT).show();
} else {
// Если вызывается эта часть кода значит фалй не был удален
Toast.makeText(context, "Файл не удалён", Toast.LENGTH_SHORT).show();
}
// В случае возникновения ошибок перехватить их и обработать здесь
} catch (Exception e) {
// Вывод в консоль информацию о возникшей ошибке
e.printStackTrace();
}
}
});
}
// Функция для переименования файла
public void renameFile(String newName, int position) {
// Создать новый пустой файл с новым именем переименованного файла
File file = new File(items.get(position).getParentFile().getAbsolutePath() + "/" + newName);
// Переименовать выбранный файл и присвоить новое имя
if (items.get(position).renameTo(file)) {
// Если получилось переименовать тогда удалить из массива старый файл
items.remove(position);
// Добавить новый файл на место удаленного файла
items.add(position, file);
// Сообщить адаптеру что инфрмация изменилась
notifyItemChanged(position);
}
}
// Получить количество элементов в списке
@Override
public int getItemCount() {
return items.size();
}
// Поток для генерации миниатюр изображений
class GenerateThumb extends AsyncTask<Object, Bitmap, Bitmap> {
// Ссылка на ImageView в котором необходимо показать сгенерированную миниатюру
ImageView iv;
// Функция выполняется в фоновом режиме, чтобы приложение не зависло при генерации миниатюр
@Override
protected Bitmap doInBackground(Object... params) {
// Присвоить локальной переменной ImageView ссылку на ImageView для которой генерируется миниатюра
iv = (ImageView) params[1];
// Получить путь к файлу на основе которого генерируется миниатюра
// Путь передан через параметры во время запуска потока
String path = params[0].toString();
// Переменная для хранения миниатюры
Bitmap thumb;
// Если type == 0 значит надо сгенерировать миниатюру для изображения
if (type == 0) {
// Создать объект Bitmap на основе пути к файлу
Bitmap bitmap = BitmapFactory.decodeFile(path);
// Присвоить временной переменной значение сгенерированного Bitmap-a размером 256 на 256 пикселей
thumb = ThumbnailUtils.extractThumbnail(bitmap, 256, 256);
// Иначе сгенерировать миниатюру для видео
} else {
// Сгенерировать миниатюру для видео и присвоить временной переменной, размер миниатюры поставить системным рамзером 512 на 384 пикселей, потому что для миниатюр есть два размера которые указываются через константы, это MINI_KIND и MICRO_KIND 96 на 96 пикселей
thumb = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND);
}
// Добавить миниатюру в HashMap, указав ключом путь к файлу и значением объект Bitmap
thumbs.put(path, thumb);
// publishProgress выполняется в главном потоке и служит для обновления пользовательского интерфейса, который вызывается из потока выполнемого в фоновом режиме, так как поток фонового режима не имеет возможности изменять объекты View
publishProgress(thumb);
// Значение return в данном случае не исользуется и если необходимо использовать надо дописать функцию onPostExecute который и принимает в качестве параметра значения рассчитанные или обработанные в фоновом потоке
return null;
}
// Функция вызывается для обновления пользовательского интерфейса в промежутках обработки фонового потока если не вызывать эту функцию то пользователю придется ждать пока все миниатюры не будут сгенерированы и только потом показать их, а вызывая эту функцию при генерации каждой миниатюры они постепенно появляются уже в пользовательском интерфейсе
@Override
protected void onProgressUpdate(Bitmap... values) {
super.onProgressUpdate(values);
// Устанавливает в ImageView сгенерированную миниатюру
iv.setImageBitmap(values[0]);
}
}
}
Создайте ещё один класс и назовите MediaList и напишите следующий код:
public class MediaList extends Fragment {
RecyclerView fileList;
FileAdapter adapter;
GridLayoutManager glm;
File base;
String path;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// Получить через файл макета RecyclerView и присвоить локальной переменной
fileList = (RecyclerView) inflater.inflate(R.layout.media_list_fragment, container, false);
// Получить переданный путь и присвоить локальной переменной
path = getArguments().get("path").toString();
// Создать экземпляр GridLayoutManager для показа содержимого RecyclerView в виде плитки
glm = new GridLayoutManager(getContext(), 2);
// Присвоить экземпляру RecyclerView экзмепляр класса GridLayoutManager
fileList.setLayoutManager(glm);
// Вызов функции для установки адаптера для RecyclerView
setAdapter();
// Вернуть полученный через LayoutInflater RecyclerView в качестве объекта View для показа в Activity
return fileList;
}
public void setAdapter() {
// Переменная для хранения объекта класса File, который хранит в себе путь к папке с изображениями и видео
base = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "My Lesson Camera/" + path);
// Проверить существует ли корневая папка
if (!base.exists()) {
// Если папка не существует что может быть при первой установке программы создать эти папки
base.mkdirs();
}
// Создать адптер, куда передаётся путь к файлам и параметр со значением 0 если надо создать миниатюры для файлов в папке Photos
// и 1 если надо сгенерировать миниатюры для файлов в папке Videos
adapter = new FileAdapter(getContext(), base.listFiles(), path.equals("Photos") ? 0 : 1);
// Установить адаптер для показа элементов в списке
fileList.setAdapter(adapter);
}
// Функция служит для обновления списка с файлами, которая вызывается из MainActivity при съемке новых фото и видео
public void UpdateAdapter() {
// Вызвать функцию UpdateDataSet из адаптера для передачи списка файлов вместе с новыми файлами для обновления списка
adapter.UpdateDataSet(base.listFiles());
}
}
Добавьте новый класс, на этот раз это класс для показа диалогового окна переименования файла, назовите новый класс DialogFragment:
public class RenameDialog extends DialogFragment {
// Объявление переменных для хранения ссылки на экземпляр адаптера, целое значение позиции названия файла и расширения файла, которое получаем из названия
public FileAdapter adapter;
public int position;
public String name;
public String extension = "";
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Объявление экземпляра AlertDialog.Builder для создания диалогового окна
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
// Получение LayoutInflater-a
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Присвоить переменной View макет загруженный через LayoutInflater
View v = inflater.inflate(R.layout.rename_file_dialog, null, false);
// Текстовое поле для ввода нового имени файла
final EditText newFileName = v.findViewById(R.id.newFileName);
// Получение переданных аргументов с ключами name и position и присвоение локальным переменным
Bundle args = getArguments();
name = args.getString("name");
position = args.getInt("position");
// Установить в качестве введенного текста старое название файла
newFileName.setText(name);
// Проверить на существование точки, которая пишется перед расширением файла String.lastInfdexOf(char) - возвращает последнюю позицию в виде числового значения указанного внутри скобок символа или строки
if (name.lastIndexOf('.') > 0) {
// Если есть расширение файла присвоить это расширение переменной extension путём получения только расширения через функцию String.substring(startIndex, endIndex)
extension = name.substring(name.lastIndexOf('.'), name.length());
}
// При клике на текстовое поле открывается клавиатура и в это время надо выбрать из старого названия часть строки до расширения файла, для удобства работы пользователя для исключения траты времени на выбор текста в текстовом поле и удаление ненужной части
newFileName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// При клике текстовое поле передаётся в качестве объекта класса View тут необходимо используя приведение типов из типа View привести к типу EditText
EditText editText = (EditText) v;
// Метод setSelection(start, stop) принимает два параметра: начальный индекс символа от куда начать выделение и конечную позицию символа до какого символа выделить тут выбирается с первого символа (это индекс 0) и до позиции последней точки, которая идёт перед расширением файла, точка не выделяется
newFileName.setSelection(0, editText.getText().toString().lastIndexOf('.'));
}
});
// Установить полученный через LayoutInflater макет диаологовому окну
builder.setView(v);
// Установить сообщение диаологового окна для удобства объяснения пользователю назначения диалогового окна
builder.setMessage("Введите новое название файла");
// Установить кнопку Сохранить и соответствующее действие выполняемое при клике на кнопку сохранить
builder.setPositiveButton("Сохранить", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Получить и присвоить переменной введенный в текстовое поле текст
String tempName = newFileName.getText().toString();
// Если длина введенного названия больше 0
if (tempName.length() > 0) {
// Здесь идет проверка не удалено ли расширение файла и если функция lastIndexOf возвращает значение меньше 0, значит расширение в текстовом поле удалено
if (tempName.lastIndexOf('.') <= 0) {
// в таком случае расширение которое было получено в верхней части этого кода добавить к концу нового названия файла
tempName += extension;
}
// Вызвать функцию renameFile через ссылку на экземпляр адаптера
adapter.renameFile(tempName, position);
// Закрыть диалоговое окно
dismiss();
}
}
});
// Установить кнопку Отмена, которая ничего не делает и только закрывает диалоговое окно
builder.setNegativeButton("Отмена", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
// Вернуть экземпляр диалогового окна
return builder.create();
}
}
Откройте файл FullScreenViewActivity.java и тут вы можете видеть много сгенерированного кода измените код файла для получения следующего кода:
public class FullScreenViewActivity extends AppCompatActivity {
private static final boolean AUTO_HIDE = true;
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
private static final int UI_ANIMATION_DELAY = 300;
private final Handler mHideHandler = new Handler();
private View mContentView;
private final Runnable mHidePart2Runnable = new Runnable() {
@SuppressLint("InlinedApi")
@Override
public void run() {
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
};
private View mControlsView;
private final Runnable mShowPart2Runnable = new Runnable() {
@Override
public void run() {
// Delayed display of UI elements
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.show();
}
mControlsView.setVisibility(View.VISIBLE);
}
};
private boolean mVisible;
private final Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
hide();
}
};
/**
* Touch listener to use for in-layout UI controls to delay hiding the
* system UI. This is to prevent the jarring behavior of controls going away
* while interacting with activity UI.
*/
private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (AUTO_HIDE) {
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_full_screen_view);
// Задача теперь получить два переданных параметра, один из них тип данных для показа (0 - фото, 1 - видео), второй путь к папке откуда брать информацию
int type = getIntent().getIntExtra("type", 0);
String path = getIntent().getStringExtra("path");
ImageView imageContent = findViewById(R.id.imageContent);
VideoView videoContent = findViewById(R.id.videoContent);
// Надо показать фото
if (type == 0)
{
// Делаем ImageView видимым
imageContent.setVisibility(View.VISIBLE);
// Устанавливаем изображение для ImageView
imageContent.setImageDrawable(Drawable.createFromPath(path));
}
// Если надо показать видео
if(type == 1)
{
// Сделать VideoView видимым
videoContent.setVisibility(View.VISIBLE);
// Указать путь к видеофайлу
videoContent.setVideoPath(path);
// Запустить проигрывание видео
videoContent.start();
}
mVisible = true;
mContentView = findViewById(R.id.fullscreen_content);
// Set up the user interaction to manually show or hide the system UI.
mContentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
toggle();
}
});
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
private void toggle() {
if (mVisible) {
hide();
} else {
show();
}
}
private void hide() {
// Hide UI first
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
// mControlsView.setVisibility(View.GONE);
mVisible = false;
// Schedule a runnable to remove the status and navigation bar after a delay
mHideHandler.removeCallbacks(mShowPart2Runnable);
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
}
@SuppressLint("InlinedApi")
private void show() {
// Show the system bar
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mVisible = true;
// Schedule a runnable to display UI elements after a delay
mHideHandler.removeCallbacks(mHidePart2Runnable);
mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
}
/**
* Schedules a call to hide() in delay milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
}
Реклама