MójDroid.pl

#17 Twórz aplikacje na Androida z Mojdroid.pl – Ustawienia (Preference, SharedPreferences)

2012-10-15
|
Damian P.

Ekran ustawień to jeden z najważniejszych widoków w aplikacji. Android przecież słynie z tego, że posiada on mnóstwo konfiguracji. My również powinniśmy o tym pamiętać, tym bardziej, że tworzenie tej części programu jest bardzo proste (w porównaniu do reszty kodu), a zalety korzystania z tego są całkiem spore.
Informacja: Nie radzisz sobie z kodem? Coś Ci nie wychodzi lub nie działa? Zgubiłeś się? W tym miejscu zobaczysz pełen kod aplikacji.

1. Preference

Do budowania ustawień w programie używamy API z systemu o nazwie Preference. Jest to interfejs, który używamy w samym pliku z konfiguracją bloków (blok - element na liście opcji). Aby program mógł taką listę obsłużyć, naszą klasę z ekranem ustawień musimy rozszerzyć o PreferenceActivity lub/i PreferenceFragment. Tutaj należy się ograniczać, bo duża liczba opcji w tym ekranie może przerazić użytkownika (nikt nie chce spędzić całego dnia na analizę każdej opcji). Jak dobrze tym zarządzać macie opisane w Android Design Guidelines. Kolejne opcje budujemy używając odpowiednich subklas w pliku XML. I tak Preference to pojedynczy, podstawowy blok, który widzimy w tym oknie. Może on występować pod różnymi postaciami, na przykład jako checkbox (CheckboxPreference) albo lista (ListPreference). Ważnym atrybutem w każdym Preference jest klucz. To po nim dostajemy się do wartości wybranego pola, a następnie stosujemy wybrany algorytm. Zazwyczaj odbieramy prostą odpowiedź typu true, ale czasami (na przykład w dużych listach wyboru) musimy stworzyć tablicę w pliku /arrays.xml, która będzie nam służyć jako lista z wartościami i pozycjami do wybrania. Każdy z elementów w Preference jest zapisywany do wewnętrznej pamięci aplikacji, która nazywa się SharedPreferences. Tutaj kolejna ważna uwaga - każda zmiana ustawień odbywa się z automatycznym zapisem, to znaczy kiedy my zmieniamy wartość w tym ekranie i wyjdziemy z aplikacji, to wracając ponownie do programu mamy zaznaczony ostatni wybór. Nie musimy dbać o zapisywanie tych wartości, całość dzieje się w locie, bez naszego udziału. Oczywiście, jeżeli potrzebujemy ręcznej zamiany wartości danego klucza to możemy to zrobić bez najmniejszego problemu (i nadal w ustawieniach będzie widać aktualne zaznaczenie). W SharedPreferences możemy zapisywać wartości takie jak:
  • Int (liczba całkowita)
  • Float (liczba rzeczywista)
  • Boolean (prawda lub fałsz)
  • Long
  • String
  • String set
Musimy o tym pamiętać, budując własną konfigurację. Innych elementów w tej części systemu nie możemy przechowywać. Niestety, samo Preference nie wystarczy do sprawnego działania. Jak wcześniej pisałem, specjalna budowa ustaweń musi być rozpoznawalna przez nasze Activity. I tak aby zapewnić wsparcie dla starszych wersji systemu (to znaczy aby po wejściu do ustawień aplikacja działała), musimy używać PreferenceActivity. Za to PreferenceFragment jest rozszerzeniem dla nowszych wersji Androida (od 4.0) i działa nieco inaczej (ale o tym jeszcze nie dziś).

2. Preference(s)

Analizując pojedynczy blok w ustawieniach - każdy z elementów ma swoje własne atrybuty, według których odpowiednio widzimy go na liście. Może być to nazwa, opis pod spodem, typ pola, tablica z wartościami, domyślne zaznaczenie i tak dalej... Najczęściej używanymi blokami są:
  • CheckBoxPreference, czyli pojedyncze pole z możliwością zaznaczenia lub odznaczenia. Wykorzystuje rzecz jasna typ Boolean jako wartość, więc może mieć tylko dwa stany.
  • ListPreference, czyli lista wyboru. Otwiera dialog z opcjami do wyboru, gdzie możemy (w zależności od samej listy) wybrać jeden element lub kilka. Swoją wartość zazwyczaj zapisuje jako liczbę, ale może być to również ciąg znaków.
  • EditTextPreference, czyli dialog z polem do wpisania dowolnego tekstu. Zapisuje się jako String.
