1.创建AppWidget布局,包含两个TextView用来显示内容:
-
<?xmlversion="1.0"encoding="utf-8"?>
-
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
-
android:layout_width="fill_parent"android:layout_height="fill_parent"
-
android:orientation="vertical">
-
<TextViewandroid:layout_height="wrap_content"
-
android:layout_width="fill_parent"android:id="@+id/tv1"
-
android:textColor="#FF0000"android:textSize="24sp"android:textStyle="bold"
-
android:text="-1"></TextView>
-
<TextViewandroid:layout_height="wrap_content"android:id="@+id/tv2"
-
android:textSize="24sp"android:textStyle="bold"android:textColor="#00FF00"
-
android:layout_width="fill_parent"android:text="-2"></TextView>
-
</LinearLayout>
2.在res下创建xml目录,再在xml目录里面创建AppWidget信息xml文件:
2.1新建xml文件时,type选择AppWidget Provider。2.2填充属性:
宽高的计算公式为:占用屏幕格数*74-2
Update period millis:设置为0,手动刷新。根据实验,设置不为0时,至少在2.2上系统根本不按照设置的值刷新,还是自己控制刷新时机好了。
Initial layout:就添控件要使用的布局。
Configure暂时不用,留空。
3.创建AppWidgetDemo类:
重载AppWidgetProvider中的所有函数,每个函数里面增加输出语句,以查看调用顺序。
-
publicclassAppWidgetDemoextendsAppWidgetProvider{
-
-
@Override
-
publicvoidonDeleted(Contextcontext,int[]appWidgetIds){
-
-
super.onDeleted(context,appWidgetIds);
-
Log.e("AppWidgetDemo","onDeleted");
-
}
-
-
@Override
-
publicvoidonDisabled(Contextcontext){
-
-
super.onDisabled(context);
-
Log.e("AppWidgetDemo","onDisabled");
-
}
-
-
@Override
-
publicvoidonEnabled(Contextcontext){
-
-
super.onEnabled(context);
-
Log.e("AppWidgetDemo","onEnabled");
-
}
-
-
@Override
-
publicvoidonReceive(Contextcontext,Intentintent){
-
-
super.onReceive(context,intent);
-
Log.e("AppWidgetDemo","onReceive,Action:"+intent.getAction());
-
}
-
-
@Override
-
publicvoidonUpdate(Contextcontext,AppWidgetManagerappWidgetManager,int[]appWidgetIds){
-
-
super.onUpdate(context,appWidgetManager,appWidgetIds);
-
Log.e("AppWidgetDemo","onUpdate,Count:"+appWidgetIds.length);
-
}
-
-
}
4.在AndroidManifest.xml文件中声明此Widget:
添加一个Receiver,其name为AppWidgetDemo类的类名。
-
<receiverandroid:name="AppWidgetDemo"></receiver>
为此Receiver添加Intent filter,接收系统发出android.appwidget.action.APPWIDGET_UPDATE的Intent。
-
<receiverandroid:name="AppWidgetDemo">
-
<intent-filter>
-
<actionandroid:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
-
</intent-filter>
-
</receiver>
此外还要为此receiver添加meta-data信息,以告知系统相关的AppWidgetProvider信息:
-
<meta-dataandroid:name="android.appwidget.provider"
-
android:resource="@xml/widget_info_demo"></meta-data>
meta-data的name是约定好的android.appwidget.provider,resource则是第2步创建的AppWidget信息xml文件。
5.至此AppWidget已经可用了,安装到模拟器上看下运行流程。
5.1添加一个Widget到桌面上:
onEnabled被呼叫:按照说明,当桌面上出现第一个此Widget的实例时,此函数被呼叫。
onReceive被呼叫:onReceive,Action:android.appwidget.action.APPWIDGET_ENABLED
onUpdate被呼叫:onUpdate,Count:1,并且待更新的AppWidget数量为1
onReceive被呼叫:onReceive,Action:android.appwidget.action.APPWIDGET_UPDATE
5.2再添加一个Widget到桌面上:
onUpdate被呼叫:onUpdate,Count:1,并且待更新的AppWidget数量仍然为1,而不是2。
onReceive被呼叫:onReceive,Action:android.appwidget.action.APPWIDGET_UPDATE
5.3从桌面上移除一个Widget:
onDeleted:每个实例被移除时都会被呼叫
onReceive,Action:android.appwidget.action.APPWIDGET_DELETED
5.4再从桌面上移除一个Widget:
onDeleted:仍然执行
onReceive,Action:android.appwidget.action.APPWIDGET_DELETED
onDisabled:因为是最后一个活动的实例被移除了,所以被呼叫。
onReceive,Action:android.appwidget.action.APPWIDGET_DISABLED
6.刷新AppWidget
6.1在onUpdate()中刷新:
onUpdate在AppWidget放到桌面时会被调用,在Update period millis达到时可能会被调用。
-
RemoteViewsappWidgetView=newRemoteViews(context.getPackageName(),R.layout.widget_layout_demo);
-
appWidgetView.setTextViewText(R.id.tv1,String.valueOf(mCount));
-
appWidgetView.setTextViewText(R.id.tv2,String.valueOf(mCount));
-
appWidgetManager.updateAppWidget(appWidgetIds,appWidgetView);
先获取一个RemoteViews,也就是AppWidget的布局所对应的View;
使用指定的Id更新要更新的控件;
更新整个RemoteViews,此时就可以更新AppWidget内容了。
但是这样的代码还有一个问题,当向桌面依次添加多个控件时会出现下面这样的效果:
即更新时没有同时更新所有的AppWidget,这是因为onUpdate中传进来的数组中只包含了1个id,如果想同时更新多个,那么可以把更新语句更换为:
-
appWidgetManager.updateAppWidget(newComponentName(context,AppWidgetDemo.class),appWidgetView);
通过组件名可以把所有的名字符合的AppWidget都更新。
6.2在onReceive()中更新
6.2.1自定义Action通知刷新:
在AndroidManifest.xml中定义的receiver的intent-filter增加一个自定义的Action:
com.demo.appwidget.refresh
-
<receiverandroid:name="AppWidgetDemo">
-
<intent-filter>
-
<actionandroid:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
-
<actionandroid:name="com.demo.appwidget.refresh"></action>
-
</intent-filter>
-
<meta-dataandroid:name="android.appwidget.provider"
-
android:resource="@xml/widget_info_demo"></meta-data>
-
lt;/receiver>
包含此Action的Intent可以在后台服务或者Activity中发出,然后会被此Receiver接收,进而触发onReceive。
本例中采用按钮按下时广播intent:
-
btnSend.setOnClickListener(newOnClickListener(){
-
publicvoidonClick(Viewv){
-
Intentintent=newIntent();
-
intent.setAction("com.demo.appwidget.refresh");
-
intent.putExtra("value",teInput.getText().toString());
-
SendMsgActivity.this.sendBroadcast(intent);
-
}
-
});
在接收端:
-
if(intent.getAction().equals("com.demo.appwidget.refresh")){
-
Stringvalue=intent.getStringExtra("value");
-
RemoteViewsappWidgetView=newRemoteViews(context.getPackageName(),R.layout.widget_layout_demo);
-
appWidgetView.setTextViewText(R.id.tv1,value);
-
appWidgetView.setTextViewText(R.id.tv2,value);
-
AppWidgetManager.getInstance(context).updateAppWidget(newComponentName(context,AppWidgetDemo.class),appWidgetView);
-
}
判断是否是感兴趣的Action,是的话就取值,然后更新。
6.2.2接收系统消息刷新:
比如intent-filter中再增加一个action:"android.provider.Telephony.SMS_RECEIVED",在AndroidMenifest.xml中任意位置添加<uses-permission android:name="android.permission.RECEIVE_SMS" />设置好权限,当系统有短消息收到时就能触发onReceive了。
但是有些Action比较特殊,比如android.intent.action.TIME_TICK,根据android.content.intent文档中的描述:You can not receive this through components declared in manifests, only by exlicitly registering for it with Context.registerReceiver().
这个Action在AndroidManifest.xml中声明了也没用,必须要自己开个服务注册receiver才能收到,然后再转发一次给自己。
6.3直接在外部Activity或者Service中刷新:
-
btnRefresh.setOnClickListener(newOnClickListener(){
-
publicvoidonClick(Viewv){
-
Stringvalue=teInput.getText().toString();
-
RemoteViewsappWidgetView=newRemoteViews(SendMsgActivity.this.getPackageName(),R.layout.widget_layout_demo);
-
appWidgetView.setTextViewText(R.id.tv1,value);
-
appWidgetView.setTextViewText(R.id.tv2,value);
-
AppWidgetManager.getInstance(SendMsgActivity.this)
-
.updateAppWidget(newComponentName(SendMsgActivity.this,AppWidgetDemo.class),appWidgetView);
-
}
-
});
此段代码可直接刷新AppWidget的内容,不会触发其onUpdate()。
7.响应点击事件
因为onUpdate是每个AppWidget被放置到桌面上时都会被呼叫的函数,所以在此函数中完成事件的关联:
-
Intentintent=newIntent(context,SendMsgActivity.class);
-
PendingIntentpendingIntent=PendingIntent.getActivity(context,0,intent,0);
-
appWidgetView.setOnClickPendingIntent(R.id.tv1,pendingIntent);
另外要注意此段代码必须要在appWidgetManager.updateAppWidget()之前,否则是不会生效的。
运行后可以点击AppWidget的第一个控件,就能呼叫指定的Activity了。
8.Config Activity
这个可直接参考SDK文档中的Dev Guide-->App Widgets了。
相关推荐
7.5 Android应用的灵魂——Intent和Activity介绍与实例 7.5.1 实例操作演示 7.5.2 实例编程实现 7.6 用好列表,做好程序——列表(ListView)介绍与实例 7.6.1 实例程序演示 7.6.2 实例编程实现 7.7 友好地互动交流...
import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android....
4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...
4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...
4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...
4.30 实时配置桌面上的AppWidget UI Layout 4.31 识别输入装置ID与InputDevice装置 4.32 选取文字的聪明文字联想 4.33 设计与Launcher相同的三页滑动Layout 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的...