page.title=Configurações page.tags=preferência,preferenceactivity,preferencefragment @jd:body

Neste documento

  1. Visão geral
    1. Preferências
  2. Definição de preferências em XML
    1. Criação de grupos de configuração
    2. Uso de intenções
  3. Criação de uma atividade de preferência
  4. Uso de fragmentos de preferência
  5. Configuração de valores padrão
  6. Uso de cabeçalhos de preferência
    1. Criação do arquivo de cabeçalhos
    2. Exibição de cabeçalhos
    3. Compatibilidade de versões mais antigas com cabeçalhos de preferência
  7. Leitura de preferências
    1. Escuta de alterações de preferência
  8. Gerenciamento de uso de rede
  9. Composição de uma preferência personalizada
    1. Especificação da interface do usuário
    2. Salvamento do valor da configuração
    3. Inicialização do valor atual
    4. Fornecimento de um valor padrão
    5. Salvamento e restauração do estado da preferência

Classes principais

  1. {@link android.preference.Preference}
  2. {@link android.preference.PreferenceActivity}
  3. {@link android.preference.PreferenceFragment}

Veja também

  1. Guia de projeto de configurações

Geralmente os aplicativos contêm configurações que permitem aos usuários modificar características e comportamentos do aplicativo. Por exemplo: alguns aplicativos permitem aos usuários especificar se as notificações estão ativadas ou especificar a frequência com que o aplicativo sincroniza dados com a nuvem.

Para fornecer configurações ao aplicativo, é preciso usar as APIs {@link android.preference.Preference} do Android para programar uma interface coerente com a experiência do usuário em outros aplicativos Android (inclusive as configurações do sistema). Este documento descreve como programar as configurações do aplicativo por meio de APIs {@link android.preference.Preference}.

Projeto de configurações

Para obter mais informações sobre o projeto de configurações, leia o guia de projeto Configurações.

Figura 1. Capturas de tela das configurações do aplicativo Mensagens do Android. A seleção de um item definido por uma {@link android.preference.Preference} abre uma interface para alterar a configuração.

Visão geral

Em vez de usar objetos {@link android.view.View} para criar a interface do usuário, as configurações são criadas por meio de várias subclasses da classe {@link android.preference.Preference} declaradas em um arquivo XML.

Os objetos {@link android.preference.Preference} são as peças fundamentais de uma única configuração. Cada {@link android.preference.Preference} aparece como um item em uma lista e oferece a IU adequada para que os usuários modifiquem a configuração. Por exemplo: uma {@link android.preference.CheckBoxPreference} cria um item de lista que exibe uma caixa de seleção e uma {@link android.preference.ListPreference} cria um item que abre uma caixa de diálogo com uma lista de opções.

Cada {@link android.preference.Preference} adicionada tem um par de valor-chave correspondente que o sistema usa para salvar a configuração em um arquivo {@link android.content.SharedPreferences} padrão para as configurações do aplicativo. Quando o usuário altera uma configuração, o sistema atualiza o valor correspondente no arquivo {@link android.content.SharedPreferences}. O único momento em que se deve interagir diretamente com o arquivo {@link android.content.SharedPreferences} associado é no momento de ler o valor para determinar o comportamento do aplicativo com base na configuração do usuário.

O valor salvo em {@link android.content.SharedPreferences} para cada configuração pode ser um dos seguintes tipos de dados:

Como a IU de configurações do aplicativo é criada com objetos {@link android.preference.Preference} em vez de objetos {@link android.view.View}, é preciso usar uma subclasse {@link android.app.Activity} ou {@link android.app.Fragment} especializada para exibir as configurações de lista:

Veja como configurar a {@link android.preference.PreferenceActivity} e instâncias de {@link android.preference.PreferenceFragment} nas seções sobre a Criação de uma atividade de preferência e Uso de fragmentos de preferência.

Preferências

Toda configuração do aplicativo é representada por uma subclasse específica da classe {@link android.preference.Preference}. Cada subclasse contém um conjunto de propriedades essenciais que permitem especificar itens como o título da configuração e o valor padrão. Cada subclasse também oferece suas propriedades e interface do usuário especializadas. Por exemplo: a figura 1 ilustra uma captura de tela das configurações do aplicativo Mensagens. Cada item de lista na tela de configurações tem, como fundo, um objeto {@link android.preference.Preference} diferente.

A seguir há algumas das preferências mais comuns:

{@link android.preference.CheckBoxPreference}
Exibe um item com uma caixa de seleção para uma configuração que esteja ativada ou desativada. O valor salvo é um booleano (true se estiver selecionada).
{@link android.preference.ListPreference}
Abre uma caixa de diálogo com uma lista de botões de opção. O valor salvo pode ser qualquer um dos tipos de valor compatíveis (listados acima).
{@link android.preference.EditTextPreference}
Abre uma caixa de diálogo com um widget {@link android.widget.EditText}. O valor salvo é um {@link java.lang.String}.