Jeżeli podstawowe elementy ustawień nas nie satysfakcjonują, zawsze możemy stworzyć własne. Nadal jednak należy pamiętać o typach, jakie SharedPreferences może przetrzymywać.

3. Definiujemy!

Przejdźmy do stworzenia prostych ustawień. Użyjemy każdego z powyższych pól, aby przedstawić ich działanie. Nie jest to trudne i zajmuje tylko chwilę. Wiecie, że Preference definiujemy w pliku XML. Nie znajduje się on jednak w katalogu /layout, a swoim własnym - /xml. Jeżeli go nie mamy, to powinniśmy stworzyć. Nazwa pliku jest dowolna, zazwyczaj jednak stosuje się tytuł preferences.xml. Tworzymy więc ten plik, a w nim dodajemy nasze elementy:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <CheckBoxPreference
        android:defaultValue="true"
        android:key="pref_checkbox"
        android:summary="Otrzymujemy true albo false"
        android:title="Checkbox" />

    <EditTextPreference
        android:dialogTitle="EditText"
        android:key="pref_edittext"
        android:summary="Dowolna, wpisana wartość"
        android:title="EditText" />

    <ListPreference
        android:dialogTitle="Nasza lista"
        android:entries="@array/przykladowa_lista"
        android:entryValues="@array/przykladowa_lista_wartosci"
        android:summary="Zwraca liczbę lub string"
        android:key="pref_list"
        android:title="Lista" />

</PreferenceScreen>
Od razu możemy zauważyć charakterystyczne atrybuty jak key, title i defaultValue. Ich nazwa mówi sama za siebie i raczej wiadomo do czego każdy z nich służy. Oczywiście samych atrybutów jest więcej. Jeżeli chcemy stworzyć dużą liczbę ustawień, to dobrym rozwiązaniem będzie stworzenie nagłówka dla każdej sekcji. Te nazywamy grupami, i w przeciwieństwie do powyższego przykładu, nie tworzymy całości pod sobą, a dopisujemy do wyznaczonej części - PreferenceCategory. Jednym z atrybutów tego elementu jest oczywiście nazwa grupy.
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory android:title="Przykładowa grupa" >

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="pref_checkbox"
            android:summary="Otrzymujemy true albo false"
            android:title="Checkbox" />
        
    </PreferenceCategory>

    <EditTextPreference
        android:dialogTitle="EditText"
        android:key="pref_edittext"
        android:summary="Dowolna, wpisana wartość"
        android:title="EditText" />

    <ListPreference
        android:dialogTitle="Nasza lista"
        android:entries="@array/przykladowa_lista"
        android:entryValues="@array/przykladowa_lista_wartosci"
        android:key="pref_list"
        android:summary="Zwraca liczbę lub string"
        android:title="Lista" />
		
</PreferenceScreen>
Czasami w ustawieniach możemy zauważyć jeszcze jeden typ Preference - taki, w którym po wybraniu go przechodzimy do kolejnego ekranu. To może być cecha PreferenceFragment, ale również i PreferenceScreen - specjalnego tagu to pokazywania opcji w oddzielnym oknie. Implementacja wygląda podobnie jak w innych tagach.
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory android:title="Przykładowa grupa" >

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="pref_checkbox"
            android:summary="Otrzymujemy true albo false"
            android:title="Checkbox" />
    </PreferenceCategory>

    <EditTextPreference
        android:dialogTitle="EditText"
        android:key="pref_edittext"
        android:summary="Dowolna, wpisana wartość"
        android:title="EditText" />

    <ListPreference
        android:dialogTitle="Nasza lista"
        android:entries="@array/przykladowa_lista"
        android:entryValues="@array/przykladowa_lista_wartosci"
        android:key="pref_list"
        android:summary="Zwraca liczbę lub string"
        android:title="Lista" />

    <PreferenceScreen
        android:persistent="false"
        android:title="PreferenceScreen" >

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="pref_preferencescreen_checkbox"
            android:summary="Otrzymujemy true albo false"
            android:title="Checkbox" />
    </PreferenceScreen>

