Las pantallas de preferencias son un común en la mayoría de aplicaciones Android ya que permiten a los usuarios modificar aspectos de configuración. Para ello Android ofrece una serie de opciones como son:
- CheckBoxPreference: Donde se incluye un checkbox para activar / desactivar
- ListPreference: Muestra un diálogo para seleccionar un elemento.
- EditTextPreference: Muestra un diálogo para introducir un texto.
- MultiSelectListPreference: Muestra un diálogo para seleccionar un elemento.
- DropDownPreference: Muestra un spinner con varias posibilidades de selección.
- Preference: Muestra una categoria seleccionada.
- PreferenceCategory: Muestra una división dentro de la pantalla de las preferencias
Cada uno de los valores que se introducirán en las selecciones anteriores se asociará a una clave con el valor determinado, de forma que si se desea recuperar tan solo habrá que llamar a la clave asociada.. Todas ellas deben de ir englobadas bajo una etiqueta que sea <PreferenceScreen> en un archivo xml dentro del paquete con nombre xml en la raíz de resources. En dicho archivo xml se asocian las siguientes etiquetas:
- title: texto mostrado
- summary: breve explicación o configuración seleccionada
- key: clave que se le asociará en el xml de las preferencias
- entries: array de valores que tendrá en elemento. Esta etiqueta tan solo aparecerá en elementos con posibilidades de selección múltiple
- entryValues: array que se quiere asociar un valor diferente al que se muestra como entries.
Un ejemplo sería
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:title="Pantalla de preferencias"> <Preference android:key="basic_preference" android:title="Preferencia1" android:summary="Sumario1" /> <Preference android:key="stylish_preference" android:title="Preferencia2" android:summary="Sumario2" /> <PreferenceCategory android:title="Configuraciones simples"> <CheckBoxPreference android:key="checkbox_preference" android:title="Check" android:summary="Sumario Check" /> </PreferenceCategory> <PreferenceCategory android:title="Configuraciones adicionales"> <EditTextPreference android:key="edittext_preference" android:title="Edit" android:summary="Sumario Edit" android:dialogTitle="Dialogo Edit" /> <DropDownPreference android:key="dropdown_preference" android:title="Spinner" android:summary="Sumario Spinner" android:entries="@array/entradas" android:entryValues="@array/entradas" /> </PreferenceCategory> <PreferenceCategory android:title="Catergoria selecciones"> <ListPreference android:key="list_preference" android:title="Lista" android:summary="Sumario lista" android:entries="@array/entradas" android:entryValues="@array/entradas" android:dialogTitle="Dialogo lista" /> </PreferenceCategory> </PreferenceScreen>
[ezcol_1half]Una vez creado el xml hay que asociárselo a una Activity. Lo diferente con respecto a lo visto hasta esta entrada es que no se trabaja con una clase que extienda de AppCompactActivity sino que se debe extender de PreferenceActivity donde en el método OnCreate() se ejecuta el método addPreferencesFromResources() indicándole el xml donde se han indicado las preferencias[/ezcol_1half] [ezcol_1half_end][/ezcol_1half_end]
public class ActividadPreferencias extends PreferenceActivity{ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferencia_ejemplo); } }
Sin embargo desde las últimas versiones de Android está forma de trabajar está deprecada, por lo que se pasa a trabajar directamente con PreferenceFragment, tratando la clase como un fragment normal a la hora de mostrarlo.
public class ActividadPreferencias extends PreferenceFragment{ @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferencia_ejemplo); } }
Este tipo de fragment tan solo admite la librería android.app, por lo que si queremos trabajar con Fragments de soporte este tipo no estaría soportado. Para poder crear un fragment de preferencias y que además sea de soporte debemos compilar la librería específica:
compile 'com.android.support:preference-v7:26.+'
Desde ese instante podré utilizar un PreferenceFragmentCompact que me permite trabajar con fragments de soporte, sobreescribiendo el método OnCreatePreference()
public class ActividadPreferencias extends PreferenceFragmentCompat{ @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.preferencia_ejemplo); } }
Una cosa muy importante es que en esta clase de preferencias (solo en la de soporte) hay que indicarle el tema que utiliza (en la Activity donde vaya a ser puesto el fragment) explícitamente en el archivo styles con la linea:
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
Desde este instante cada vez que se cambie alguna preferencia se generará un archivo xml en la raíz del proyecto /data/data/nombre_proyecto/shared/preferences.xml donde se guardará un par clave – valor con cada una de las preferencias seleccionadas. Así, si cambiamos uno de los check a verdadero y en la preferencia del edit se pone una palabra se generará el siguiente xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="dropdown_preference">Opcion 5</string> <boolean name="checkbox_preference" value="true" /> <string name="edit_preference">Opcion introducida</string> </map>
Estas configuraciones se guardan solas cada vez que se realiza una modificación, pero para poder acceder a ellas hay que crear un objeto de tipo SharedPreferences y preguntar por la clave asociada para obtener el valor correspondiente:
SharedPreferences sh = PreferenceManager.getDefaultSharedPreferences(getActivity()); Boolean valorCh = sh.getBoolean("checkbox_preference",false); String entrada = sh.getString("edittext_preference","no hay nada");
El método getTipo() obtiene como parámetros la clave que se quiere buscar y el valor por defecto que devolverá en el caso que no se encuentre la clave. Del mismo modo también se puede preguntar por todas las claves obteniendo un objeto de tipo Map (que se podrá recorrer) mediante el método getAll así como preguntar si una clave está contenida en el fichero de preferencias mediante el método contains()
Map entradas = sh.getAll(); sh.contains("clave_buscar");
Por último si se quiere actuar ante un cambio en ejecución de alguna preferencia se debe registrar un escuchador a que detecte el cambio en la clave concreta
aredPreferences.OnSharedPreferenceChangeListener escuchador = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { Log.v("cambios",sharedPreferences.getString(s,"no hay nada")); }}; sh.registerOnSharedPreferenceChangeListener(escuchador); sh.unregisterOnSharedPreferenceChangeListener(escuchador);