2013/5/20
61_Widgets
------------------
Java技术qq交流群:JavaDream:2515720721.Widgets就是指窗口小部件,就是浮动在窗口中的部件.
2.当添加小部件的时候,是发出一个更新广播,对于同一个,窗口小部件可以
在桌面上放置多个.
---------------------------------------
3.如果只要更新特定某个Widgets,需要给要更新的Widgets指定ip
如果更新某一种Widgets,需要指定Widgets的名称
----------------------------------------------
4.这里为了演示Widgets应用,借助帮助文档来完成,一个显示时间的窗口小部件.
帮组文档地址:
G:\android\android-sdk-windows\docs\guide\topics\appwidgets\index.html
------------------------------------------------------------
a.首先在清单文件中描述:
Declaring an App Widget in the Manifest
First, declare the AppWidgetProvider class in your application's AndroidManifest.xml file. For example:
<receiver android:name="ExampleAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>
通过这里可以看到,这里:Widgets就是广播接收者,就是通过广播接收者实现的.
--------------------------------------------------------------------------
b.adding the AppWidgetProviderInfo Metadata添加widget的元数据.
The AppWidgetProviderInfo defines the essential qualities of an App Widget,
such as its minimum layout dimensions, its initial layout resource,
how often to update the App Widget, and (optionally) a configuration Activity to
launch at create-time. Define the AppWidgetProviderInfo object in an XML resource
using a single <appwidget-provider> element and save it in the project's res/xml/ folder.
For example:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/preview"//这个用来指定预览的图片
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
//这里用来指定一个activity,这个activity用来对widget进行参数配置的
android:resizeMode="horizontal|vertical">//这里是重树模式,水平的还是垂直的
</appwidget-provider>
---------------------------------------------------------
5./widgets/res/layout/time_appwidget.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/rectangle"
//指定widget的背景图片
>
<!-- //这个text这个是widget的界面
-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/textView"
android:textColor="#FFFFFF"
android:textSize="14dp"
android:text="我是Widgets"
/>
</LinearLayout>
----------------------------------------------------------------------------
6./widgets/res/drawable/rectangle.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">//方形
<corners android:radius="9dp"/>//圆角的半径
<gradient//渐变色
android:angle="270"
android:endColor="#5EADF4"//红色过度到黄色,结束的颜色
android:startColor="#B3F0FF" />//开始的颜色
<padding
android:left="5dp"//内间距,内容离里边界的距离
android:top="5dp"
android:right="5dp"
android:bottom="5dp" />
<stroke//边界的颜色.
android:width="1dp"
android:color="#0000CC"
android:dashWidth="10dp"
android:dashGap="6dp" />
</shape>
<!-- 建立一个方形 -->
-----------------------------------------------------------
下面是一个显示时间的一个widgets的所有源码:
1./widgets/src/com/credream/widget/TimerService.java
package com.credream.widget;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.IBinder;
import android.widget.RemoteViews;
public class TimerService extends Service {
//服务要继承Service,要在清单文件中进行配置
/* <service android:name=".TimerService"></service>
这里的写法是这样的.
</application>
*/
//1.这里用一个定时器,来定时刷新widget.
private Timer timer;
//1.当服务的实例第一次被创建的时候会调用这个方法.
@Override
public void onCreate() {
super.onCreate();
timer = new Timer();
// timer.schedule(new TimeUpdateTask(), 0, 1000);
/*参数的意思是:第一个是定时器要定时执行什么任务,第二个是,要延时多长时间执行,
第三个是每隔多长时间执行一次.这里1000是代表1秒.
*/
timer.schedule(new TimeUpdateTask(), 0, 1000);
}
// 3.定时器要执行的方法。
private final class TimeUpdateTask extends TimerTask{
// 4.重写run方法
public void run() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = dateFormat.format(new Date());
RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.time_appwidget);
remoteView.setTextViewText(R.id.textView, time);
// 7.这里控制,当用户点击这个widget的时候,要激活的哪个activity.哪个应用.
// PendingIntent.getActivity打开activity对象.
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 100,
// 8.指定要激活的activity.Intent.ACTION_CALL指定电话拨号器.0标志.
new Intent(Intent.ACTION_CALL, Uri.parse("tel:133333333")), 0);
remoteView.setOnClickPendingIntent(R.id.textView, pendingIntent);
// 5.取得widget管理器.//AppWidgetManager.getInstance()内部实现是通过service得到了一个系统的服务
// widget是一个远程服务.
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
// 6.new ComponentName(getApplicationContext(), TimeWidgetProvider.class), remoteView)
// 这里是指会更新所有同类型的桌面上的widget的数据,remoteView这是指widget的类型
appWidgetManager.updateAppWidget(
new ComponentName(getApplicationContext(), TimeWidgetProvider.class), remoteView);
}
}
// 7.当服务被摧毁的时候,要取消定时器.
// 8.当把widget第一次添加到桌面上的时候,就启动服务,当最后一个同类型的widget被停掉了的话,就停止服务.
@Override
public void onDestroy() {
timer.cancel();
timer = null;
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
---------------------------------------------------------
2./widgets/src/com/credream/widget/TimeWidgetProvider.java
package com.credream.widget;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
public class TimeWidgetProvider extends AppWidgetProvider {
// 5.当widget被删除的时候,不需要再清单文件中对,广播进行订阅,广播接收者也可以接收到这个广播信息
// 当widget被删除的时候会自动的发出一个删除广播
//1.当添加完一个widget后,会发送一个广播,然后广播会附带一个参数,这个参数
//会被系统截获,系统根据参数:这里得到的参数是固定的,是系统规定的一个参数:
//android:name="android.appwidget.action.APPWIDGET_UPDATE
//系统得到这个名字后,会调用onUpdate方法
// 3.当用户在桌面上删除widget的时候,会自动的调用这个方法.
// 这时候onDeleted不用写内容,因为,当如果桌面上有很多同类型的widget的话,
// 如果在这个方法里停掉服务,那么同类型的其他widget也没办法运行了.
@Override
public void onDeleted(Context context, int[] appWidgetIds) {//当用户从桌面上删除widgets的时候就会调用此方法
}
// 5.//第一次往桌面添加Widgets的时候才会被调用,往后再往桌面添加同类型Widgets的时候是不会被调用的
@Override
public void onEnabled(Context context) {
//
context.startService(new Intent(context, TimerService.class));
}
//4. //是在最后一个同类型Widgets实例被删除的时候调用,当在最后一个同类型Widgets被删除的时候会发出一个广播
//然后就调用这个方法.这里就额可以停掉服务.
@Override
public void onDisabled(Context context) {
context.stopService(new Intent(context, TimerService.class));
}
//4.在真时的机器上运行后,发现时间的秒没有在动,这是因为当,用户添加完这个widget后,会自动的
//调用onUpdate方法进行,更新widget,而更新后就完了,就不会在调用onUpdate方法了
//所以这里,时间只会显示但并不会走动,这时候,需要不断的更新,不断的调用onUpdate方法,
//注意,这里也不可以使用子进程来实现,因为广播接收者的生命周期很短,当onrecever这个方法执行
//完后,那么这个广播接收者实例就被摧毁了,被摧毁后变成空进程,空进程,很容易被系统回收,所以这里
//不可以使用子进程的形式进行循环调用onUpdate方法.,那么这里可以使用服务来完成.
//2.Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds
//context上下文
//AppWidgetManager这个是widget管理器,桌面上所有的widget就是通过AppWidgetManager管理
//appWidgetIds :widget的id,这个数组就是传入的同类型的,每一个widget的id.
// @Override
/*public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = dateFormat.format(new Date());*/
//调用这个方法,来更新显示内容.
//1.注意这里widget管理器,是android系统提供的服务,是一个远程服务,因为这里:
//3. void android.appwidget.AppWidgetManager.updateAppWidget(int appWidgetId, RemoteViews views)
//可以看到这里的第二个参数是remoteview,这里取得remoteview是因为,widget管理器是系统的一个
//内置服务,这个内置服务,实现了进程通信,那么一个对象要传给远程服务
//那么这里第二个参数,所以叫RemoteView,所以这里第二个参数实际上是一个远程对象.
//第一个参数是包名,第二个参数是这个widget的界面文件.
/* RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.time_appwidget);
remoteView.setTextViewText(R.id.textView, time);
//4.这里调用updateAppWidget来更新指定widget的时间.
appWidgetManager.updateAppWidget(appWidgetIds[0], remoteView);*/
//2.appWidgetIds[0]就是当前在桌面上的widget,这个数组就是对应的widget
//给定特定的id就可以更新特定的widget实例.
}
//}
----------------------------------------------
3./widgets/res/layout/time_appwidget.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/rectangle"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/textView"
android:textColor="#FF0000"
android:textSize="30dp"
android:paddingTop="15dp"
android:paddingLeft="15dp"
android:text="我是Widgets"
/>
</LinearLayout>
------------------------------------------------------
4./widgets/res/xml/appwidget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/time_appwidget"
/>
<!-- 1.android:minWidth="294dp"
android:minHeight="72dp"
用来指定widgets的宽和高度
2.android:updatePeriodMillis="0"
用来指定每隔多长时间发送一次更新广播,现在已经不建议使用了
3.android:initialLayout="@layout/time_appwidget"
用来指定widgs的一个初始化界面
-->
------------------------------------------------------
5./widgets/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.credream.widget"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<!-- 1. <receiver android:name=".TimeWidgetProvider">
TimeWidgetProvider,这个自己建的类,本身就是一个广播接收者.
这里就是配置,广播接收者的地方,也就是那个窗口小部件
2. <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
这里指定的是当用户把小部件拖到窗口中的时候会自动的发送一个更新广播,这里指定的就是发送的那个更新广播的名称
3. 这里可以用一个 <intent-filter>意图过滤器来得到这个更新广播
4. <meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info" />
在这里可以使用<meta-data>,来给这个广播接收者,传递参数,这里:
android.appwidget.provider,是android系统提供的固定的<meta-data>名称
@xml/appwidget_info这个文件用于指定widget窗口小部件,要使用的显示文件,在
这个文件中,规定了,这个widget的显示效果:比如这个widget的高度,宽度,初始化界面等.
-->
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<receiver android:name=".TimeWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info" />
</receiver>
<!-- 这里设置服务, -->
<service android:name=".TimerService"></service>
</application>
<uses-permission android:name="android.permission.CALL_PHONE"/>
</manifest>
---------------------------------------------------------------------------