作者:徐建祥(netpirate@gmail.com)
时间: 2010/12/15
来自: http://www.anymobile.org
Widget杂谈:最早Widget是指在PC的桌面上的小窗口程序;Web上的先行者似乎是Yahoo!;当然,OPhone也搞了一套Widget,HTML+CSS的东东。
我们这里谈的所谓Widget,就是窗口小部件,Android SDK从1.5版本开始支持AppWidget framework,返个框架允许开发者开发Widgets,这些Widgets可以被用户通过长按桌面进行添加,与应用程序进行数据交互。
需求:
在桌面上开发一个Widget,可以实时显示IM软件的状态更新变化;可以通过左右按钮,查看上次或下调更新内容。
(参考效果图)
设计思路:
(参考设计序列图)
代码:
Java:
/src/org.anymobile.demo.Globals //Intent.action 声明
/src/org.anymobile.demo.service.UpdateService extends Service //同步、更新Widget布局数据的Service
/src/org.anymobile.demo.widget.UpdateAppWidgetProvider extends AppWidgetProvider //Widget,接收器
XML:
/res/layout/update_appwidget.xml //布局设计
/res/values/strings.xml //常量声明
/res/xml/update_appwidget_info.xml //app widget定义
AndroidManifest.xml
#AndroidManifest.xml
<receiver android:name=".widget.UpdateAppWidgetProvider"
android:label="@string/app_widget_label" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/update_appwidget_info" />
</receiver>
<service android:name=".service.UpdateService" android:label="@string/app_name">
<intent-filter>
<action android:name="org.anymobile.demo.service.IMM_UPDATE_SERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
#strings.xml
<string name="app_widget_label">AnymobileDemo Widget</string>
<string name="app_widget_title">Updates</string>
<string name="app_widget_error_message">No messages, please check to login.</string>
</resources>
#update_appwidget_info.xml
#update_appwidget.xml
<LinearLayout
android:id="@+id/app_widget_body"
android:orientation="horizontal"
android:background="@drawable/widget_body"
android:layout_width="fill_parent"
android:layout_height="100dip">
<LinearLayout
android:id="@+id/app_widget_message"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/widget_message"
android:text="@string/app_widget_error_message"
android:paddingRight="5dip"
android:paddingLeft="5dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/app_widget_bottom"
android:gravity="right"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
</LinearLayout>
#Globals.java
public static final String ACTION_APP_WIDGET_PREV = "org.anymobile.demo.intent.action.APP_WIDGET_PREV";
public static final String ACTION_APP_WIDGET_NEXT = "org.anymobile.demo.intent.action.APP_WIDGET_NEXT";
public static final String ACTION_APP_WIDGET_RELOAD = "org.anymobile.demo.intent.action.APP_WIDGET_RELOAD";
}
#UpdateService.java
private ArrayList<String> mList;
private int mCount;
private int mId;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
Log.d(TAG, "onReceive() " + action);
if (action.equals(Globals.ACTION_APP_WIDGET_RELOAD))
{
doReload();
}
}
};
@Override
public void onCreate()
{
Log.d(TAG, "onCreate()");
super.onCreate();
reloadQueue();
IntentFilter filter = new IntentFilter();
filter.addAction(Globals.ACTION_APP_WIDGET_RELOAD);
registerReceiver(mIntentReceiver, filter);
}
@Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
String action = intent.getAction();
Log.d(TAG, "onStart() " + action);
if (action.equals(Globals.ACTION_APP_WIDGET_PREV))
{
doPrev();
}
else if (action.equals(Globals.ACTION_APP_WIDGET_NEXT))
{
doNext();
}
else// if (action.equals(Globals.ACTION_APP_WIDGET_SERVICE))
{
notifyWidget();
}
}
private void notifyWidget()
{
Log.d(TAG, "notifyWidget()");
ComponentName widget = new ComponentName(this, UpdateAppWidgetProvider.class);
RemoteViews updateViews = buildUpdate(this);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(widget, updateViews);
}
@Override
public void onDestroy()
{
Log.d(TAG, "onDestroy()");
unregisterReceiver(mIntentReceiver);
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent)
{
Log.d(TAG, "onBind()");
return null;
}
private RemoteViews buildUpdate(Context context)
{
RemoteViews updateViews =
new RemoteViews(context.getPackageName(), R.layout.update_appwidget);
String item = null;
if (mCount > 0)
{
item = mList.get(mId);
if (item != null)
{
updateViews.setViewVisibility(R.id.app_widget_content, View.GONE);
updateViews.setViewVisibility(R.id.app_widget_message, View.VISIBLE);
// updateViews.setViewVisibility(R.id.app_widget_content, View.VISIBLE);
// updateViews.setViewVisibility(R.id.app_widget_message, View.GONE);
//
// updateViews.setImageViewResource(R.id.update_appwidget_icon, item.getTypeIconId());
// updateViews.setTextViewText(R.id.update_appwidget_name, item.getNickName());
// updateViews.setTextViewText(R.id.update_appwidget_time, item.getModifyTime());
// updateViews.setTextViewText(R.id.update_appwidget_content, item.getMessage());
updateViews.setTextViewText(R.id.widget_message, item);
}
}
if (item == null)
{
updateViews.setViewVisibility(R.id.app_widget_content, View.GONE);
updateViews.setViewVisibility(R.id.app_widget_message, View.VISIBLE);
updateViews.setTextViewText(R.id.widget_message,
context.getText(R.string.app_widget_error_message));
}
Log.d(TAG, "buildUpdate: layoutId = " + updateViews.getLayoutId() +
"; count = " + mCount + "; id = " + mId);
return updateViews;
}
private void doReload()
{
Log.d(TAG, "doReload()");
reloadQueue();
notifyWidget();
}
private void reloadQueue()
{
mList = new ArrayList<String>();
String[] arr = {"aa", "bb", "cc", "dd"};
for (int i = 0; i < arr.length; i++)
{
mList.add(arr[i]);
}
if (mList != null)
{
mCount = mList.size();
}
else
{
mCount = 0;
}
mId = 0;
//TODO check login and poll updates from buddie list
}
private void doPrev()
{
Log.d(TAG, "doPrev()");
mId -= 1;
if (mId < 0)
{
mId = mCount - 1;
}
notifyWidget();
}
private void doNext()
{
Log.d(TAG, "doNext()");
mId += 1;
if (mId > mCount - 1)
{
mId = 0;
}
notifyWidget();
}
}
#UpdateAppWidgetProvider.java
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import org.anymobile.demo.Globals;
import org.anymobile.demo.R;
import org.anymobile.demo.service.UpdateService;
public class UpdateAppWidgetProvider extends AppWidgetProvider
{
public static final String TAG = "ANYMOBILE-DEMO-UpdateAppWidgetProvider";
public static final String APP_WIDGET_UPDATE = "appwidgetupdate";
public static final ComponentName APPWIDGET_COMPONENT_NAME =
new ComponentName("org.anymobile.demo",
"org.anymobile.demo.widget.UpdateAppWidgetProvider");
@Override
public void onReceive(Context context, Intent intent)
{
Log.d(TAG, "onReceive() " + intent.getAction());
super.onReceive(context, intent);
}
@Override
public void onEnabled(Context context)
{
Log.d(TAG, "onEnabled()");
super.onEnabled(context);
}
@Override
public void onDisabled(Context context)
{
Log.d(TAG, "onDisabled()");
super.onDisabled(context);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.d(TAG, "onUpdate()");
defaultAppWidget(context, appWidgetIds);
context.startService(new Intent(Globals.ACTION_APP_WIDGET_SERVICE));
}
private void defaultAppWidget(Context context, int[] appWidgetIds)
{
final RemoteViews views =
new RemoteViews(context.getPackageName(), R.layout.update_appwidget);
views.setViewVisibility(R.id.app_widget_content, View.GONE);
views.setViewVisibility(R.id.app_widget_message, View.VISIBLE);
// Link actions buttons to intents
linkButtons(context, views);
pushUpdate(context, appWidgetIds, views);
}
private void linkButtons(Context context, RemoteViews views)
{
Intent intent;
PendingIntent pendingIntent;
final ComponentName serviceName = new ComponentName(context, UpdateService.class);
intent = new Intent(Globals.ACTION_APP_WIDGET_PREV);
intent.setComponent(serviceName);
pendingIntent = PendingIntent.getService(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.widget_btn_prev_page, pendingIntent);
intent = new Intent(Globals.ACTION_APP_WIDGET_NEXT);
intent.setComponent(serviceName);
pendingIntent = PendingIntent.getService(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.widget_btn_next_page, pendingIntent);
}
private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views)
{
final AppWidgetManager gm = AppWidgetManager.getInstance(context);
if (appWidgetIds != null)
{
gm.updateAppWidget(appWidgetIds, views);
}
else
{
gm.updateAppWidget(APPWIDGET_COMPONENT_NAME, views);
}
}
void notifyChange(UpdateService service, String what)
{
//
}
}
日志:
#init
12-15 19:23:09.479 D/ANYMOBILE-DEMO--UpdateAppWidgetProvider( 585): onReceive() android.appwidget.action.APPWIDGET_UPDATE
12-15 19:23:09.509 D/ANYMOBILE-DEMO--UpdateAppWidgetProvider( 585): onUpdate()
12-15 19:23:09.549 D/ANYMOBILE-DEMO--UpdateService( 585): onCreate()
12-15 19:23:09.579 D/ANYMOBILE-DEMO--UpdateService( 585): onStart()
#add widget
12-15 19:24:23.780 D/ANYMOBILE-DEMO--UpdateAppWidgetProvider( 585): onReceive() android.appwidget.action.APPWIDGET_UPDATE
12-15 19:24:23.780 D/ANYMOBILE-DEMO--UpdateAppWidgetProvider( 585): onUpdate()
12-15 19:24:23.850 D/ANYMOBILE-DEMO--UpdateService( 585): onStart()
#receive software event, reload and update widget
12-15 19:24:58.150 D/ANYMOBILE-DEMO--UpdateService( 585): onReceive() Activation
12-15 19:24:58.150 D/ANYMOBILE-DEMO--UpdateService( 585): doReload()
12-15 19:24:58.150 D/ANYMOBILE-DEMO--UpdateService( 585): notifyWidget()
12-15 19:24:58.200 D/ANYMOBILE-DEMO--UpdateService( 585): buildUpdate: layoutId = 2130903068; count = 11; id = 0
#click widget button, new start the bind service
12-15 19:25:49.260 D/ANYMOBILE-DEMO--UpdateService( 585): onStart()
12-15 19:24:58.150 D/ANYMOBILE-DEMO--UpdateService( 585): notifyWidget()
12-15 19:24:58.200 D/ANYMOBILE-DEMO--UpdateService( 585): buildUpdate: layoutId = 2130903068; count = 11; id = 0
OVER!
参考:
com.android.music/.MediaAppWidgetProvider
com.android.music/.MediaPlaybackService