Consulte a classe {@link android.preference.Preference} para ver uma lista de todas as outras subclasses e as propriedades correspondentes.

É claro que as classes embutidas não acomodam todas as necessidades e o aplicativo pode exigir algo mais especializado. Por exemplo: a plataforma atualmente não fornece nenhuma classe {@link android.preference.Preference} para selecionar um número ou data. Portanto, pode ser necessário definir a própria subclasse {@link android.preference.Preference}. Veja mais informações na seção sobre Composição de uma preferência personalizada.

Definição de preferências em XML

Embora se possa instanciar novos objetos {@link android.preference.Preference} em tempo de execução, deve-se definir uma lista de configurações no XML com uma hierarquia de objetos {@link android.preference.Preference}. Recomenda-se o uso de um arquivo XML para definir a coleção de configurações porque o arquivo oferece uma estrutura fácil de ler e simples de atualizar. Além disso, as configurações do aplicativo geralmente são predeterminadas, embora ainda seja possível modificar a coleção em tempo de execução.

Cada subclasse {@link android.preference.Preference} pode ser declarada com um elemento XML correspondente ao nome da classe, como {@code <CheckBoxPreference>}.

É preciso salvar o arquivo XML no diretório {@code res/xml/}. Embora seja possível nomear livremente o arquivo, é mais frequente vê-lo com o nome {@code preferences.xml}. Geralmente só é necessário um arquivo porque as ramificações na hierarquia (que abrem sua própria lista de configurações) são declaradas por meio de instâncias aninhadas de {@link android.preference.PreferenceScreen}.

Observação: se você deseja criar um layout de vários painéis para as configurações, serão necessários arquivos XML separados para cada fragmento.

O nó raiz do arquivo XML deve ser um elemento {@link android.preference.PreferenceScreen <PreferenceScreen>}. É dentro desse elemento que se adiciona cada {@link android.preference.Preference}. Cada filho adicionado dentro do elemento {@link android.preference.PreferenceScreen <PreferenceScreen>} é exibido com um item único na lista de configurações.

Por exemplo:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="pref_sync"
        android:title="@string/pref_sync"
        android:summary="@string/pref_sync_summ"
        android:defaultValue="true" />
    <ListPreference
        android:dependency="pref_sync"
        android:key="pref_syncConnectionType"
        android:title="@string/pref_syncConnectionType"
        android:dialogTitle="@string/pref_syncConnectionType"
        android:entries="@array/pref_syncConnectionTypes_entries"
        android:entryValues="@array/pref_syncConnectionTypes_values"
        android:defaultValue="@string/pref_syncConnectionTypes_default" />
</PreferenceScreen>

Nesse exemplo, existe um {@link android.preference.CheckBoxPreference} e um {@link android.preference.ListPreference}. Os dois itens contêm estes três atributos:

{@code android:key}
Esse atributo é necessário para preferências que persistem a um valor de dados. Ele especifica a chave exclusiva (uma string) que o sistema usa ao salvar o valor dessa configuração em {@link android.content.SharedPreferences}.

As únicas instâncias em que esse atributo é dispensável ocorrem quando a preferência é um {@link android.preference.PreferenceCategory} ou {@link android.preference.PreferenceScreen}, ou quando a preferência especifica um {@link android.content.Intent} para invocar (com um elemento {@code <intent>}) ou um {@link android.app.Fragment} para exibir (com um atributo {@code android:fragment}).

{@code android:title}
Fornece à configuração um nome visível ao usuário.
{@code android:defaultValue}
Especifica o valor inicial que o sistema deve definir no arquivo {@link android.content.SharedPreferences}. Deve-se fornecer um valor padrão para todas as configurações.

Para mais informações sobre todos os outros atributos compatíveis, consulte a documentação {@link android.preference.Preference} (e subclasse respectiva).

Figura 2. Definição de categorias com títulos.
1. A categoria é especificada pelo elemento {@link android.preference.PreferenceCategory <PreferenceCategory>}.
2. O título é especificado com o atributo {@code android:title}.

Quando a lista de configurações excede cerca de 10 itens, pode ser necessário adicionar títulos para definir grupos de configurações ou exibir esses grupos em uma tela separada. Essas opções são descritas nas seções a seguir.

Criação de grupos de configuração

Se você apresentar uma lista de 10 ou mais configurações, os usuários podem ter dificuldade de percorrê-las, compreendê-las e processá-las. Para solucionar isso, pode-se dividir algumas ou todas as configurações em grupos, transformando uma longa lista em várias listas mais curtas. Um grupo de configurações relacionadas pode ser apresentado de uma das seguintes formas:

Pode-se usar uma ou ambas as técnicas de agrupamento para organizar as configurações do aplicativo. Ao decidir qual delas usar e como dividir as configurações, deve-se seguir as diretrizes do guia Configurações de Projeto do Android.

Uso de títulos

Para usar divisores com cabeçalhos entre grupos de configurações (como ilustrado na figura 2), coloca-se cada grupo de objetos {@link android.preference.Preference} dentro de {@link android.preference.PreferenceCategory}.

