Tutorial Android #23 – Widgets (I)

Padrão

Olá pessoal! No post de hoje vamos ver a primeira parte sobre como criarmos widgets no Android.

Pra quem não sabe, widgets é uma espécie de miniatura do aplicativo que você pode deixar em uma das áreas de trabalho do Android, colocando à disposição do usuário informações de maneira mais rápida e prática. O widget também pode redirecionar o usuário para o aplicativo principal, funcionando como uma espécie de “atalho”.

Bom, vamos então começar a colocar a mão na massa!

Primeiramente, precisamos definir o layout do nosso widget. Para isso, crie o arquivo widget.xml dentro da pasta res/layout. Ele será bastante simples, inicialmente apenas exibindo o nome de um restaurante cadastrado. Sendo assim, ele terá apenas um TextView em sua estrutura:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/frame">
    <TextView android:id="@+id/nome"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:textSize="10pt"
        android:textColor="#FFFFFF"/>
</RelativeLayout>

De diferente do que já fizemos das outras vezes, somente as propriedades que modificam o tamanho e a cor do texto. No mais, tudo dentro dos conformes. O arquivo frame.9.png pode ser baixado junto com o projeto no fim do post. Por que este 9? Porque a imagem é uma NinePatch, ideal para compor fundos de frames. Entenda melhor como funciona aqui.

O próximo passo é criarmos uma classe para gerenciar o conteúdo do widget. Inicialmente, apenas crie uma classe chamada WidgetAplicativo dentro do pacote net.rafaeltoledo.restaurante, estendendo AppWidgetProvider.

package net.rafaeltoledo.restaurante;

import android.appwidget.AppWidgetProvider;

public class WidgetAplicativo extends AppWidgetProvider {
}

Continuando, vamos agora definir algumas propriedades do widget em um arquivo XML. Crie dentro da pasta res/xml o arquivo provedor_widget.xml.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
	android:minWidth="300dip"
	android:minHeight="79dip"
	android:updatePeriodMillis="1800000"
	android:initialLayout="@layout/widget"
/>

Basicamente definimos a largura e altura mínimas, o tempo de atualização das informações do widget (no caso, dos restaurantes – a cada 30 minutos) e qual o layout a ser utilizado por ele (no caso, o que definimos no XML anterior).

Em seguida, precisamos atualizar o AndroidManifest.xml para que o nosso aplicativo suporte o widget. Adicione o seguinte nó receiver ao final do nó application.

<receiver android:name=".WidgetAplicativo"
	android:label="@string/app_name"
	android:icon="@drawable/ic_launcher">
	<intent-filter>
		<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
	</intent-filter>
	<meta-data android:name="android.appwidget.provider"
		android:resource="@xml/provedor_widget" />
</receiver>

Neste trecho, definimos que a classe que representa o widget é a WidgetApp, que o nome e o ícone a serem exibidos nas opções são os mesmos da aplicação no menu (app_name e ic_launcher). Além disso, definimos que o widget realizará operações de atualização e que suas propriedades estão definidas no arquivo provedor_widget dentro da pasta xml.

Por fim, vamos implementar o método onUpdate() para a classe WidgetApp. É este método que fará a busca em nosso banco de dados para exibir o nome de um restaurante.

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
		int[] appWidgetIds) {
	ComponentName cn = new ComponentName(context, WidgetApp.class);
	RemoteViews atualizarFrame = new RemoteViews("net.rafaeltoledo.restaurante", R.layout.widget);
	GerenciadorRestaurantes gerenciador = new GerenciadorRestaurantes(context);

	try {
		Cursor c = gerenciador.getReadableDatabase().rawQuery("SELECT COUNT(*) FROM restaurantes", null);
		c.moveToFirst();
		int count = c.getInt(0);
		c.close();

		if (count > 0) {
			int offset = (int) (count * Math.random());
			String args[] = {String.valueOf(offset)};
			c = gerenciador.getReadableDatabase().rawQuery("SELECT nome FROM restaurantes LIMIT 1 OFFSET ?", args);
			c.moveToFirst();
			atualizarFrame.setTextViewText(R.id.nome, c.getString(0));
		} else {
			atualizarFrame.setTextViewText(R.id.nome, context.getString(R.string.vazio));
		}
	} finally {
		gerenciador.close();
	}

	appWidgetManager.updateAppWidget(cn, atualizarFrame);
}

Resumidamente, esse trecho de código:

  • Cria um objeto RemoteView, que nos permite modificar o widget;
  • Estabelece uma conexão com o banco de dados;
  • Verifica quantos restaurantes salvos existem;
  • Carrega um restaurante aleatório (por isso o uso de Math.random());
  • Exibe o nome do restaurante, ou uma mensagem dizendo que não existem restaurantes cadastrados;
  • Atualiza o widget propriamente dito.

A mensagem “vazio” (R.string.vazio) deve ser definida nos seus arquivos string.xml nas pastas values que você tem. No meu caso, vou defini-la em português e em espanhol (idiomas que minha aplicação suporta).

<string name="vazio">Nenhum registro.</string>
<string name="vazio">Ningún registro.</string>

E pronto! Para ativar o widget, clique e segure sobre a área de trabalho para aparecer o menu e a opção de inserir widget.

Pra baixar o projeto, só clicar aqui.

Bom, é isso pessoal! No próximo post vamos melhorar esse widget! Até lá!

  • André Kunde

    Parabéns Rafael!
    Muito legal este tutorial…. eu já tinha visto um sobre widgets, mas não tão claro assim…

    Queria saber também se você pode me ajudar…. Estou acompanhando a sequencia dos tutoriais, mas queria mudar a minha aplicação, deixando de utilizar as abas…. gostaria de trabalhar com botões que chamam outras activities…. você sabe como posso fazer??

    • Basicamente, você realiza a a chamadas a novas Activities através de Intents. Nos tutoriais anteriores você encontra vários exemplos desse tipo de chamadas. Em alguns dias vou montar uma página pra centralizar todos os tutoriais e facilitar a busca entre os conteúdos. 🙂

  • André Kunde

    uhm….
    é…. eu acompanhei só até antes da integração com o twitter….
    é que eu tentei ‘separar’ a tela lista da tela de cadastro/alteração
    com uma activity para cada tela….
    aí não consegui acertar a passagem de parâmetros…. :/

    mas então vou aguardar a organizada na página…. valeu!