</PreferenceScreen>
Kod XML już mamy, teraz czas na opisanie PreferenceActivity. Tworzymy nową klasę w naszej paczce, o nazwie na przykład View_Preference. Powinniście sobie od razu poradzić, bo implementacja wygląda jak w normalnym Activtiy. Jeżeli jednak nie wiecie o co chodzi, to mój kod macie poniżej:
package com.example.moja.pierwsza.aplikacja;

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class View_Preference extends PreferenceActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	addPreferencesFromResource(R.xml.preference);
    }

}
Najważniejszą rzeczą do zapamiętania w tym elemencie jest powiązanie pliku XML z naszą klasą. Nadal robimy to (najlepiej) w OnCreate(), ale zamiast setContentView używamy addPreferencesFromResource. Mamy więc obsługę ustawień, czas na wyświetlanie zmian. Tworzymy kolejną klasę, tym razem będzie to normalne Activity. U mnie plik ten nazywać się będzie View_Preference_Activity. Użyłem również starego szablonu, bo i tak pobrane dane będziemy chcieli wyświetlić jako tekst, a do ustawień będziemy chcieli przejść przez tapnięcie elementu (tutaj przycisku). Pobieranie wartości z SharedPreferences będzie się odbywać w onResume(), bo za każdym powrotem do Activity chcemy widzieć aktualne dane (możemy również robić to w specjalnym listenerze nazwanym onSharedPreferenceChanged). Nowa klasa prezentuje się tak:
package com.example.moja.pierwsza.aplikacja;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class View_Preference_Activity extends Activity {
    
    TextView textView;
    SharedPreferences zapisane_ustawienia;
    
    // Nasze pola w ustawieniach
    Boolean CheckBox;
    String EditText;
    int List;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.view_listener_click);
	
	final Button przycisk = (Button) findViewById(R.id.bt_click_listener);	
	final ImageView obrazek = (ImageView) findViewById(R.id.iV_click_listener);
	textView = (TextView) findViewById(R.id.tv_click_listener);
	
	// nasze ustawienia
	zapisane_ustawienia = PreferenceManager.getDefaultSharedPreferences(this);
	
	// ukrywamy obrazek, bo nie będzie nam potrzebny
	obrazek.setVisibility(View.GONE);
	
	przycisk.setOnClickListener(new OnClickListener() {
	    
	    public void onClick(View v) {
		Intent otworzopcje = new Intent(View_Preference_Activity.this, View_Preference.class);
		startActivity(otworzopcje);
	    }
	    
	});
	
    }

    @Override
    protected void onResume() {
	super.onResume();
	
	// zmienna checkbox = nasze ustawienia->pobierz warość true/false z klucza (+ domyślne ustawienie)
	CheckBox = zapisane_ustawienia.getBoolean("pref_checkbox", false);
	EditText = zapisane_ustawienia.getString("pref_edittext", "brak");
	List = Integer.parseInt(zapisane_ustawienia.getString("pref_list", "0"));
	
	// Tekst do wyświetlenia w textView
	// \n to nowa linia
	String ustawienia = "";
	ustawienia += "Ustawienia to: \nCheckbox: ";

	if (CheckBox) {
	    ustawienia += "true \n";
	} else {
	    ustawienia += "false \n";
	}
	
	ustawienia += "EditText: " + EditText + " \n";
	ustawienia += "Lista: " + List + " \n";
	
	// Wyświetlamy wartości w textview
	textView.setText(ustawienia);
	
    }

}
Ustawienia aplikacji pobieramy z SharedPreferences, do każdej opcji nawigujemy poprzez wybranie odpowiedniego typu i podanie klucza. Później wystarczy operować na pobranych danych. To wszystko na dziś, sporo kodu, ale same ustawienia do trudnych nie należą. Dziękuję za uwagę i zapraszam do kolejnej części (wkrótce :))