В прошлой статье мы разработали кастомный ListView, который заполнялся локальными
данными. В этой статье мы расширем функционал нашего приложения и реализуем
возможность сохранения и загрузки данных.
Практика
Заметка: Для того чтобы комментарии в скаченном проекте были читаемы (русский язык), необходимо поменять кодировку проекта на UTF-8 (Project –> Properties –> Resource –> Text file incoding).
В этой статье я покажу вам, как работать с файлами на sd-карте (сохранение и
загрузка). А так же мы расширим возможные действия с нашим списком с помощью
основного и контекстного меню. Результат программы вы можете посмотреть на
скриншотах, представленных ниже.
Перейдем сразу к практике.
1. Сохранение/Загрузка списка
Для получения доступа к SD-карте используется класс Environment.
Этот класс предоставляет доступ к переменным среды. Используем метод getExternalStorageDirectory().getAbsolutePath()
для получения пути к SD-карте устройства.
В классе Utils
добавим строчку:
// Директория на SD-карте, где храниться список public static File cacheDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/customlistview");
Для сохранения и загрузки листа используется следующая идея:
сохранять объект (ArrayList) целиком в бинарный файл. Для правильной работы мы
в предыдущей статье добавили интерфейс Serializable для класса ToDoItem.
/** * Сохранить список на SD-карте */ private synchronized void saveList() { try { File infoFile = new File(Utils.cacheDir, "cache"); infoFile.createNewFile(); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(infoFile)); out.writeObject(list); out.close(); } catch (Exception e) { e.printStackTrace(); } } /** * Загрузить список с SD-карты */ @SuppressWarnings("unchecked") public boolean loadList() { try { Utils.cacheDir.mkdir(); File infoFile = new File(Utils.cacheDir, "cache"); infoFile.createNewFile(); ObjectInputStream in = new ObjectInputStream(new FileInputStream(infoFile)); list = (List<ToDoItem>) in.readObject(); in.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
Внесем изменения в существующие алгоритмы:
В методе onCreate() вместо initList() будем использовать
loadList().
Так же необходимо добавить сохранение списка при смене
статуса элемента. Это происходит в Handler’е.
private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { ... case MSG_CHANGE_ITEM: // Сделано / Не сделано дело ToDoItem item = list.get(msg.arg1); item.setCheck(!item.isCheck()); Utils.sorting(list, 0); saveList(); adapter.notifyDataSetChanged(); setCountPurchaseProduct(); break; } } };
2. Разработка основного меню
Основное меню появляется в приложении при нажатии на кнопку
меню на девайсе.
С помощью основного меню наше приложение сможет выполнять
следующие функции:
- добавить элемент;
- удалить все элементы;
- отметить все элементы;
- убрать отметки со всех элементов.
Сначала добавим все необходимые строковые ресурсы в файл strings.xml
<string name="menu_item_add">Добавить</string>
<string name="menu_item_remove">Удалить</string>
<string name="menu_item_rename">Переименовать</string>
<string name="menu_item_remove_all">Удалить все</string>
<string name="menu_item_check_all">Сделано все</string>
<string name="menu_item_uncheck_all">Ничего не сделано</string>
<string name="btn_yes">Да</string>
<string name="btn_no">Нет</string>
<string name="msg_empty">Название не может быть пустым</string>
Теперь разработает макет для меню. Необходимо в папке res создать директорию menu. В ней создать файл menu.xml
Исходный код файла menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
>
<item
android:id="@+id/menu_item_add"
android:title="@string/menu_item_add"/>
<item
android:id="@+id/menu_item_remove_all"
android:title="@string/menu_item_remove_all"/>
<item
android:id="@+id/menu_item_check_all"
android:title="@string/menu_item_check_all"/>
<item
android:id="@+id/menu_item_uncheck_all"
android:title="@string/menu_item_uncheck_all"/>
</menu>
Теперь необходимо разработать методы, которые будут
выполняться при выборе пунктов меню.
- Добавить элемент (showAddToDoItemDialog)
Данный метод будет создавать диалоговое окно с полем для
ввода названия. После нажатия на кнопку «Да», в список добавится новый элемент.
/** * Открыть диалоговое окно для добавления айтема */ private void showAddToDoItemDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.menu_item_add)); final Context context = this; final EditText input = new EditText(this); builder.setView(input); builder.setPositiveButton(getString(R.string.btn_yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); if (value.equals("")) Toast.makeText(context, getString(R.string.msg_empty), Toast.LENGTH_SHORT).show(); else { list.add(new ToDoItem(value, getIndexFromList() + 1)); Utils.sorting(list, 0); saveList(); getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER); } } }); builder.setNegativeButton(getString(R.string.btn_no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { return; } }); AlertDialog alert = builder.create(); alert.show(); }
- Удалить все элементы (removeAll)
Данный метод будет полностью очищать список.
/** * Удаление всех айтемов из списка */ private void removeAll() { list.clear(); saveList(); getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER); }
- Отметить/Убрать отметки со всех элементов (setCheckAll)
Данный метод, в зависимости от параметра, устанавливает/убирает
отметку с каждого элемента в списке.
/** * Сделать/Не сделать все дела */ private void setCheckAll(boolean check) { Iterator<ToDoItem> it = list.iterator(); while (it.hasNext()) { ToDoItem item = it.next(); item.setCheck(check); } Utils.sorting(list, 0); saveList(); getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER); }
- Получение последнего индекса в списке (getIndexFromList)
Данный метод возвращает
последний максимальный индекс в списке.
/** * Получение последнего индекса в списке */ private int getIndexFromList() { int index = 0; Iterator<ToDoItem> it = list.iterator(); while (it.hasNext()) { ToDoItem item = it.next(); if (item.getIndex() > index) index = item.getIndex(); } return index; }
Теперь добавим меню в Activity.
/** * Создание основного меню */ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } /** * Обработка нажатия на айтем основного меню */ @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.menu_item_add: // Добавление айтема showAddToDoItemDialog(); return true; case R.id.menu_item_remove_all: // Удаление всех айтемов removeAll(); return true; case R.id.menu_item_check_all: // Сделать все дела setCheckAll(true); return true; case R.id.menu_item_uncheck_all: // Вернуть лист в начальное состояние setCheckAll(false); return true; default: return super.onOptionsItemSelected(item); } }
3. Разработка контекстного меню
Контекстное меню для ListView появляется при долгом нажатии на элемент. Регистрируется
с помощью метода registerForContextMenu().
С помощью контекстного меню наше приложение сможет выполнять
следующие функции:
- удалить элемент;
- переименовать элемент.
Сначала разработаем используемые методы для контекстного
меню.
- Удалить выбранный элемент (removeItem)
Удаляет указанный элемент из списка. После удаления
происходит переиндексирование списка.
/** * Удаление айтема из списка */ private void removeItem(int index) { list.remove(index); reindexList(); saveList(); getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER); }
- Переименовать выбранный элемент (showRenameDialog)
Данный метод будет создавать диалоговое окно с полем для
изменения названия элемента.
/** * Открыть диалоговое окно для переименования айтема */ private void showRenameDialog(final int index) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.menu_item_rename)); final Context context = this; final EditText input = new EditText(this); input.setText(list.get(index).getName()); builder.setView(input); builder.setPositiveButton(getString(R.string.btn_yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); if (value.equals("")) Toast.makeText(context, getString(R.string.msg_empty), Toast.LENGTH_SHORT).show(); else { list.get(index).setName(value); saveList(); getHandler().sendEmptyMessage(MainActivity.MSG_UPDATE_ADAPTER); } } }); builder.setNegativeButton(getString(R.string.btn_no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { return; } }); AlertDialog alert = builder.create(); alert.show(); }
- Переиндексирование списка (reindexList)
Данный метод упорядочивает индексы в списке после удаления
элемента.
/** * Перераспределить индексы для айтемов в списке */ private void reindexList() { int index = 1; Utils.sorting(list, 1); Iterator<ToDoItem> it = list.iterator(); while (it.hasNext()) { ToDoItem item = it.next(); item.setIndex(index); index++; } Utils.sorting(list, 0); }
Теперь добавим контекстное меню в Activity.
Зарегистрируем меню для ListView в методе onCreate():
registerForContextMenu(listview); // Регистрация контекстного меню для ListView)
Добавим методы в Activity:
/** * Создание контекстного меню для ListView */ @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { int index = ((AdapterView.AdapterContextMenuInfo)menuInfo).position; if (index == list.size() + 1 || index == 0) return; super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, MSG_REMOVE_ITEM, Menu.NONE, R.string.menu_item_remove); menu.add(0, MSG_RENAME_ITEM, Menu.NONE, R.string.menu_item_rename); } /** * Обработка нажатия на айтем контекстного меню */ @Override public boolean onContextItemSelected(MenuItem item) { super.onContextItemSelected(item); AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); int index = menuInfo.position - 1; switch (item.getItemId()) { case MSG_REMOVE_ITEM: // Удалить айтем removeItem(index); return true; case MSG_RENAME_ITEM: // Переименовать айтем showRenameDialog(index); return true; } return false; }
Комментариев нет:
Отправить комментарий