Criando Telas de Preferências em Android • Introdução Quando criamos qualquer tipo de aplicação, seja ela mobile, web ou desktop, na grande maioria dos casos se faz necessária uma tela de preferências, ou configurações. Para plataformas mobile teremos dificuldades em plataformas um pouco mais antigos, como é o caso do Java ME, onde não possuímos recursos avançados de persistência de dados. Já no BlackBerry e no iPhone este trabalho é facilitado. Porém, criar tela de preferências no Android é simples, rápido e fácil. A persistência dos dados é feita de forma automática, o programador não precisa se preocupar com isso. Basta mostrar a tela com as opções para o usuário e pronto. Neste artigo vamos ver como fazer isso criando um aplicativo básico. Digamos que vamos construir um despertador, e para isso, precisamos saber o fuso horário, a cidade do usuário e o ringtone que será tocado. Não vamos programar a lógico do aplicativo de despertador mesmo, apenas criaremos a tela de preferências para prova de conceito.
• Parte 1: Tela Inicial A tela inicial será simples. Mostrará apenas um relógio de enfeite e um botão que leva o usuário para a tela de preferências. Veja a Figura 1;
Figura 1: Tela Inicial Despertador.
Veja agora o arquivo .xml desta interface: Listagem 1: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/imgRelogio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> <Button android:text="Preferências" android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> </LinearLayout>
Obs: Para saber mais sobre interfaces gráficas no Android acesse http://issuu.com/ricardoogliari/docs/como_criar_interfaces_graficas_co m_android. Para iniciantes em Android leiam http://issuu.com/ricardoogliari/docs/criando_seu_primeiro_aplicativo_a ndroid_no_eclipse. No http://issuu.com/ricardoogliari o leitor também encontra outros artigos. Estamos usando um LinearLayout para orientar os componentes verticalmente e linearmente. Configura tanto sua largura (layout_width) como altura (layout_height) com o tamanho da tela. Adiciono duas Views a tela, um ImageView e um Button. Ambos configuram largura e altura como wrap_content , ou seja, referente ao tamanho do seu conteúdo. Ambos são alinhados centralizados horizontalmente, através do layout_gravity. Os dois recebem um id único. Button recebe um texto. Perceba que não configuramos a imagem do ImageView ainda.
Agora vamos analisar a classe principal do projeto, aquela que herda de Activity. Listagem 1: 1: package com.persistencia.estudo; 2: 3: import android.app.Activity; 4: ... 5: public class PersistenciaDados extends Activity { 6: 7: public void onCreate(Bundle savedInstanceState) { 8: super.onCreate(savedInstanceState); 9: setContentView(R.layout.main); 10: 11: Button prefBtn = (Button) findViewById(R.id.btn); 12: prefBtn.setOnClickListener(new View.OnClickListener() { 13: public void onClick(View v) {} 14: }); 15: 16: ImageView img = (ImageView) findViewById(R.id.imgRelogio); 17: img.setImageResource(android.R.drawable.ic_lock_idle_alarm); 18: } 19: }
Este código não tem nenhum segredo. Criamos uma classe que herda de Activity na linha 5. Sobrescrevemos o onCreate (linha 7), chamamos o setContentView para ligar a atividade a uma Screen (linha 9). Na linha 11 recuperamos o objeto Button e definimos seu OnClickListener. Perceba que ainda não programamos nenhuma ação. Na linha 16 recuperamos o objeto ImageView. Como ainda não havíamos definido sua imagem, fazemos isso na linha 17. Perceba que não estamos usando R.drawable, mas sim, android.R.drawable. Neste caso, buscamos os recursos do Android, não aqueles que estão na pasta res/drawable de nosso projeto. Existem diversas imagem que você pode usar no se projeto livremente. Para maiores informações acesse o link: http://developer.android.com/reference/android/R.drawable.html. Agora vamos criar a tela de preferências.
• Parte 2: Tela de Preferências
Nosso trabalho agora é construir a interface de preferências abaixo:
Figura 2: Tela de preferências do Despertador.
Agora um pouco mais de atenção é bem vinda, pois é aqui que a brincadeira começa ficar interessante. Vamos dissecar o XML (preferences.xml) desta tela em partes. Veja a Listagem 3: Listagem 3: <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> ... </PreferenceScreen>
Não estamos utilizando nenhum dos gerenciadores de layouts “comuns”, nem um ViewGroup “normal”. Aonde está o TableLayout, o LinearLayout, etc? Devemos utilizar o PreferenceScreen. Ele representa a raiz de uma hierarquia de preferências, representadas por Preference.
A classe Preference tem algumas subclasses que são utilizadas como Views dentro da hierarquia de preferências. Reveja novamente a Figura 2, cada uma das opções é uma destas subclasses, veremos algumas aqui: • CheckBoxPreference: uma simples caixa de seleção, que pode retornar true ou false; • ListPreference: mostra uma caixa de seleção popup onde apenas um item pode ser selecionado. Retorna o identificador da opção escolhida; • EditTextPreference: mostra um diálogo com uma caixa de texto. Retorna uma String; • RingtonePreference: mostra um popup com os ringtones; • Preference: uma preferência customizada; • PreferenceScreen: conduz o usuário para outra tela com outras preferências; • PreferenceCategory: categoriza preferências, Estas subclasses representam o bloco básico de interface gráfica de uma preferência. Cada uma delas será associada com um SharedPreferences para armazenar e recuperar a informação. Obs: SharedPreferences é umas das formas de persistência de dados que o Android oferece. Futuramente pretendo escrever somente sobre esta tema em um futuro artigo. Por hora, basta saber que ele armazena dados primitivos em pares chaves-valor. Também, lembre-se que os dados persistem sobre sessões. Agora que o leitor já conhece o PreferenceCategory, vamos modificar nosso xml e adicionar as categorias: Listagem 4: ... <PreferenceCategory android:title="Principal"> PreferenceCategory> <PreferenceCategory android:title="Outras Preferências"> PreferenceCategory> <PreferenceCategory android:title="Preferências Avançadas"> PreferenceCategory> ...
Se por acaso o aplicativo for executado agora veremos algo semelhante à Figura 3:
Figura 3: Somente categorias.
Vamos em frente. Na Listagem 5 criaremos a primeira preferência que o usuário poderá editar: Listagem 5: ... 1: <PreferenceCategory android:title="Principal"> 2: <CheckBoxPreference android:title="Habilita Preferências" 3: android:key="EnablePreferences" android:summary="Marque para hablitar outras opções" /> 4: </PreferenceCategory> ...
A linha 1 e 4 são da listagem anterior. Na linha 2 criamos um CheckBoxPreference, que cria uma preferência que é editada somente como verdadeiro/falso, ligado/desligado, etc. Podemos usar este campo para apresentar ao usuário opções como: • Permite som? • Permite acesso a Internet? • Permite localização por GPS? • Armazenar dados em cachê? As opções desta tag são: • android:title: título da view; • android:key: chave para possível acesso via código Java; • android:summary: sumário da view. Se o aplicativo fosse testado agora viríamos a tela da Figura 4.
Figura 4: Categoria principal visível.
Agora vamos criar as “Outras Preferências”. Veja a Listagem 6: Listagem 6: ... <PreferenceCategory android:title="Outras Preferências"> <ListPreference android:title="Fuso Horario" android:key="fusoHorario" android:dependency="EnablePreferences" android:summary="Selecione seu fuso horário" android:entries="@array/fusos" android:entryValues="@array/fusosValues" /> <EditTextPreference android:title="Cidade" android:key="Cidade" android:dependency="EnablePreferences" android:summary="Informe a sua cidade" android:dialogTitle="Informe a sua cidade" android:defaultValue="David Canabarro" /> <RingtonePreference android:title="Ringtone" android:key="Ringtone" android:dependency="EnablePreferences" android:summary="Selecione o Ringtone" android:ringtoneType="all" /> </PreferenceCategory> ...
Ao testar o aplicativo neste momento veremos a seguinte tela:
Figura 5: Categoria “Outras Preferências” visível.
Agora vamos aos poucos detalhar esta tela. A primeira preferência é uma lista de opções, onde o usuário escolher o seu fuso horário. É representado pela classe ListPreference: <ListPreference android:title="Fuso Horario" android:key="fusoHorario" android:dependency="EnablePreferences" android:summary="Selecione seu fuso horário" android:entries="@array/fusos" android:entryValues="@array/fusosValues" />
Algumas propriedades são as mesmas da View anterior: android:title, android:key e android:summary. Acrescenta-se a estas: • android:dependency: condiciona seu estado selecionável a seleção ou não da preferência com a key EnabledPreferences, que é a propriedade vista anteriormente; • android:entries: mostra o rótulo dos textos que serão exibidos na caixa de rolagem; • android:values: informa o valor de cada item da caixa de rolagem. Os dois arrays, res/values/arrays.xml:
fusos
e
fusosValues
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="fusos"> <item>Brasília</item> <item>Assunção</item> <item>Buenos Aires</item> <item>La Paz</item> <item>Montevideu</item> <item>Santiago</item> <item>Saturday</item> <item>Bogotá</item> <item>Caracas</item> <item>Lima</item> <item>Quito</item> </string-array> <string-array name="fusosValues"> <item>1</item> <item>2</item> <item>3</item> <item>4</item> <item>5</item> <item>6</item> <item>7</item> <item>8</item> <item>9</item> <item>10</item>
estão
no
arquivo
<item>11</item> </string-array> </resources>
Veremos estes valores ao clicar sobre o Preference do fuso horário. Veja a Figura 6:
Figura 6: Seleção de fuso horário.
A segunda opção é um EditTextPreference, que mostra um diálogo com uma caixa de texto ao ser selecionado. Ele possui os campos já vistos nos outros Preferences, exceto por: • android:dialogTitle: informa o título da caixa de diálogo; • android:defaultValue: valor padrão da caixa de diálogo. <EditTextPreference android:title="Cidade" android:key="Cidade" android:dependency="EnablePreferences" android:summary="Informe a sua cidade" android:dialogTitle="Informe a sua cidade" android:defaultValue="David Canabarro" />
Ao selecionar esta opção veremos a tela da Figura 7:
Figura 7: Utilizando TableLayout com espaço a mais.
O último campo é semelhante a lista, porém, o RingtonePreference automaticamente preenche a caixa de seleção com os ringotones disponíveis no aparelho. Para isso, basta marcar all na opção android:ringtoneType. <RingtonePreference android:title="Ringtone" android:key="Ringtone" android:dependency="EnablePreferences" android:summary="Selecione o Ringtone" android:ringtoneType="all" />
Falta pouco!!!! A última preferência foi colocada nesta tela apenas para exemplificar a possibilidade de subdividir as preferências em mais de uma tela. Veja a Listagem 7: Listagem 7: <PreferenceCategory android:title="Preferências Avançadas"> <PreferenceScreen android:title="Proxy"> <EditTextPreference android:title="Informe seu Proxy" android:key="proxy" /> </PreferenceScreen>
</PreferenceCategory
Um PreferenceScreen dentro de outro PreferenceScreen? Isso mesmo, isso causa a divisão das propriedades em duas telas, veja: primeiramente vamos abrir o aplicativo Despertador:
Clicando em Proxy somos direcionados a outra tela:
Neste ponto temos um EditTextPreference. Já visto neste artigo. Agora que construímos a tela de preferência vamos criar a sua Activity. Veja a Listagem 8: Listagem 8: package com.persistencia.estudo; imports... public class Preferencias extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.layout.preferences); } }
Existem várias diferenças aqui. A primeira é que a classe herda de PreferenceActiviy, não de Actvity. Esta classe mostra uma hierarquia de Preferences como uma lista. As preferências já serão salvas automaticamente no SharedPreferences quando o usuário interage com eles. O setContentView também sumiu. No lugar dele temos o método addPreferencesFromResources(R.layout.preferences), que recebe como parâmetro o xml que trabalhamos a algumas páginas atrás. Para mostrar a PreferenceActiviy vamos editar a classe principal, a PersistenciaDados. Veja a Listagem 9. Listagem 9: Button prefBtn = (Button) findViewById(R.id.btn); prefBtn.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Intent settingsActivity = new Intent(getBaseContext(), Preferencias.class); startActivity(settingsActivity); } });
Quando o botão de preferências é clicado, apenas criamos um Intent com a classe Preferências (aquela que herda de PreferenceActivity) e iniciamos esta mesma Intent.
• Parte 3: Persistência dos dados Para realmente verificarmos que a persistências das preferências acontece basta edita-las, sair da aplicação, retornar e abrir a tela de preferências novamente. Os valores estarão lá. Mas para ter mais certeza vamos editar a PersistenciaDados novamente e acrescentar o seguinte código no método onCreate: SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); cid = prefs.getString("cidade", "David"); Toast.makeText(getBaseContext(), cid, Toast.LENGTH_LONG).show();
Neste novo código apenas recuperamos as preferências contidas na classe SharedPreferences através do método getDefaultSharedPreferences.
Posteriormente recuperamos a String com a chave cidade, retornando “David” como valor padrão, para o caso da preferência cidade ainda não estiver sido configurada. Lembram da preferência de cidade quando a criamos, veja o início dela: <EditTextPreference android:title="Cidade" android:key="cidade"
Por fim, apenas mostramos a cidade recuperada em um Toast. O resultado pode ser visto na Figura 8:
Figura 8: Toast mostrando a cidade de preferência.
CONCLUSÃO Neste artigo foi possível ver o quanto é útil a união das classes PreferenceActicity, Preference e suas subclasses. De uma maneira limpa e concisa, o Android nos permite criar telas de preferências sensacionais em questão de minutos.
SOBRE MIM Meu nome é Ricardo da Silva Ogliari, sou graduado em Ciência da Computação pela Universidade de Passo Fundo. Pós-graduando em Web: Estratégias de Inovação e Tecnologia, no Senac SP. Desenvolvedor mobile a cerca de 6 anos; Trabalho atualmente na Navita Tecnologia. Escrevo artigos para algumas revistas nacionais especializadas. Sou criador e mantenedor do http://www.mobilidadetudo.com e sou um dos membros do www.javamovel.com. Palestrei em eventos nacionais e internacionais, como o FISL, Java Day e o JustJava, além de ter dezenas de artigos meus espalhados pela internet.