Настройки, диалоговые окна.
SharedPreferences
- SharedPreferences - компонент хранения настроек приложения
- Каталог хранения: /data/data/имя_пакета/shared_prefs/имя_файла_настроек.xml
- getPreferences() — внутри Activity, чтобы обратиться к определённому для Activity предпочтению;
- getSharedPreferences() — внутри Activity, чтобы обратиться к предпочтению на уровне приложения;
- getDefaultSharedPreferences() — из объекта PreferencesManager, чтобы получить общедоступную настройку, предоставляемую Android.
Получение настроек
- getBoolean(String key, boolean defValue)
- getFloat(String key, float defValue)
- getInt(String key, int defValue)
- getLong(String key, long defValue)
- getString(String key, String defValue)
- тип double не поддерживается.
SharedPreferences: получение объекта настроек
// это будет именем файла настроек
public static final String APP_PREFERENCES = "mysettings";
SharedPreferences mySharedPreferences =
getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE);
SharedPreferences: флаги
- MODE_PRIVATE - только приложение имеет доступ к настройкам.
- MODE_APPEND - присоединяет новые настройки к существующим
- MODE_ENABLE_WRITE_AHEAD_LOGGING - включена запись с опережением записи.
- MODE_MULTI_PROCESS - проверяет изменение настроек, даже если экземпляр sharedpreference уже был загружен
- MODE_WORLD_READABLE - позволяет другим приложениям читать настройки
- MODE_WORLD_WRITEABLE - позволяет другим приложениям записывать новые настройки
SharedPreferences: файл
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="0005">|000000|000000|000000|0</string>
<string name="0004">|000000|000000|000000|1</string>
<string name="0006">|000000|000000|000000|0</string>
</map>
SharedPreferences: пример
// это будет именем файла настроек
public static final String APP_PREFERENCES = "mysettings";
public static final String APP_PREFERENCES_NAME = "Nickname";
public static final String APP_PREFERENCES_AGE = "Age";
SharedPreferences mSettings;
mSettings = getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE);
EditText editNickname = findViewById(R.id.editNickname);
String strNickName = editNickname.getText().toString(); // здесь содержится текст, введенный в текстовом поле
Editor editor = mSettings.edit();
editor.putString(APP_PREFERENCES_NAME, strNickName);
editor.apply();
SharedPreferences: сохранение
- Чтобы внести изменения в настройки (редактировать), нужно использовать класс SharedPreferences.Editor.
- Получить объект Editor можно через вызов метода edit объекта SharedPreferences, который вы хотите изменить.
- После того, как вы внесли все необходимые изменения, вызовите метод commit() или apply() объекта Editor
- Метод apply() появился в API 9 и работает в асинхронном режиме, что является более предпочтительным вариантом.
- Метод commit() приходится использовать для старых версий и кроме того, он возвращает значение true в успешном случае и false в случае ошибки.
SharedPreferences: дополнительные методы
- Для очистки значений используйте методы SharedPreferences.Editor.remove(String key)
- Очистить все значения: SharedPreferences.Editor.clear().
- Можно получить ассоциативный массив со всеми ключами и значениями через метод getAll()
- Проверить наличие конкретного ключа с помощью метода contains().
- Начиная с API 11, у класса SharedPreferences появился новый метод getStringSet()
- У класса SharedPreferences.Editor родственный ему метод putStringSet().
SharedPreferences: Методы getStringSet() и putStringSet()
SharedPreferences sp;
String catnames;
// записываем имена котов в файл настроек
public void onPutSettings(View v){
Set catnames = new HashSet();
catnames.add("Мурзик");
catnames.add("Барсик");
catnames.add("Рыжик");
Editor e = sp.edit();
e.putStringSet("strSetKey", catnames);
e.apply();
}
// считываем имена котов обратно
public void onShowSettings(View v)
{
Set ret = sp.getStringSet("strSetKey", new HashSet());
for(String r : ret) {
Log.i("Share", "Имя кота: " + r);
}
}
getDefaultSharedPreferences()
- getSharedPreferences() - необходимо придумывание имени файла для хранения настроек.
- Этот способ придаёт гибкости в том случае, когда вам нужно создать несколько отдельных файлов.
- Но если вам нужен один файл, то можно ничего не придумывать, а использовать метод getDefaultSharedPreferences() из объекта PreferencesManager.
- Система сама сгенерирует имя файла из имени вашего пакета с добавлением слова _preferences.
getDefaultSharedPreferences()
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
getDefaultSharedPreferencesMode());
}
private static String getDefaultSharedPreferencesName(Context context) {
return context.getPackageName() + "_preferences";
}
private static int getDefaultSharedPreferencesMode() {
return Context.MODE_PRIVATE;
}
Диалоговые окна
- Dialog - базовый класс для всех типов диалоговых окон;
- AlertDialog — диалоговое окно с кнопками, списком, флажками или переключателями;
- CharacterPickerDialog - диалоговое окно, позволяющее выбрать символ с ударением, связанный с базовым символом;
- ProgressDiaiog — диалоговое окно с индикатором прогресса при помощи компонента ProgressBar. В API 26 признан устаревшим.
- DatePickerDialog — диалоговое окно выбора даты с элементом DatePicker
- TimePickerDialog — диалоговое окно выбора времени с элементом TimePicker
Класс Dialog
- Класс Dialog является базовым для всех классов диалоговых окон.
- ProgressDialog, TimePickerDialog И DatePickerDialog — расширение класса AlertDialog, они также могут иметь командные кнопки.
- Каждое диалоговое окно должно быть определено внутри активности или фрагмента
- Диалоговое окно можно открыть один раз или несколько раз.
- Для отображения диалогового окна необходимо вызвать метод showDialog()
Класс Dialog
- Метод dismissDialog() прячет диалоговое окно (но не удаляет), не отображая его на экране.
- Окно остаётся в пуле диалоговых окон данной активности.
- При повторном отображении при помощи метода showDialog() будет использована кэшированная версия окна.
- Метод removeDialog() удаляет диалоговое окно из пула окон данной активности.
- При повторном вызове метода showDialog() диалоговое окно придётся создавать снова.
Dialog: пример
Dialog dialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dialog = new Dialog(MainActivity.this);
// Установите заголовок
dialog.setTitle("Заголовок диалога");
// Передайте ссылку на разметку
dialog.setContentView(R.layout.dialog_view);
// Найдите элемент TextView внутри вашей разметки
// и установите ему соответствующий текст
TextView text = (TextView) dialog.findViewById(R.id.dialogTextView);
text.setText("Текст в диалоговом окне. Вы любите котов?");
}
public void onClick(View v)
{
// Выводим диалоговое окно на экран
dialog.show();
}
Методы onCreateDialog() и onPrepareDialog()
- Метод onCreateDialog() вызывается один раз при создании окна.
- После начального создания при каждом вызове метода showDialog() будет срабатывать обработчик onPrepareDialog().
- Переопределив этот метод, вы можете изменять диалоговое окно при каждом его выводе на экран.
- Это позволит привнести контекст в любое из отображаемых значений.
- Если требуется перед каждым вызовом диалогового окна изменять его свойства (например, текстовое сообщение или количество кнопок), то можно реализовать внутри этого метода.
Методы onCreateDialog() и onPrepareDialog()
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case (TIME_DIALOG) :
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
Date currentTime = new Date(java.lang.System.currentTimeMillis());
String dateString = sdf.format(currentTime);
AlertDialog timeDialog = (AlertDialog)dialog;
timeDialog.setMessage(dateString);
break;
}
}
Фрагменты
- Два способа использования фрагментов
- Первый способ основан на замещении родительского контейнера.
- Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, FrameLayout.
- В коде контейнер замещается фрагментом.
- При использовании подобного сценария в разметке не используется тег fragment, так как его нельзя менять динамически.
- Также придётся обновлять ActionBar, если он зависит от фрагмента.
Фрагменты
- Второй вариант - используются отдельные разметки для телефонов и планшетов, которые можно разместить в разных папках ресурсов.
- Например, если в планшете используется двухпанельная разметка с двумя фрагментами на одной активности
- используем эту же активность для телефона, но подключаем другую разметку, которая содержит один фрагмент.
- Когда нам нужно переключиться на второй фрагмент, то запускаем вторую активность.
- Второй подход является наиболее гибким и в целом предпочтительным способом использования фрагментов.
Фрагменты: классы
- Сами фрагменты наследуются от androidx.fragment.app.Fragment.
- Существует подклассы фрагментов: ListFragment, DialogFragment, PreferenceFragment, WebViewFragment и др.
- Для взаимодействия между фрагментами используется класс android.app.FragmentManager - специальный менеджер по фрагментам.
- Для транзакций (добавление, удаление, замена) используется класс-помощник android.app.FragmentTransaction.
- В 2018 году Гугл объявила фрагменты из пакета androd.app устаревшими.
- Заменяйте везде на версию из библиотеки совместимости. В 2020 году уже используют пакет androidx.fragment.app.
Фрагменты
- У каждого фрагмента должен быть свой класс.
- Класс наследуется от класса Fragment или схожих классов
- Создаются различные методы типа onCreate() и др.
- Если фрагмент имеет разметку, то используется метод onCreateView() - считайте его аналогом метода setContentView()
- При этом метод onCreateView() возвращает объект View, который является корневым элементом разметки фрагмента.
Фрагменты
public class FirstFragment extends Fragment implements OnClickListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.first_fragment,
container, false);
Button nextButton = (Button) view.findViewById(R.id.button_first);
nextButton.setOnClickListener(this);
return view;
}
// ...
}
Фрагменты
- Перед методом findViewById() используется view
- Этот метод относится к компоненту, а не к активности, как обычно делается в программах, когда просто опускали имя активности.
- ищем ссылку на кнопку не среди разметки активности, а внутри разметки самого фрагмента.
- в методе inflate() последний параметр должен иметь значение false в большинстве случаев.
FragmentManager
- Класс FragmentManager имеет два метода, позволяющих найти фрагмент, который связан с активностью:
- findFragmentById(int id) - Находит фрагмент по идентификатору
- findFragmentByTag(String tag) - Находит фрагмент по заданному тегу
Методы транзакции (FragmentTransaction)
- add() - Добавляет фрагмент к активности
- remove() - Удаляет фрагмент из активности
- replace() - Заменяет один фрагмент на другой
- hide() - Прячет фрагмент (делает невидимым на экране)
- show() - Выводит скрытый фрагмент на экран
- detach() (API 13) - Отсоединяет фрагмент от графического интерфейса, но экземпляр класса сохраняется
- attach() (API 13) - Присоединяет фрагмент, который был отсоединён методом detach()
Методы транзакции (FragmentTransaction)
- Методы remove(), replace(), detach(), attach() не применимы к статичным фрагментам.
- Перед началом транзакции нужно получить экземпляр FragmentTransaction через метод FragmentManager.beginTransaction().
- В конце любой транзакции, которая может состоять из цепочки вышеперечисленных методов, следует вызвать метод commit().
FragmentManager fragmentManager = getFragmentManager()
fragmentManager.beginTransaction()
.remove(fragment1)
.add(R.id.fragment_container, fragment2)
.show(fragment3)
.hide(fragment4)
.commit();
Адаптеры
- Адаптеры упрощают связывание данных с элементом управления.
- Адаптеры используются при работе с виджетами, которые дополняют android.widget.AdapterView: ListView, ExpandableListView, GridView, Spinner, Gallery
- AdapterView дополняет android.widget.ViewGroup
- Назначение адаптера заключается в том, чтобы предоставлять дочерние view для контейнера.
- Адаптер берет данные и метаданные определенного контейнера и строит каждый дочерний вид.
- Например, мы формируем пункты списка (массив строк) и передаём его списку ListView.
Готовые адаптеры
- ArrayAdapter - предназначен для работы с ListView. Данные представлены в виде массива, которые размещаются в отдельных элементах TextView
- ListAdapter - адаптер между ListView и данными. Строго говоря, это класс-интерфейс, который можно использовать и в ArrayAdapter и в SimpleAdapter и т.д.
- SpinnerAdapter - адаптер для связки данных с элементом Spinner. Это тоже интерфейс, как ListAdapter и работает по схожему принципу
- SimpleAdapter - адаптер, позволяющий заполнить данными список более сложной структуры, например, два текста в одной строке списка.
Готовые адаптеры
- SimpleCursorAdapter - дополняет ResourceCursorAdapter и создаёт компоненты TextView/ImageView из столбцов, содержащихся в курсоре. Компоненты определяются в ресурсах
- CursorAdapter - предназначен для работы с ListView, предоставляет данные для списка через курсор, который должен иметь колонку с именем "_id"
- ResourceCursorAdapter - этот адаптер дополняет CursorAdapter и может создавать виды из ресурсов
- HeaderViewListAdapter - расширенный вариант ListAdapter, когда ListView имеет заголовки.
- WrapperListAdapter - еще один адаптер для списков.
BaseAdapter
- Если вам нужен свой собственный адаптер, то в Android есть абстрактный класс BaseAdapter, который можно расширить.
- Собственный адаптер необходим в тех случаях, когда требуется специальное управление данными или дополнительный контроль над отображением дочерних представлений.
- Кроме того, вы можете предусмотреть в своём адаптере элементы кэширования для повышения производительности работы.
- У BaseAdapter есть несколько методов, которые следует переопределить. Например, метод getCount() позволяет узнать количество выводимых объектов.
Разметка
- Компоновка (также используются термины разметка или макет) хранится в виде XML-файла в папке /res/layout.
- компоновка – это некий визуальный шаблон для пользовательского интерфейса вашего приложения, который позволяет управлять элементами управления, их свойствами и расположением.
- Каждый файл разметки должен содержать только один корневой элемент компоновки, который должен быть объектом View или ViewGroup.
- Внутри корневого элемента вы можете добавлять дополнительные объекты разметки или виджеты как дочерние элементы.
Виды разметок
- FrameLayout
- LinearLayout
- TableLayout
- RelativeLayout
- GridLayout
- SwipeRefreshLayout
- ConstraintLayout
- CoordinatorLayout