Por exemplo:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory 
        android:title="@string/pref_sms_storage_title"
        android:key="pref_key_storage_settings">
        <CheckBoxPreference
            android:key="pref_key_auto_delete"
            android:summary="@string/pref_summary_auto_delete"
            android:title="@string/pref_title_auto_delete"
            android:defaultValue="false"... />
        <Preference 
            android:key="pref_key_sms_delete_limit"
            android:dependency="pref_key_auto_delete"
            android:summary="@string/pref_summary_delete_limit"
            android:title="@string/pref_title_sms_delete"... />
        <Preference 
            android:key="pref_key_mms_delete_limit"
            android:dependency="pref_key_auto_delete"
            android:summary="@string/pref_summary_delete_limit"
            android:title="@string/pref_title_mms_delete" ... />
    </PreferenceCategory>
    ...
</PreferenceScreen>

Uso de subtelas

Para usar grupos de configurações em uma subtela (como ilustrado na figura 3), coloque o grupo de objetos {@link android.preference.Preference} dentro de {@link android.preference.PreferenceScreen}.

Figura 3. Subtelas de configuração. O elemento {@code <PreferenceScreen>} cria um item que, quando selecionado, abre uma lista separada para exibir as configurações aninhadas.

Por exemplo:

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- opens a subscreen of settings -->
    <PreferenceScreen
        android:key="button_voicemail_category_key"
        android:title="@string/voicemail"
        android:persistent="false">
        <ListPreference
            android:key="button_voicemail_provider_key"
            android:title="@string/voicemail_provider" ... />
        <!-- opens another nested subscreen -->
        <PreferenceScreen
            android:key="button_voicemail_setting_key"
            android:title="@string/voicemail_settings"
            android:persistent="false">
            ...
        </PreferenceScreen>
        <RingtonePreference
            android:key="button_voicemail_ringtone_key"
            android:title="@string/voicemail_ringtone_title"
            android:ringtoneType="notification" ... />
        ...
    </PreferenceScreen>
    ...
</PreferenceScreen>

Uso de intenções

Em alguns casos, pode ser necessário que um item de preferência abra em um atividade diferente e não na tela de configuração, como um navegador da web para exibir uma página da web. Para invocar um {@link android.content.Intent} quando o usuário seleciona um item de preferência, adicione um elemento {@code <intent>} como filho do elemento {@code <Preference>} correspondente.

Por exemplo, a seguir apresenta-se como usar um item de preferência para abrir uma página da web:

<Preference android:title="@string/prefs_web_page" >
    <intent android:action="android.intent.action.VIEW"
            android:data="http://www.example.com" />
</Preference>

É possível criar intenções implícitas e explícitas usando os seguintes atributos:

