Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Очень часто требуется сохранить какие-то настройки. Простой пример - девочка с удовольствием пользовалась нашей программой Счётчик ворон и вертела головой в поисках очередной птицы. Но вот незадача - когда девочка вечером закрывала своё приложение, то на следующий день счётчик обнулялся. А так хотелось узнать, сколько ворон можно насчитать за месяц. Но однажды в дождливый осенний день она встретила рыжее чудо. Впрочем, это уже совсем другая история.
Самый простой способ, который приходит в голову - сбросить данные в файл, а при запуске приложения считывать необходимые данные из файла. Второй вариант - работать с базой данных и хранить настройки там.
Рассмотрим сначала второй вариант. Хранить данные в базе данных не всегда оправдано, если данных не слишком много, они простые и нам не нужно анализировать данные на предмет, сколько мышек поймал котик в прошлом году и сколько часов он спал в январе и феврале.
Первый вариант с файлами хорош, например, для сохранения больших текстовых заметок. Естественно, и для простых данных мы тоже можем использовать файлы. Записали что-то в файл, а потом открыли его и считали данные.
На самом деле нет необходимости изобретать свой велосипед и придумывать свою структуру для хранения данных. В Android существует класс SharedPreferences (Общие настройки), разработанный специально для этих целей. Приложение автоматически создаёт файл в своей папке и хранит простые данные в виде «ключ — значение». Весь процесс создания, открытия, чтения файла оптимизирован и избавляет вас от головной боли.
Общие настройки поддерживают базовые типы boolean, String, float, long и int, что делает их идеальным средством для быстрого сохранения значений по умолчанию, переменных экземпляра класса, текущего состояния UI и пользовательских настроек. Они чаще всего используются для обеспечения постоянства данных между пользовательскими сессиями и доступа к ним компонентов приложения.
Если у вас сохранился старый проект по подсчёту ворон, то можете снова его открыть и добавить новый код. Либо заново создайте проект по памяти, заодно проверите, как усвоили урок и сможете ли вы самостоятельно создать проект с нуля, не заглядывая на сайт за подсказкой.
Создаём переменную, представляющую экземпляр класса SharedPreferences, который отвечает за работу с настройками:
private lateinit var prefs: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
prefs =
getSharedPreferences("settings", Context.MODE_PRIVATE)
}
Вы передаёте в указанный метод getSharedPreferences() имя вашего файла (файл будет создан автоматически) и стандартное разрешение по умолчанию MODE_PRIVATE - только приложение имеет доступ к настройкам. Если вам необходимо, вы можете создать несколько подобных файлов настроек.
Немного опередим события и представим, что приложение запущено, и мы считаем ворон весь день. Когда мы закрываем приложение, то нам необходимо сохранить информацию в "Общих настройках". Обычно для этих целей используют методы onPause() или onStop().
Чтобы внести изменения в настройки, нужно использовать класс SharedPreferences.Editor. Получить объект Editor можно через вызов метода edit() объекта SharedPreferences. После того, как вы внесли все необходимые изменения, вызовите метод apply(), чтобы изменения вступили в силу.
override fun onPause() {
super.onPause()
// Запоминаем данные
val editor = prefs.edit()
editor.putInt(APP_PREFERENCES_COUNTER, counter).apply()
}
Теперь при закрытии программы значение счётчика автоматически запишется в файл. При повторном запуске приложения нам уже не нужно инициализировать счётчик со значением 0. Мы можем прочитать сохранённое значение и использовать его для счётчика, чтобы продолжить подсчёт. Сделаем это в методе onResume().
override fun onResume() {
super.onResume()
if(prefs.contains(APP_PREFERENCES_COUNTER)){
// Получаем число из настроек
counter = prefs.getInt(APP_PREFERENCES_COUNTER, 0)
// Выводим на экран данные из настроек
infoTextView.text = "Я насчитал $counter ворон"
}
}
Мы проверяем сначала наличие ключа APP_PREFERENCES_COUNTER, а затем извлекаем из ключа его значение.
Вот и всё. Небольшие изменения в коде сделали программу продвинутой. Теперь вы можете спокойно закрывать и открывать программу, ваши данные не будут потеряны. При желании вы можете добавить кнопку для сброса счётчика. Это вам в качестве домашнего задания.
В теории показаны дополнительные примеры и даны подробные сведения об использовании "Общих настроек". Вам следует хорошенько разобраться в этом механизме, так как он часто используется на практике. Более того, некоторые программисты предпочитают использовать "Общие настройки" вместо базы данных, если это позволяет логика программы, так как это работает быстрее и потребляет меньше ресурсов. Выбор за вами.
Не волнуйтесь, с котёнком всё в порядке. Девочка подобрала его и принесла домой. И добрая девочка по-прежнему пользуется нашей программой "Счётчик ворон". Наверное, биологом станет или ветеринаром.
При написании статьи использовались иллюстрации Рины З..
Разметка
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/crowButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Считаем ворон"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/infoTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/crowButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
Код класса активности.
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.counter
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var prefs: SharedPreferences
private val APP_PREFERENCES_COUNTER = "counter"
private var counter: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
prefs =
getSharedPreferences("settings", Context.MODE_PRIVATE)
crowButton.setOnClickListener {
counter = ++counter
infoTextView.text = "Я насчитал $counter ворон"
}
}
override fun onPause() {
super.onPause()
// Запоминаем данные
val editor = prefs.edit()
editor.putInt(APP_PREFERENCES_COUNTER, counter).apply()
}
override fun onResume() {
super.onResume()
if(prefs.contains(APP_PREFERENCES_COUNTER)){
// Получаем число из настроек
counter = prefs.getInt(APP_PREFERENCES_COUNTER, 0)
// Выводим на экран данные из настроек
infoTextView.text = "Я насчитал $counter ворон"
}
}
}
Разметка
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/buttonCrowsCounter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Считаем ворон" />
<TextView
android:id="@+id/textViewInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
Код класса активности
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.counter;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
private int mCounter;
private TextView mInfoTextView;
// имя файла настройки
public static final String APP_PREFERENCES = "mysettings";
public static final String APP_PREFERENCES_COUNTER = "counter";
private SharedPreferences mSettings;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSettings = getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE);
mInfoTextView = (TextView) findViewById(R.id.textViewInfo);
}
public void onClick(View v) {
// Выводим на экран
mInfoTextView.setText("Я насчитал " + ++mCounter + " ворон");
}
@Override
protected void onResume() {
super.onResume();
if (mSettings.contains(APP_PREFERENCES_COUNTER)) {
// Получаем число из настроек
mCounter = mSettings.getInt(APP_PREFERENCES_COUNTER, 0);
// Выводим на экран данные из настроек
mInfoTextView.setText("Я насчитал "
+ mCounter + " ворон");
}
}
@Override
protected void onPause() {
super.onPause();
// Запоминаем данные
SharedPreferences.Editor editor = mSettings.edit();
editor.putInt(APP_PREFERENCES_COUNTER, mCounter);
editor.apply();
}
}
Сохраняем объекты в настройках SharedPreferences