Пишем свой Android Preference
Доброго времени суток. Если вы пишете приложения под Android, то наверняка сталкивались с настройками приложения (Preferences). На первый взгляд всё делается просто (см. сюда), но после беглого знакомства понимаешь что стандартных настроек очень мало. Их всего пять штук:
Очевидно что всех нужд эти настройки покрыть не могут.
Но тут самое время вспомнить что как то же можно настроить громкость звука или яркость экрана на телефоне — их нельзя настроить с помощью приведённого выше списка настроек! Идем в системные настройки и смотрим как это реализованно: Там есть обычная Preference, по клику на которую вылазит окошко с элементом управления «бегунок»(SeekBar). Впринципе приемлимое решение, но хочется избавится от лишнего окна и перенести бегунок прямо на экран настроек. Беглый поиск не нашёл приемлимых решений, и мы решили написать свой SeekBarPreference.
Наш SeekBarPreference должен обладать следущими качествами: он должен показовать текущее значение бегунка «на ходу», и мы хотим использовать его с разными параметрами длинны шага и количества шагов.
Собственно нам понадобится класс, которые отвечает за создание отображения настройки и поведение при перемещении ползунка.
Для упрощния кода описание отображения было вынесено в отдельный layout:
Layout содержит два текстовых поля — название настройки и текущее значение, и SeekBar.
Для переиспользования этой настройки нам надо определить особые атрибуты — максимальное значение ползунка и начальное положение ползунка. Самый простой способ — сделать специальные атрибуты для нашей настройки. Для этого создадим файл в папке «res/values» файл «attributes.xml»:
Теперь у нас есть числовые атрибуты которые мы можем использовать при задании настройки:
Но чтобы компилятор понял эти атрибуты надо определить из какого пакета их брать. Для этого в начале файла с настройками надо добавить определение пакета:
После этого можно насладится красивой настройкой без лишних окон.
- Preference
- CheckBoxPreference
- EditTextPreference
- ListPreference
- RingtonePreference
Очевидно что всех нужд эти настройки покрыть не могут.
Но тут самое время вспомнить что как то же можно настроить громкость звука или яркость экрана на телефоне — их нельзя настроить с помощью приведённого выше списка настроек! Идем в системные настройки и смотрим как это реализованно: Там есть обычная Preference, по клику на которую вылазит окошко с элементом управления «бегунок»(SeekBar). Впринципе приемлимое решение, но хочется избавится от лишнего окна и перенести бегунок прямо на экран настроек. Беглый поиск не нашёл приемлимых решений, и мы решили написать свой SeekBarPreference.
Наш SeekBarPreference должен обладать следущими качествами: он должен показовать текущее значение бегунка «на ходу», и мы хотим использовать его с разными параметрами длинны шага и количества шагов.
Собственно нам понадобится класс, которые отвечает за создание отображения настройки и поведение при перемещении ползунка.
public class SeekBarPreference extends Preference implements
OnSeekBarChangeListener {
private TextView valueTextView;
private int currentValue;
private int max;
//конструктор, вытаскивает спецальные атрибуты настройки
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
max = attrs.getAttributeIntValue(
"http://schemas.android.com/apk/res/com.sample", "max", 99);
currentValue = attrs.getAttributeIntValue(
"http://schemas.android.com/apk/res/com.sample",
"currentValue", 0);
}
//Переопределяем процедуру создания View для этой настройки
@Override
protected View onCreateView(ViewGroup parent) {
RelativeLayout layout = (RelativeLayout) LayoutInflater.from(
getContext())
.inflate(R.layout.seek_bar_preference_layout, null);
((TextView) layout.findViewById(R.id.title)).setText(getTitle());
SeekBar bar = (SeekBar) layout.findViewById(R.id.seekBar);
bar.setMax(max);
bar.setProgress(currentValue);
bar.setOnSeekBarChangeListener(this);
valueTextView = (TextView) layout.findViewById(R.id.value);
valueTextView.setText(currentValue+"");
return layout;
}
//Функция, вызываемая каждый раз при перемещении ползунка
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
valueTextView.setText(progress+"");
valueTextView.invalidate();
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
//Функция, вызываемая после окончания движения пользователем
public void onStopTrackingTouch(SeekBar seekBar) {
currentValue = seekBar.getProgress();
updatePreference(currentValue);
notifyChanged();
}
//Сохранение значения настройки
private void updatePreference(int newValue) {
SharedPreferences.Editor editor = getEditor();
editor.putInt(getKey(), newValue);
editor.commit();
}
}
Для упрощния кода описание отображения было вынесено в отдельный layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp" />
<TextView
android:id="@+id/value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/title"
android:layout_alignBaseline="@+id/title"
android:paddingLeft="5dp"
android:textSize="18dp" />
<SeekBar
android:id="@+id/seekBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="3dp"
android:layout_below="@+id/value" />
Layout содержит два текстовых поля — название настройки и текущее значение, и SeekBar.
Для переиспользования этой настройки нам надо определить особые атрибуты — максимальное значение ползунка и начальное положение ползунка. Самый простой способ — сделать специальные атрибуты для нашей настройки. Для этого создадим файл в папке «res/values» файл «attributes.xml»:
<declare-styleable name="SeekBarPreferenceAttrs">
/>
/>
</declare-styleable>
Теперь у нас есть числовые атрибуты которые мы можем использовать при задании настройки:
<com.example.SeekBarPreference
android:key="pref"
android:title="Sample preference"
sample:max="99"
sample:currentValue="30" />
Но чтобы компилятор понял эти атрибуты надо определить из какого пакета их брать. Для этого в начале файла с настройками надо добавить определение пакета:
xmlns:sample="http://schemas.android.com/apk/res/com.example"
После этого можно насладится красивой настройкой без лишних окон.
0 комментариев
Но у меня есть предложения по улучшению:
1. В SDK предусмотрены стандартные атрибуты для SeekBar. Это max, progress и secondaryProgress. Чтобы ими воспользоваться в XML, куда подключаем этот элемент вместо sample:max достаточно написать android:max и т.д. Тогда в конструкторе можно заменить DNS на http://schemas.android.com/apk/res/android, а текущее значение прогресса назвать не currentValue, а progress.
2. После запуска наблюдается такой баг - настройка сохраняется, но при повторном открытии экрана настроек не восстанавливается ее значение. Чтобы полечить баг надо в методе onCreateView переделать код вот так:
...
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext);
int progress = settings.getInt(getKey(), currentValue);
SeekBar bar = (SeekBar) layout.findViewById(R.id.seekBar);
bar.setMax(max);
bar.setProgress(progress);
...
добавить переменную в класс:
private Context mContext;
и в конструкторе ее заполнить:
mContext = context;
3. Еще можно воспользоваться secondaryProgress для показа прошлого установленного значения, если в onCreateView добавить строку:
bar.setSecondaryProgress(progress);