{@code android:action}
A ação a atribuir, conforme o método {@link android.content.Intent#setAction setAction()}.
{@code android:data}
Os dados a atribuir, conforme o método {@link android.content.Intent#setData setData()}.
{@code android:mimeType}
O tipo MIME atribuir, conforme o método {@link android.content.Intent#setType setType()}.
{@code android:targetClass}
A parte de classe do nome do componente, conforme o método {@link android.content.Intent#setComponent setComponent()}.
{@code android:targetPackage}
A parte de pacote do nome do componente, conforme o método {@link android.content.Intent#setComponent setComponent()}.

Criação de uma atividade de preferência

Para exibir as configurações em uma atividade, estenda a classe {@link android.preference.PreferenceActivity}. É uma extensão da classe tradicional {@link android.app.Activity} que exibe uma lista de configurações com base em uma hierarquia de objetos {@link android.preference.Preference}. A {@link android.preference.PreferenceActivity} automaticamente persiste às configurações associadas a cada {@link android.preference.Preference} quando o usuário faz uma alteração.

Observação: ao desenvolver um aplicativo para Android 3.0 ou superior, deve-se usar o {@link android.preference.PreferenceFragment}. Consulte a próxima seção sobre o Uso de fragmentos de preferência.

O mais importante é não carregar um layout de vistas durante o retorno de chamada {@link android.preference.PreferenceActivity#onCreate onCreate()}. Em vez disso, chama-se {@link android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} para adicionar à atividade as preferências declaradas em um arquivo XML. Por exemplo: abaixo há o código mínimo necessário para um {@link android.preference.PreferenceActivity} funcional:

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}

Na verdade, esse código é suficiente para alguns aplicativos porque, assim que o usuário modifica uma preferência, o sistema salva as alterações em um arquivo padrão {@link android.content.SharedPreferences} que os componentes do outro aplicativo poderá ler quando for necessário verificar as configurações do usuário. No entanto, muitos aplicativos exigem um pouco mais de código para escutar as alterações que ocorrem nas preferências. Para informações sobre a escuda de alterações no arquivo {@link android.content.SharedPreferences}, consulte a seção sobre Leitura de preferências.

Uso de fragmentos de preferência

Ao desenvolver para Android 3.0 (nível da API 11) ou versões posteriores, deve-se usar um {@link android.preference.PreferenceFragment} para exibir a lista de objetos {@link android.preference.Preference}. Pode-se adicionar um {@link android.preference.PreferenceFragment} a qualquer atividade — não é necessário usar {@link android.preference.PreferenceActivity}.

Os fragmentos permitem uma arquitetura mais flexível para o aplicativo em comparação com o uso de apenas atividades para qualquer tipo de atividade criada. Assim, sugerimos usar {@link android.preference.PreferenceFragment} para controlar a exibição das configurações em vez de {@link android.preference.PreferenceActivity} sempre que possível.

A implementação de {@link android.preference.PreferenceFragment} pode ser tão simples quanto definir o método {@link android.preference.PreferenceFragment#onCreate onCreate()} para carregar um arquivo de preferências com {@link android.preference.PreferenceFragment#addPreferencesFromResource addPreferencesFromResource()}. Por exemplo:

public static class SettingsFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
    ...
}

É possível adicionar esse fragmento a um {@link android.app.Activity} como se faria com qualquer outro {@link android.app.Fragment}. Por exemplo:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }
}

Observação: um {@link android.preference.PreferenceFragment} não tem seu próprio objeto {@link android.content.Context}. Se for necessário um objeto {@link android.content.Context} , é possível chamar {@link android.app.Fragment#getActivity()}. No entanto, tome cuidado para chamar {@link android.app.Fragment#getActivity()} somente quando o fragmento estiver anexado a uma atividade. Quando o fragmento ainda não estiver anexado, ou tiver sido separado durante o fim do seu ciclo de vida, {@link android.app.Fragment#getActivity()} retornará como nulo.

Configuração de valores padrão

As preferências criadas provavelmente definem alguns comportamentos importantes do aplicativo, portanto é necessário inicializar o arquivo {@link android.content.SharedPreferences} associado com os valores padrão de cada {@link android.preference.Preference} quando o usuário abre o aplicativo pela primeira vez.

A primeira coisa a fazer é especificar o valor padrão de cada objeto {@link android.preference.Preference} no arquivo XML com o atributo {@code android:defaultValue}. O valor pode ser qualquer tipo de dados apropriado para o objeto {@link android.preference.Preference} correspondente. Por exemplo:

<!-- default value is a boolean -->
<CheckBoxPreference
    android:defaultValue="true"
    ... />

<!-- default value is a string -->
<ListPreference
    android:defaultValue="@string/pref_syncConnectionTypes_default"
    ... />

Em seguida, a partir do método {@link android.app.Activity#onCreate onCreate()} na atividade principal do aplicativo — e em qualquer outra atividade pela qual o usuário possa entrar no aplicativo pela primeira vez —, chame {@link android.preference.PreferenceManager#setDefaultValues setDefaultValues()}:

PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);

Essa chamada durante {@link android.app.Activity#onCreate onCreate()} garante que o aplicativo seja adequadamente inicializado com as configurações padrão, que o aplicativo pode precisar ler para determinar alguns comportamentos (se, por exemplo, baixará dados enquanto estiver em uma rede de celular).

Esse método usa três argumentos:

Enquanto o terceiro argumento estiver definido como false, pode-se chamar esse método com segurança toda vez que a atividade iniciar sem substituir as preferências salvas do usuário redefinindo-as para os padrões. No entanto, se ele for definido como true, todos os valores anteriores serão substituídos pelos padrões.

Uso de cabeçalhos de preferência

Em casos raros, pode ser necessário projetar as configurações de forma que a primeira tela exiba somente uma lista de subtelas (como as do aplicativo Configurações conforme ilustrado nas figuras 4 e 5). Ao desenvolver um projeto desse tipo para Android 3.0 ou versão posterior, deve-se usar um novo recurso "cabeçalhos" no Android 3.0 em vez de criar subtelas com elementos {@link android.preference.PreferenceScreen} aninhados.

Para criar as configurações com cabeçalhos, é preciso:

  1. Separar cada grupo de configurações em instâncias separadas de {@link android.preference.PreferenceFragment}. Ou seja, cada grupo de configurações precisa de um arquivo XML separado.
  2. Criar um arquivo XML de cabeçalhos que lista cada grupo de configurações e declara que fragmento contém a lista correspondente de configurações.
  3. Estender a classe {@link android.preference.PreferenceActivity} para hospedar as configurações.
  4. Implementar o retorno de chamada {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} para especificar o arquivo de cabeçalhos.

Um grande benefício de usar esse modelo é que {@link android.preference.PreferenceActivity} automaticamente apresenta o layout de dois painéis ilustrado na figura 4 ao executar em telas grandes.

Mesmo se o aplicativo for compatível com versões de Android anteriores à 3.0, é possível programar o aplicativo para usar {@link android.preference.PreferenceFragment} para uma apresentação em dois painéis em dispositivos mais novos e ser compatível com a hierarquia tradicional multitelas em dispositivos mais antigos (veja a seção sobre Compatibilidade de versões mais antigas com cabeçalhos de preferência).

Figura 4. Layout de dois painéis com cabeçalhos.
1. Os cabeçalhos são definidos com um arquivo XML de cabeçalhos.
2. Cada grupo de configurações é definido por um {@link android.preference.PreferenceFragment} especificado por um elemento {@code <header>} no arquivo de cabeçalhos.

Figura 5. Um dispositivo celular com cabeçalhos de configuração. Quando um item é selecionado o {@link android.preference.PreferenceFragment} associado substitui os cabeçalhos.

Criação do arquivo de cabeçalhos

Cada grupo de configurações na lista de cabeçalhos é especificado por um único elemento {@code <header>} dentro de um elemento raiz {@code <preference-headers>}. Por exemplo:

<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <header 
        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one" />
    <header 
        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" >
        <!-- key/value pairs can be included as arguments for the fragment. -->
        <extra android:name="someKey" android:value="someHeaderValue" />
    </header>
</preference-headers>

Com o atributo {@code android:fragment}, cada cabeçalho declara uma instância de {@link android.preference.PreferenceFragment} que deve abrir quando o usuário selecionar o cabeçalho.

O elemento {@code <extras>} permite passar os pares de valores-chave para o fragmento em um {@link android.os.Bundle}. O fragmento pode recuperar os argumentos chamando {@link android.app.Fragment#getArguments()}. Há vários motivos para passar argumentos ao fragmento, mas um bom motivo é reutilizar a mesma subclasse de {@link android.preference.PreferenceFragment} para cada grupo e usar o argumento para especificar o arquivo XML de preferências que o fragmento deve carregar.

Por exemplo, a seguir há um fragmento que pode ser reutilizado em vários grupos de configurações, quando cada cabeçalho define um argumento {@code <extra>} com a chave {@code "settings"}:

public static class SettingsFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String settings = getArguments().getString("settings");
        if ("notifications".equals(settings)) {
            addPreferencesFromResource(R.xml.settings_wifi);
        } else if ("sync".equals(settings)) {
            addPreferencesFromResource(R.xml.settings_sync);
        }
    }
}

Exibição de cabeçalhos

Para exibir os cabeçalhos de preferência, é preciso implementar o método de retorno de chamada {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} e chamar {@link android.preference.PreferenceActivity#loadHeadersFromResource loadHeadersFromResource()}. Por exemplo:

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.preference_headers, target);
    }
}

Quando o usuário seleciona um item de uma lista de cabeçalhos, o sistema abre o {@link android.preference.PreferenceFragment} associado.

Observação: ao usar cabeçalhos de preferência, a subclasse de {@link android.preference.PreferenceActivity} não precisa implementar o método {@link android.preference.PreferenceActivity#onCreate onCreate()} porque a única tarefa necessária para a atividade é carregar os cabeçalhos.

Compatibilidade de versões mais antigas com cabeçalhos de preferência

Se o aplicativo for compatível com versões de Android anteriores à 3.0, ainda será possível usar cabeçalhos para fornecer um layout em dois painéis ao executar no Android 3.0 e versões posteriores. Basta criar um arquivo XML de preferências adicional que usa elementos básicos {@link android.preference.Preference <Preference>} que se comportam como os itens de cabeçalho (para uso das versões mais antigas do Android).

No entanto, em vez de abrir um novo {@link android.preference.PreferenceScreen}, cada elemento {@link android.preference.Preference <Preference>} envia um {@link android.content.Intent} ao {@link android.preference.PreferenceActivity} que especifica que arquivo XML de preferência carregar.

Por exemplo, abaixo há um arquivo XML de cabeçalhos de preferência usado no Android 3.0 e posterior ({@code res/xml/preference_headers.xml}):

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <header 
        android:fragment="com.example.prefs.SettingsFragmentOne"
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one" />
    <header 
        android:fragment="com.example.prefs.SettingsFragmentTwo"
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" />
</preference-headers>

E apresenta-se também um arquivo de preferências que fornece os mesmos cabeçalhos de versões de Android mais antigas que a 3.0 ({@code res/xml/preference_headers_legacy.xml}):

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <Preference 
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one"  >
        <intent 
            android:targetPackage="com.example.prefs"
            android:targetClass="com.example.prefs.SettingsActivity"
            android:action="com.example.prefs.PREFS_ONE" />
    </Preference>
    <Preference 
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" >
        <intent 
            android:targetPackage="com.example.prefs"
            android:targetClass="com.example.prefs.SettingsActivity"
            android:action="com.example.prefs.PREFS_TWO" />
    </Preference>
</PreferenceScreen>

Como a compatibilidade com {@code <preference-headers>} foi adicionada no Android 3.0, o sistema chama {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} em seu {@link android.preference.PreferenceActivity} somente ao executar em Androd 3.0 ou posterior. Para carregar o arquivo de cabeçalhos de “legado" ({@code preference_headers_legacy.xml}), é preciso verificar a versãodo Android e, se a versão for mais antiga que o Android 3.0 ({@link android.os.Build.VERSION_CODES#HONEYCOMB}), chama {@link android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} para carregar o arquivo de cabeçalho legado. Por exemplo:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}

// Called only on Honeycomb and later
@Override
public void onBuildHeaders(List<Header> target) {
   loadHeadersFromResource(R.xml.preference_headers, target);
}

Depois só resta tratar o {@link android.content.Intent} passado para a atividade para identificar que arquivo de preferências carregar. Portanto, para recuperar a ação da intenção e compará-la com strings de ações conhecidas usadas nas tags de {@code <intent>} do XML de preferências:

final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
...

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    String action = getIntent().getAction();
    if (action != null && action.equals(ACTION_PREFS_ONE)) {
        addPreferencesFromResource(R.xml.preferences);
    }
    ...

    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}

Observe que chamadas consecutivas a {@link android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} empilharão todas as preferências em uma única lista, portanto certifique-se de que seja chamado somente uma vez, encadeando as condições com declarações else-if.

Leitura de preferências

Por padrão, todas as preferências do aplicativo são salvas em um arquivo acessível de qualquer lugar dentro do aplicativo chamando o método estático {@link android.preference.PreferenceManager#getDefaultSharedPreferences PreferenceManager.getDefaultSharedPreferences()}. Isso retorna o objeto {@link android.content.SharedPreferences} que contém todos os pares de valores-chave associados aos objetos {@link android.preference.Preference} usados em {@link android.preference.PreferenceActivity}.

Por exemplo, abaixo apresenta-se como ler um dos valores de preferência de outra atividade no aplicativo:

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");

Escuta de alterações de preferência

Há alguns motivos pelos quais pode ser necessário ser notificado assim que o usuário altera uma das preferências. Para receber um retorno de chamada quando acontece uma alteração em alguma das preferências, implemente a interface {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener SharedPreference.OnSharedPreferenceChangeListener} e registre a escuta para o objeto {@link android.content.SharedPreferences} chamando {@link android.content.SharedPreferences#registerOnSharedPreferenceChangeListener registerOnSharedPreferenceChangeListener()}.

A interface tem somente um método de retorno de chamada, {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged onSharedPreferenceChanged()}, e pode ser mais fácil implementar a interface como parte da atividade. Por exemplo:

public class SettingsActivity extends PreferenceActivity
                              implements OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
    ...

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
        String key) {
        if (key.equals(KEY_PREF_SYNC_CONN)) {
            Preference connectionPref = findPreference(key);
            // Set summary to be the user-description for the selected value
            connectionPref.setSummary(sharedPreferences.getString(key, ""));
        }
    }
}

Nesse exemplo, o método verifica se a configuração alterada se destina a uma chave de preferência conhecida. Ele chama {@link android.preference.PreferenceActivity#findPreference findPreference()} para obter o objeto {@link android.preference.Preference} alterado para que possa modificar o sumário do item como uma descrição da seleção do usuário. Ou seja, quando a configuração for uma {@link android.preference.ListPreference} ou outra configuração de múltipla escolha, deve-se chamar {@link android.preference.Preference#setSummary setSummary()} quando a configuração for alterada para exibir o status atual (como a configuração Suspensão mostrada na figura 5).

Observação: conforme descrito no documento do Projeto para Android sobre Configurações, recomendamos atualizar o sumário de {@link android.preference.ListPreference} a cada vez que o usuário alterar a preferência para descrever a configuração atual.

Para um gerenciamento adequado do ciclo de vida na atividade, recomendamos registrar e remover o registro de {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener} durante os retornos de chamada de {@link android.app.Activity#onResume} e {@link android.app.Activity#onPause} respectivamente:

@Override
protected void onResume() {
    super.onResume();
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
    super.onPause();
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}

Atenção: ao chamar {@link android.content.SharedPreferences#registerOnSharedPreferenceChangeListener registerOnSharedPreferenceChangeListener()}, o gerenciador de preferências não armazena atualmente uma referência à escuta. É preciso armazenar uma referência forte à escuta, senão ela será suscetível à coleta de lixo. Recomendamos manter uma referência à escuta nos dados de instância de um objeto que existirá enquanto a escuta for necessária.

Por exemplo: no código a seguir, o autor da chamada não mantém nenhuma referência à escuta. Como resultado, a escuta estará sujeita à coleta de lixo e falhará futuramente em algum momento indeterminado:

prefs.registerOnSharedPreferenceChangeListener(
  // Bad! The listener is subject to garbage collection!
  new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // listener implementation
  }
});

Em vez disso, armazene uma referência à escuta nos dados de instância de um objeto que existirá enquanto a escuta for necessária:

SharedPreferences.OnSharedPreferenceChangeListener listener =
    new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // listener implementation
  }
};
prefs.registerOnSharedPreferenceChangeListener(listener);

Gerenciamento de uso de rede

A partir do Android 4.0, o aplicativo Configurações do sistema permite aos usuários ver o quanto de dados de rede que os aplicativos usam em primeiro e segundo plano. Portanto, os usuários podem desativar os dados em segundo plano de aplicativos individuais. Para evitar que os usuários desativem o acesso do aplicativo a dados em segundo plano, deve-se usar a conexão de dados de forma eficiente e permitir aos usuários refinar o uso de dados do aplicativo por meio das configurações do aplicativo.

Por exemplo: deve-se permitir ao usuário controlar a frequência de sincronização dos dados do aplicativo para uploads/downloads somente quando estiver em Wi-Fi, o aplicativo usar dados em deslocamento etc. Com esses controles disponíveis para eles, é bem menos provável que os usuários desativem o acesso do aplicativo a dados quando eles se aproximam dos limites que definem nas Configurações do sistema porque, em vez disso, podem controlar precisamente a quantidade de dados que o aplicativo usa.

Depois de adicionadas as preferências necessárias em {@link android.preference.PreferenceActivity} para controlar os hábitos de dados do aplicativo, deve-se adicionar um filtro de intenções para {@link android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} no arquivo de manifesto. Por exemplo:

<activity android:name="SettingsActivity" ... >
    <intent-filter>
       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
       <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Esse filtro de intenções indica ao sistema que se trata da atividade que controla o uso de dados do aplicativo. Assim, quando o usuário inspeciona a quantidade de dados que o aplicativo está usando no aplicativo Configurações do sistema, um botão Exibir configurações de aplicativo fica disponível e inicia {@link android.preference.PreferenceActivity} para que o usuário refine a quantidade de dados que o aplicativo usa.

Composição de uma preferência personalizada

A estrutura do Android contém uma variedade de subclasses {@link android.preference.Preference} que permitem criar uma IU com diferentes tipos de configurações. No entanto, pode ser necessário descobrir uma configuração pra a qual não há nenhuma solução embutida, como um seletor de números ou seletor de datas. Nesse caso, será preciso criar uma preferência personalizada, estendendo a classe {@link android.preference.Preference} ou uma das outras subclasses.

Ao estender a classe {@link android.preference.Preference}, há algumas coisas importantes a fazer:

As seções a seguir descrevem como executar cada uma dessas tarefas.

Especificação da interface do usuário

Se a classe {@link android.preference.Preference} for estendida, será preciso implementar {@link android.preference.Preference#onClick()} para definir a ação que ocorre quando o usuário a seleciona. No entanto, a maioria das configurações personalizadas estendem {@link android.preference.DialogPreference} para exibir uma caixa de diálogo, o que simplifica o procedimento. Quando se estende {@link android.preference.DialogPreference}, é preciso chamar {@link android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()} na classe do construtor para especificar o layout da caixa de diálogo.

Por exemplo, eis o construtor de um {@link android.preference.DialogPreference} personalizado que declara o layout e especifica o texto dos botões padrão da caixa de diálogo positiva e negativa:

public class NumberPickerPreference extends DialogPreference {
    public NumberPickerPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        setDialogLayoutResource(R.layout.numberpicker_dialog);
        setPositiveButtonText(android.R.string.ok);
        setNegativeButtonText(android.R.string.cancel);
        
        setDialogIcon(null);
    }
    ...
}

Salvamento do valor da configuração

Para salvar um valor da configuração a qualquer momento, chame um dos métodos {@code persist*()} da classe {@link android.preference.Preference}, como {@link android.preference.Preference#persistInt persistInt()} se o valor da configuração for um inteiro ou {@link android.preference.Preference#persistBoolean persistBoolean()} para salvar um booleano.

Observação: cada {@link android.preference.Preference} pode salvar somente um tipo de dados, portanto é preciso usar o método {@code persist*()} adequado para o tipo de dados usado pela {@link android.preference.Preference} personalizada.

Quando se opta por persistir, a configuração pode depender da classe {@link android.preference.Preference} estendida. Se {@link android.preference.DialogPreference} for estendida, deve-se persistir o valor somente quando a caixa de diálogo fecha devido a um resultado positivo (o usuário seleciona o botão "OK").

Quando uma {@link android.preference.DialogPreference} fecha, o sistema chama o método {@link android.preference.DialogPreference#onDialogClosed onDialogClosed()}. O método contém um argumento booleano que especifica se o resultado do usuário é "positivo" — se o valor é true e, em seguida, o usuário selecionou o botão positivo e você deve salvar o novo valor. Por exemplo:

@Override
protected void onDialogClosed(boolean positiveResult) {
    // When the user selects "OK", persist the new value
    if (positiveResult) {
        persistInt(mNewValue);
    }
}

Nesse exemplo, mNewValue é um membro da classe que retém o valor atual da configuração. A chamada de {@link android.preference.Preference#persistInt persistInt()} salva o valor no arquivo {@link android.content.SharedPreferences} (usando automaticamente a chave especificada no arquivo XML dessa {@link android.preference.Preference}).

Inicialização do valor atual

Quando o sistema adiciona o {@link android.preference.Preference} à tela, ele chama {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} para notificar se a configuração tem um valor persistido. Se não houver valor persistido, essa chamada fornece o valor padrão.

O método {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} passa um booleano, restorePersistedValue, para indicar se um valor já foi persistido para a configuração. Se for true, deve-se recuperar o valor persistindo chamando-se um dos métodos {@code getPersisted*()} da classe {@link android.preference.Preference}, como {@link android.preference.Preference#getPersistedInt getPersistedInt()} para um valor inteiro. Geralmente se recupera o valor persistido para atualizar adequadamente a IU de forma a refletir o valor salvo anteriormente.

Se restorePersistedValue for false, deve-se usar o valor padrão passado no segundo argumento.

@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    if (restorePersistedValue) {
        // Restore existing state
        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
    } else {
        // Set default state from the XML attribute
        mCurrentValue = (Integer) defaultValue;
        persistInt(mCurrentValue);
    }
}

Cada método {@code getPersisted*()} pega um argumento que especifica o valor padrão a usar caso não haja nenhum valor persistido ou se a chave não existir. No exemplo acima, uma constante local é usada para especificar o valor padrão se {@link android.preference.Preference#getPersistedInt getPersistedInt()} não puder retornar um valor persistido.

Atenção: não é possível usar defaultValue como valor padrão no método {@code getPersisted*()} porque seu valor é sempre nulo quando restorePersistedValue é true.

Fornecimento de um valor padrão

Se a instância da classe {@link android.preference.Preference} especificar um valor padrão (com o atributo {@code android:defaultValue}), o sistema chama {@link android.preference.Preference#onGetDefaultValue onGetDefaultValue()} quando instancia o objeto para recuperar o valor. É preciso implementar esse método para que o sistema salve o valor padrão em {@link android.content.SharedPreferences}. Por exemplo:

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
    return a.getInteger(index, DEFAULT_VALUE);
}

Os argumentos do método oferecem todo o necessário: a matriz de atributos e a posição do índice do {@code android:defaultValue}, que é preciso recuperar. É preciso implementar esse método para extrair o valor padrão do atributo porque deve-se especificar um valor padrão local para o atributo caso o valor seja indefinido.

Salvamento e restauração do estado da preferência

Como um {@link android.view.View} em um layout, a subclasse {@link android.preference.Preference} é responsável por salvar e restaurar seu estado caso a atividade ou fragmento seja reiniciado (como ocorre quando o usuário gira a tela). Para salvar e restaurar adequadamente o estado da classe {@link android.preference.Preference}, é preciso implementar os métodos de retorno de chamada do ciclo de vida {@link android.preference.Preference#onSaveInstanceState onSaveInstanceState()} e {@link android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}.

O estado de {@link android.preference.Preference} é definido por um objeto que implementa a interface {@link android.os.Parcelable}. A estrutura do Android fornece esse objeto como um ponto inicial para definir o objeto de estado: a classe {@link android.preference.Preference.BaseSavedState}.

Para definir como a classe {@link android.preference.Preference} salva seu estado, deve-se estender a classe {@link android.preference.Preference.BaseSavedState}. É preciso substituir alguns métodos e definir o objeto {@link android.preference.Preference.BaseSavedState#CREATOR}.

Na maioria dos aplicativos, é possível copiar a implementação a seguir e simplesmente alterar as linhas que tratam o {@code value} se a subclasse {@link android.preference.Preference} salvar um tipo de dados que não seja um inteiro.

private static class SavedState extends BaseSavedState {
    // Member that holds the setting's value
    // Change this data type to match the type saved by your Preference
    int value;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        // Get the current preference's value
        value = source.readInt();  // Change this to read the appropriate data type
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        // Write the preference's value
        dest.writeInt(value);  // Change this to write the appropriate data type
    }

    // Standard creator object using an instance of this class
    public static final Parcelable.Creator<SavedState> CREATOR =
            new Parcelable.Creator<SavedState>() {

        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

Com a implementação acima de {@link android.preference.Preference.BaseSavedState} adicionada ao aplicativo (geralmente como uma subclasse da subclasse {@link android.preference.Preference}), é preciso implementar os métodos {@link android.preference.Preference#onSaveInstanceState onSaveInstanceState()} e {@link android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} da subclasse {@link android.preference.Preference}.

Por exemplo:

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    // Check whether this Preference is persistent (continually saved)
    if (isPersistent()) {
        // No need to save instance state since it's persistent,
        // use superclass state
        return superState;
    }

    // Create instance of custom BaseSavedState
    final SavedState myState = new SavedState(superState);
    // Set the state's value with the class member that holds current
    // setting value
    myState.value = mNewValue;
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    // Check whether we saved the state in onSaveInstanceState
    if (state == null || !state.getClass().equals(SavedState.class)) {
        // Didn't save the state, so call superclass
        super.onRestoreInstanceState(state);
        return;
    }

    // Cast state to custom BaseSavedState and pass to superclass
    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    
    // Set this Preference's widget to reflect the restored state
    mNumberPicker.setValue(myState.value);
}