这几天公司要求开发一个手机应用的widget,刚开始只是懂得不多这方面的知识,但是通过这几天的研究现在把这几天的研究成果做个终结,顺便分享给大家。
每个Widget其实就是一个BroadcastReceiver,它们用XML metadata来描述Widget的细节。AppWidget framework通过Broadcast intents和Widget通信,Widget的更新使用RemoteView来发送的。RemoteViews被包装成一个layout和特定内容来显示到桌面上。下面我来简单的介绍下Widget的开发:
1.定义一个类命名为ExampleAppWidgetProvider 继承类AppWidgetProvider,通过这个类来处理Widget中的一些事件响应等的一些操作,代码如下
/**
* 为手机添加桌面控件,当点击桌面控件时则进入主程序
*
* AppWidgetProvider:继承自BroadcastRecevier,在AppWidget应用update、enable、
* disable和delete时接收通知。 其中,onUpdate、onReceive是最常用到的方法,它们接收更新通知
*/
public class ExampleAppWidgetProvider extends AppWidgetProvider {
/**
* 用来间隔的更新App
* Widget,间隔时间用AppWidgetProviderInfo里的updatePeriodMillis属性定义(单位为毫秒)。
* 注意:SDK1.5之后此android:updatePeriodMillis就失效了,要自己创建service更新。 这个方法也会在用户添加App
* Widget时被调用,因此它应该执行基础的设置,比如为视图定义事件处理器并启动一个临时的服务Service,如果需要的话。
* 但是,如果你已经声明了一个配置活动,这个方法在用户添加App Widget时将不会被调用,
* 而只在后续更新时被调用。配置活动应该在配置完成时负责执行第一次更新。
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
System.out.println("onUpdate");
// 点击桌面组件时进入主程序入口
Intent intent = new Intent(context, ABMainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// RemoteViews类描述了一个View对象能够显示在其他进程中,可以融合layout资源文件实现布局。
// 虽然该类在android.widget.RemoteViews而不是appWidget下面,但在Android
// Widgets开发中会经常用到它,
// 主要是可以跨进程调用(appWidget由一个服务宿主来统一运行的)。
RemoteViews myRemoteViews = new RemoteViews(context.getPackageName(), R.layout.my_layout);
// myRemoteViews.setImageViewResource(R.id.imageView,
// R.drawable.png1);//设置布局控件的属性(要特别注意)
myRemoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent);
//Button button = (Button)View.findViewByID(R.id.btn);
ComponentName myComponentName = new ComponentName(context, ExampleAppWidgetProvider.class);
// 负责管理AppWidget,向AppwidgetProvider发送通知。提供了更新AppWidget状态,获取已经安装的Appwidget提供信息和其他的相关状态
AppWidgetManager myAppWidgetManager = AppWidgetManager.getInstance(context);
myAppWidgetManager.updateAppWidget(myComponentName, myRemoteViews);
}
/**
* 当App Widget从宿主中删除时被调用。
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
System.out.println("onDeleted");
super.onDeleted(context, appWidgetIds);
}
/**
* 当一个App Widget实例第一次创建时被调用。 比如,如果用户添加两个App Widget实例,只在第一次被调用。
* 如果你需要打开一个新的数据库或者执行其他对于所有的App Widget实例只需要发生一次的设置, 那么这里是完成这个工作的好地方。
*/
@Override
public void onEnabled(Context context) {
System.out.println("onEnabled");
super.onEnabled(context);
}
/**
* 当你的App Widget的最后一个实例被从宿主中删除时被调用。你应该在onEnabled(Context)中做一些清理工作,
* 比如删除一个临时的数据库
*/
@Override
public void onDisabled(Context context) {
System.out.println("onDisabled");
super.onDisabled(context);
}
/**
* 接收到每个广播时都会被调用,而且在上面的回调函数之前。 你通常不需要实现这个方法,因为缺省的AppWidgetProvider实现过滤所有App
* Widget广播并恰当的调用上述方法。 注意: 在Android 1.5中,有一个已知问题,onDeleted()方法在调用时不被调用。
* 为了规避这个问题,你可以像Group post中描述的那样实现onReceive()来接收这个onDeleted()回调。
*/
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("onReceive");
super.onReceive(context, intent);
}
}
2.定义桌面上显示的Widget布局文件,新建xml文件my_layout代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="150dip"
android:layout_height="72dip"
android:orientation="vertical"
android:background="#ffffff" >
<RelativeLayout
android:id="@+id/search_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#AA3fbbff"
>
<ImageView
android:id="@+id/imageicon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dip"
android:src="@drawable/ic_launcher" />
<ImageView
android:id="@+id/editText1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/search"
android:layout_toRightOf="@+id/imageicon"
android:focusable="false"
android:padding="10dip" />
<Button
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dip"
android:background="@drawable/ic_launcher"
android:padding="10dip"
android:text="搜索"
android:textColor="#ffffff"
android:textSize="16dp" />
</RelativeLayout>
<ImageView
android:id="@+id/imageView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center" />
<Button
android:id="@+id/btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/ic_launcher"
android:gravity="center" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/my_layout"
android:minWidth="150dip"
android:minHeight="72dip"
android:updatePeriodMillis="1000" />
4.在AndroidManifest.xml文件中添加receive广播接收器代码如下:
<receiver
android:name=".ExampleAppWidgetProvider"
android:label="添加桌面控件示例" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget" />
</receiver>
5.总结如下:通过上面这四个步骤Widget已经成功添加了,你可以运行程序到桌面上去添加桌面控件就会看到我们刚刚加的“添加桌面控件例子”,点击它就会看见自己写的的Widget。
通过做AppWidget的一些心得总结:
1.在AppWidget中有些控件是不支持的比如说EditText,滑动控件等一些用户需要交互的控件。
2.如果监听Widget中的控件响应事件的话,必须在清单文件中的receive里面注册每个控件的action,然后在ExampleAppWidgetProvider 类的Intent中添加对应的action name就可以了,然后在onReceive方法中判断对应的事件就可以了。示例代码如下
(1).
<intent-filter>
<action android:name="com.yaodian.activity.widget_left" />
<action android:name="com.yaodian.activity.widget_right" />
<action android:name="com.yaodian.activity.widget_search" />
<action android:name="com.yaodian.activity.widget_voicesearch" />
<action android:name="com.yaodian.activity.widget_image" />
</intent-filter>
(2).
private final static String ONCLICK_LEFT = "com.yaodian.activity.widget_left";//左边按钮的action
private final static String ONCLICK_RIGHT = "com.yaodian.activity.widget_right";//右边按钮的action
private final static String ONCLICK_IMAGE = "com.yaodian.activity.widget_image";//图片的action
Intent intent_left = new Intent(ONCLICK_LEFT);//左边按钮呢的Intent
Intent intent_right = new Intent(ONCLICK_RIGHT);//右边按钮的Intent
Intent intent_image = new Intent(ONCLICK_IMAGE);//图片的Intent
(3).onReceive方法中
if (intent.getAction().equals(ONCLICK_LEFT)) {//左边按钮的事件响应
} else if (intent.getAction().equals(ONCLICK_RIGHT)) {//右边按钮的事件响应
} else if (intent.getAction().equals(ONCLICK_IMAGE)) {//点击图片跳转到资讯详情页
}
3.如果要点击某个控件跳转到对应的Activity里,可以这样写:
Intent intent_search = new Intent(context, XXX.class);
如果要在对应的intent中传递参数到对应的Activity这可以PendingIntent pendingIntent_search = PendingIntent.getActivity(context, 0, intent_search, PendingIntent.FLAG_UPDATE_CURRENT);最后一个参数的介绍:
int FLAG_CANCEL_CURRENT:如果该PendingIntent已经存在,则在生成新的之前取消当前的。
int FLAG_NO_CREATE:如果该PendingIntent不存在,直接返回null而不是创建一个PendingIntent.
int FLAG_ONE_SHOT:该PendingIntent只能用一次,在send()方法执行后,自动取消。
int FLAG_UPDATE_CURRENT:如果该PendingIntent已经存在,则用新传入的Intent更新当前的数据。
8.
9.