quarta-feira, 6 de outubro de 2010

Android Widgets 2

Olá povo,

Já coloquei aqui no blog como criar um Widget, mas hoje vou mostrar como tratar eventos em componentes de um Widget.

Crie um novo projeto Android. Vamos começar criando o arquivo de layout do widget. Crie um arquivo /res/layout/widget.xml e deixe-o como abaixo. A imagem que eu estou usando como background eu peguei no próprio diretório do SDK. Procurem por *.9.png lá que vocês vão achar um monte :)


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_frame"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/widget_bg"
>
<Button
android:layout_width="wrap_content"
android:id="@+id/prev" android:text="<<"
android:layout_height="fill_parent"></Button>

<TextView
android:text="@+id/TextView01"
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFF"
android:layout_weight="1"
android:gravity="center">
</TextView>

<Button
android:layout_width="wrap_content"
android:id="@+id/next"
android:text=">>"
android:layout_height="fill_parent">
</Button>
</LinearLayout>

Depois, vamos descrever o nosso Widget. Crie o arquivo /res/xml/meuwidget_info.xml. Nesse arquivo, descrevemos o tamanho do widget e o layout que o representa.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dp"
android:minHeight="72dp"
android:initialLayout="@layout/widget"
>
</appwidget-provider>

Agora é só criarmos a classe que vai carregar o nosso Widget. Ela herda de AppWidgetProvider, que por sua vez herda de BroadcastReceiver. Logo a maioria do processo de atualização do widget é feito via mensagens de broadcast.

public class MeuWidgetProvider
extends AppWidgetProvider {

private static final String ACTION =
"ngvl.android.meuwidget.WIDGET_CONTROL";
private static final String LOG_TAG = "MeuWidget";
public static final String URI_SCHEME = "meuwidget";

private static int count;

@Override
public void onUpdate(Context context,
AppWidgetManager wm, int[] appWidgetIds) {

Log.d(LOG_TAG, "onUpdate(): ");

int id = appWidgetIds[0];
Log.d(LOG_TAG, "appWidgetId: "+ id);

RemoteViews remoteView = new RemoteViews(
context.getPackageName(), R.layout.widget);

remoteView.setTextViewText(
R.id.TextView01, "texto inicial");

atualizaViews(context, id, remoteView);
}

private void atualizaViews(Context context,
int widgetId, RemoteViews remoteView) {

remoteView.setOnClickPendingIntent(
R.id.next,
getPendingIntent(context, "next", widgetId));

remoteView.setOnClickPendingIntent(
R.id.prev,
getPendingIntent(context, "prev", widgetId));

AppWidgetManager.getInstance(context).
updateAppWidget(widgetId, remoteView);
}

private PendingIntent getPendingIntent(
Context context, String command, int appWidgetId){

Intent active = new Intent();
active.setAction(ACTION);
active.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId);

Uri data = Uri.withAppendedPath(
Uri.parse(URI_SCHEME+"://widget/id/#"+command),
String.valueOf(appWidgetId));

active.setData(data);
return PendingIntent.getBroadcast(
context, 0, active, PendingIntent.FLAG_ONE_SHOT);
}

@Override
public void onReceive(Context context, Intent intent) {

final String action = intent.getAction();
Log.d(LOG_TAG, "OnReceive:Action: " + action);
// Bug conhecido do Android 1.5
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.
equals(action)) {

final int appWidgetId =
intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);

if (appWidgetId !=
AppWidgetManager.INVALID_APPWIDGET_ID) {

this.onDeleted(context, new int[]{appWidgetId});
}

} else if (ACTION.equals(action)) {
final int appWidgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);

if (appWidgetId !=
AppWidgetManager.INVALID_APPWIDGET_ID) {

this.trataAcao(context, appWidgetId,
intent.getData());
}

} else {
super.onReceive(context, intent);
}
}

public void trataAcao(Context context,
int appWidgetId, Uri data) {

Log.d(LOG_TAG, "onHandleAction: "+ appWidgetId);
RemoteViews remoteView = new RemoteViews(
context.getPackageName(), R.layout.widget);

Log.d(LOG_TAG, "data = "+ data);
String controlType = data.getFragment();

if (controlType.equalsIgnoreCase("prev")) {
remoteView.setTextViewText(
R.id.TextView01, "Count: "+ --count);

} else if (controlType.equalsIgnoreCase("next")) {
remoteView.setTextViewText(
R.id.TextView01, "Count: "+ ++count);
}
atualizaViews(context, appWidgetId, remoteView);
}
}


O método onUpdate() é chamado quando adicionamos o widget na HomeScreen. Nele estamos obtendo as view remotas. A classe RemoteViews representa views que estão em outro processo, que nesse caso é a aplicação de Home onde ficam os widgets. Então nós obtemos apenas as nossas (baseado no pacote da aplicação e no arquivo de layout). Uma vez com as referencias das views, podemos definir o texto inicial e setar o evento de clique. Esse evento está sendo definido no método atualizaViews().
Os eventos de clique disparam PendingItents para o próprio widget (lembre-se que ele é uma subclasse de BroadcastReceiver). Esses eventos são tratados (como todo receiver) pelo método onReceive() que chama o método trataAcao() para atualizar o texto da view e "reassignar" os eventos aos botões.

Agora declare o widget no AndroidManifest.xml.


<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="ngvl.android.meuwidget"
android:versionCode="1"
android:versionName="1.0">

<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="true">

<receiver
android:name=".MeuWidgetProvider">
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

<intent-filter>
<action
android:name="ngvl.android.meuwidget.WIDGET_CONTROL" />
<data android:scheme="meuwidget" />
</intent-filter>

<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/meuwidget_info" />
</receiver>
</application>
</manifest>


Notem que a declaração do Widget é com a tag receiver e no meta-data passamos o arquivo XML com suas configurações. Notem que temos dois intent-filters para o widget: um com a ação android.appwidget.action.APPWIDGET_UPDATE que deve sempre ser declarado, e outro com a ação qeu disparamos no clique do botão.

A estrutura final do projeto ficará dessa forma:



Execute a aplicação, dê um clique longo na HomeScreen e adicione o widget à tela. O resultado é exibido na figura abaixo :)



Estou sem tempo de explicar bem o exemplo, mas tentem colocar pra rodar e qualquer dúvida, postem comentários aqui :)

4br4ç05,
nglauber

Nenhum comentário: