下面是Demo的详细步骤:
一、新建一个Android工程命名为:WidgetDemo.
二、准备素材,一个是Widget的图标,一个是Widget的背景。存放目录如下图:
三、修改string.xml文件如下:
[html] view plain copy print?
Hello World, WidetDemo!
DaysToWorldCup
四、建立Widget内容提供者文件,我们在res下建立xml文件夹,并且新建一个widget_provider.xml代码入下:
[html] view plain copy print?
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="50dip"
android:minHeight="50dip"
android:updatePeriodMillis="10000"
android:initialLayout="@layout/main"/>
其中宽度、长度很清楚,还有android:updatePeriodMillis是自动更新的时间间隔,android:initialLayout是Widget的界面描述文件。
还有一个属性Android:configure是可选的,如果你的Widget需要在启动时先启动一个Activity,则需要设定该项为你的Activity。
五、修改main.xml布局,代码如下:
[html] view plain copy print?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/wordcup"
>
<TextView
android:id="@+id/wordcup"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textSize="12px"
android:textColor="#ff0000"
/>
六、修改WidgetDemo.java代码如下:
[java] view plain copy print?
package com.android.tutor;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.widget.RemoteViews;
public class WidetDemo extends AppWidgetProvider {
/** Called when the activity is first created. */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new MyTime(context,appWidgetManager), 1, 60000);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
private class MyTime extends TimerTask{
RemoteViews remoteViews;
AppWidgetManager appWidgetManager;
ComponentName thisWidget;
public MyTime(Context context,AppWidgetManager appWidgetManager){
this.appWidgetManager = appWidgetManager;
remoteViews = new RemoteViews(context.getPackageName(),R.layout.main);
thisWidget = new ComponentName(context,WidetDemo.class);
}
public void run() {
Date date = new Date();
Calendar calendar = new GregorianCalendar(2010,06,11);
long days = (((calendar.getTimeInMillis()-date.getTime())/1000))/86400;
remoteViews.setTextViewText(R.id.wordcup, "距离南非世界杯还有" + days+"天");
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
}
}
}
七、修改配置文件AndroidManifest.xml,代码如下:
[html] view plain copy print?
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tutor"
android:versionCode="1"
android:versionName="1.0">
<receiver android:name=".WidetDemo"
android:label="@string/app_name">
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_provider"/>
其中
[html] view plain copy print?
name指定该Widget的接收者是WidetDemo,即你建立的AppWidgetProvider子类,label指定该Widget的标签,还可以用属性icon指定图标
[html] view plain copy print?
是采用android文档中提供的,用于接收更新的intent意图
[html] view plain copy print?
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_provider"/>
resource指定该Widget的描述信息,该描述中定义了Widget的相关信息,如该Widget的宽度、长度、自动更新的间隔时间等信息,也就是前面四所定义的内容
(1)创建一个类,让其继承类 AppWidgetProvider,在 AppWidgetProvider 类 中有许多的方法,例如 onDelete(Context, int[]),onEnable(Context)等等, 一般情况下我们纸需要重写 onUpdate(Context, AppWidgetManager, int[])这 个方法就可以了,这个方法是当触发器更新 widget 时候执行的操作。 (2)在项目的 AndroidMenifest.xml 文件中添加一个 receiver 标签,让其指向 前面创建的 AppWidgetProvider 子类,内容如下: 4. 5. 6. 7. 9. intent-filter 中过滤了 APPWIDGET_UPDATE 事件,这个事件是由系统触发的更 新事件,每个 widget 必须包含这个事件;meta-data 标签描述的是 widget 的配 置文件指向,该文件描述了 widget 的一些基本信息。 (3)编写 widget 的 provider 文件信息,本例中该文件名叫做 widget_setting.xml,开发者可以随便取名,只要在 AndroidMenifest.xml 中写 正确就行。 1. 2.<appwidget-provider 3.="" xmlns:android="http://schemas.android.com/apk/res/android" 4.="" android:minwidth="100dp" 5. android:minHeight="100dp" 6. android:initialLayout="@layout/main" 7. android:updatePeriodMillis="1000" > 8. minWidth 和 minHeight 是 widget 的最小宽度和高度,这个值是一个参考值,系 统会根据实际情况进行改 变,initialLayout 属性指明了 widget 的视图布局文 件,updatePeriodMillis 属性是 widget 每隔多久更新一次的时 间, 单位为毫秒。 (4)接下来就是界面布局,在这个示例中只需要一个 TextView 控件就可以,代 码如下: 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 如果没能解释明白 可以 ,到我群里讨论 look at my n a m e
一、UI部分的编写: 参照Google的文档,首先在建立一个类继承AppWidgetProvider import android.appwidget.AppWidgetProvider;public class MyWidget extends AppWidgetProvider { } 然后在清单文件中申明它,我们必须注意到,AppWidgetProvider实际上是BroadcastReceiver,所以要注册成一个receiver,然后还有一些其他的东西需要注意: android.appwidget.action.APPWIDGET_UPDATE 表明这个receiver能够接受一个APPWIDGET_UPDATE的广播,而且在这里,只能加入这一个action。 android.appwidget.provider 表明数据类型时widget提供者提供的数据,example_appwidget_info表明这个widget的参数配置文件名和位置 那么接下来就需要在res目录下建立一个xml文件夹,并且在其中建立一个example_appwidget_info.xml的配置文件,Google的文档中给出了示例有很多参数,实际上关键的参数只有下面的4个: 其中,minWidth和minHeight代表这个widget控件所占据的最小空间,这个空间一般来讲不需要太大,因为太大的话,一个屏幕可能都没办法放下,Google的官方文档的说法是大于4x4的就可能无法显示。 updatePeriodMillis代表数据更新的时间,这里86400000毫秒实际上是24小时,可能最开始看到这个参数会想我能否将其设的很小,每一秒刷新很多次?,实际上对于updatePeriodMillis这个参数而言,即算你设的再小也没用,Google设定widget控件这个参数控制的最短update时间为30分钟,就算将其设置在30分钟以内也会以30分钟的频率来更新数据。 initialLayout参数代表的是本widget空间的布局文件。 那么下一步就是定义出一个对应的布局文件。我们可以简单的在layout目录下建立一个布局文件example_appwidget.xml 在Google的文档中有指出,并非所有的布局组件都可以在上面的这个布局中生效,有效的组件或布局为: 至此,一个简单的widget控件就写好了,我们可以在模拟器上将其拖到桌面上看一看效果: 二、功能逻辑部分的实现 大部分的Widget小控件都会需要在特定情况下更新上面显示的数据,那么这个是如何实现的呢,我们经过上面的代码不难发现实际上这个widget控件并没有一个Activity,所以说这个控件的显示实际上不是本应用来实现的,它实际上是桌面这个应用来显示的,所以我们也不可能直接去更新它上面的数据。 回过头去看看上面我们写的那个receiver,实际上没有实现任何方法。实际上AppWidgetProvider里面有几个比较重要的方法:onReceive、onUpdate、onDisabled、onEnabled 其中onReceive方法跟大多数广播接收者的onReceive方法一样,但是在这里,onReceive方法的调用并不是我们可以决定的,它依赖于显示该widget控件的Host组件,在这里也就是Android桌面应用,所以我们会发现在不同的手机上,将widget控件拖到桌面上显示的时候onReceive可能调用的次数和先后顺序可能完全不一样,这依赖于Host组件是如何实现的。 所以在这里onReceive方法对于我们刷新widget数据基本没有什么帮助。 而onUpdate方法则是由上面所说的updatePeriodMillis参数来控制的,经过上面的分析,我们都知道了,它的最小周期为30分钟。所以我们一般将这个参数设为0即可。那么在这个方法里,我们往往会在其中放置一些启动更新数据服务的功能,因为如果后台的更新数据的Service被意外停止了,那么每30分钟还会被重新启用,不至于一直启动不了了: @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); // System.out.println("onUpdate"); //每隔一段时间重新启动服务,防止服务中间被终止了之后没法重启 Intent intent = new Intent(context, UpdateWidgetService.class); context.startService(intent); } 下面是比较重要的两个方法了:onDisabled和onEnabled 我们知道,widget小控件是可以拖动多个到桌面上的,而onEnabled方法会在第一个widget控件拖到桌面上的时候调用一次,onDisabled会在最后一个widget控件从桌面被删除时调用一次,那么我们需要做的就是在onEnabled这个方法中启用一个刷新widget数据的服务,在onDisabled方法中使用stopService方法来停止这个服务。 @Override public void onDisabled(Context context) { super.onDisabled(context); System.out.println("onDisabled"); //停止数据刷新服务 Intent intent = new Intent(context, UpdateWidgetService.class); context.stopService(intent); } @Override public void onEnabled(Context context) { super.onEnabled(context); System.out.println("onEnabled"); //开启数据刷新服务 Intent intent = new Intent(context, UpdateWidgetService.class); context.startService(intent); }
参照Google的文档,首先在建立一个类继承 AppWidgetProvider
import android.appwidget.AppWidgetProvider;
public class MyWidget extends AppWidgetProvider {
}
然后在清单文件中申明它,必须注意到,AppWidgetProvider实际上是BroadcastReceiver,所以要注册成一个receiver,然后还有一些其他的东西需要注意:
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
android.appwidget.action.APPWIDGET_UPDATE
表明这个receiver能够接受一个APPWIDGET_UPDATE的广播,而且在这里,只能加入这一个action。
android.appwidget.provider
表明数据类型时widget提供者提供的数据, example_appwidget_info
表明这个widget的参数配置文件名和位置
那么接下来就需要在res目录下建立一个xml文件夹,并且在其中建立一个
example_appwidget_info.xml
的配置文件,Google的文档中给出了示例有很多参数,实际上关键的参数只有下面的4个:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="94dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/example_appwidget">
其中, minWidth 和 minHeight
代表这个widget控件所占据的最小空间,这个空间一般来讲不需要太大,因为太大的话,一个屏幕可能都没办法放下,Google的官方文档的说法是大于4x4的就可能无法显示。
updatePeriodMillis
代表数据更新的时间,这里86400000毫秒实际上是24小时,可能最开始看到这个参数会想我能否将其设的很小,每一秒刷新很多次?,
实际上对于 updatePeriodMillis
这个参数而言,
即算设的再小也没用,Google设定widget控件这个参数控制的最短update时间为30分钟,就算将其设置在30分钟以内也会以30分钟的频率来更新数据。
initialLayout 参数代表的是本widget空间的布局文件。
那么下一步就是定义出一个对应的布局文件。我们可以简单的在layout目录下建立一个布局文件example_appwidget.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="80dp"
android:background="@android:color/white"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="widget控件测试"
android:textColor="@android:color/black"
android:textSize="15sp" />
<Button
android:id="@+id/btn_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="清理内存"
android:textColor="#ff000000" />
本篇打算从以下几个点来介绍AppWidget:
1.如何创建一个简单的AppWidget
2.如何使得AppWidget与客户端程序交互
创建简单的AppWidget
在介绍之前给大家看一下程序运行的最后结果和项目结构图,以便大家有个整体的印象。
运行结果图:
项目结构图:
第一步:
首先在res文件夹下新建一个名字为xml的文件夹,然后在xml目录下创建一个名为appwidget01的xml文件(如上图所示)。这个appwidget01中的内容如下:
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth = "294dp"
android:minHeight = "72dp"
android:updatePeriodMillis = "86400000"
android:initialLayout = "@layout/appwidgetlayout"
>
这个xml是用来描述你所要创建的appWidget的一些描述信息的,比如高度、宽度、刷新间隔、布局文件等等。仅仅这个描述文件还不够,我们看到的appWidget可都是有界面元素的呀,比如说文本,图片,按钮等等,这些东西的定义都需要放到layout文件夹下面。这个文件就是上面代码中写到的那个appwidgetlayout。
第二步:
在layout文件夹下面新建一个appwidgetlayout.xml文件,在这个文件中描述了appWidget的控件和布局等等信息,就和我们平常创建的一个activity的布局文件没什么两样,因为只是简单的演示,所以仅用一个文本和一个按钮。xml的内容如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:id="@+id/txtapp" android:text="test" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:background="#ffffff">
<Button android:id="@+id/btnSend" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Send">
第三步:
既然appWidget中存在按钮等等控件,那么就肯定少不了处理这些控件事件的处理代码啦。这些代码被放在一个继承于AppWidgetProvider的类中,在本例子中我新建了一个AppWidget的类,该类继承于AppWidgetProvider,以后所有的AppWidget上面的控件事件都会在这个类中处理。看一下类的内容:
public class AppWidget extends AppWidgetProvider
{
private final String broadCastString = "com.qlf.appWidgetUpdate";
/**
* 删除一个AppWidget时调用
* */
@Override
public void onDeleted(Context context, int[] appWidgetIds)
{
super.onDeleted(context, appWidgetIds);
}
/**
* 最后一个appWidget被删除时调用
* */
@Override
public void onDisabled(Context context)
{
super.onDisabled(context);
}
/**
* AppWidget的实例第一次被创建时调用
* */
@Override
public void onEnabled(Context context)
{
super.onEnabled(context);
}
/**
* 接受广播事件
* */
@Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
}
/**
* 到达指定的更新时间或者当用户向桌面添加AppWidget时被调用
* */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
}
}
各个方法的作用大家一看上面的注释就明白了。我们暂时不需要实现里面的方法。
第四步:
在AndroidManifest.xml中定义一些创建AppWidget必要的东西,先看代码:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qlf.widget" android:versionCode="1" android:versionName="1.0">
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget01" />
可以看到我们在配置文件里面定义了一个receiver,他的名字是上面创建处理控件代码的那个类,下面那个intent-filter中的action是系统自带的用于更新所有appwidget的广播动作。然后meta-data标签是一个描述我们创建appwidget的元数据,那个android:name="android.appwidget.provider"是固定的,android:resource="@xml/appwidget01"指定创建的appWidget的描述信息的位置。这样程序就知道到哪里去初始化这些appWidget啦。
经过上面四个步骤,我想您已经能够成功在桌面上添加小工具了,效果就是我们最前面发出的样子。
你想编写什么样的widget?各种widget如下
1. Yahoo!Widgets
Yahoo! Widgets桌面应用程序平台由Widget工具和Widget引擎两部分组成。在Yahoo! Widgets工具平台上运行的迷你应用程序就统称为Widget工具
Widget引擎提供了Widget应用程序的运行平台,在Windows和Mac操作系统环境下都可以使用,安装引擎后就能在此平台上运行各式各样的Widget工具了。Yahoo! Widgets引擎开放了基于XML和JavaScript的Widget开发接口,任何人都可以开发新的有趣的Widget,包括各种小工具、小游戏等。当多个Widget同时运行时,每一个Widget又作为独立的程序运行,这可以确保一个Widget出现异常时不会影响到其他的Widget。
2. Apple Dashboard Widget
Dashboard技术 通过Dashboard可以开发一种新的迷你程序 —— Dashboard Widget。
Dashboard是基于WebKit开发的,它为Apple Widget程序提供了一个运行环境。Widget应用可以采用HTML、JavaScript以及CSS等Web技术开发。另外,由于Dashboard是Mac OS系统内置的技术,因此Dashboard Widget还可以调用Mac OS系统本身提供的部分功能。
Dashboard Widget以“.wdgt”为后缀,根据运行所需要的资源,可以分成3种类别。
(1)附件Widget:是自包含的 Widget,它不需要其他应用程序的支持或者访问互联网。时钟、定时器、计算器以及便签都属于这个范畴。
(2)应用程序Widget:它与一个功能完全的应用程序相关联。这种 Widget 为应用程序提供一个复杂度比较低的界面,而且通常是只读的界面,对应用程序进行增强。iTunes 控制器和地址簿 Widget 都属于这个范畴。
(3)信息Widget:这是为了向用户提供来自互联网的数据而设计的。这些 Widget 使用户可以监控某些事件,比如天气、航班状态或者股票价格。
Dashboard 在为Mac OS的最终用户提供强大功能的同时,也为开发者带来了一个新鲜而丰富的开发环境。Widget 开发起来很快且易于部署,还可以调用Mac OS的部分系统功能。对于那些仅使用少量数据或者需要和其他程序交互的场合来说,Widget 是非常完美的技术,而且它还提供一个非常好的方式,来为已经存在的应用程序增加功能。对于开发者来说,这意味着很多机会:可以创建一个新的产品,也可以为现有的产品增加新功能,使之区别于其他同类产品。
3. Google Gadget
Google Gadget是简单的XML和JavaScript小型应用程序,,包括iGoogle、Google地图、Orkut或其他任何网页以及Google Desktop。
另外,Google Gadget还将Google现有的一些服务,例如Calendar、Blog、Map等,以独立模块的形式整合在一起。Google Gadget服务的表现形式是个性化主页或者Google桌面软件。
Google Gadget运行环境可以分为两类:一类是网页环境,如iGoogle、Google 地图、Orkut 或其他任何网页;另外一类就是Google Desktop。目前Google Desktop可以运行在Linux、Windows和Mac三种操作系统之上。运行在网页上的Gadget采用HTML语言编写,为了增强交互效果,可以加入JavaScript、Flash或者Silver light。Desktop Gadgets采用XML和JavaScript编写,也可以采用C、C++、C#或者 VB.net编写。Desktop Gadgets可以使用一些高级功能,如任意形状、透明效果、客户端库,而且能够响应用户来自Gadget外的一些动作
4. Opera Widget
Opera Widget并未提供一个统一的Widget管理工具,如果要运行Widget,需要首先启动Opera浏览器。在浏览器的窗口中,人们可以通过Opera桌面浏览器菜单“饰件→添加Widgets”来访问Opera Widget网站,并且可以进行Widget应用的下载、安装以及运行。
Opera Widget 也是用 HTML、CSS和JavaScrip等Web技术创建的跨平台应用程序,它是按照zip格式压缩的。Opera Widget必须以“.wgt”为后缀,content-type为“application/x-opera-Widgets”。它可以运行在所有安装Opera浏览器的平台上,如个人电脑、移动设备和游戏机。
Opera Widget是在不同设备上快速创建和部署应用程序的很好的方案。因此,用户可以快速轻松地开发Opera Widget,并且只要稍加改动就可以运行在各种不同的设备上。
Opera Widget运行环境可以运行在很多平台上,不同的平台有不同的特性和用例。不同平台的Widget运行环境,都希望最好地支持此设备的特性。运行环境可能会要求Widget以下面4种模式中的一种模式显示:Widget模式、漂浮模式、应用程序模式、全屏模式。
可以通过设置Widget的配置文件config. xml中的Widget的default mode属性来设置Widget的默认模式。Widget运行环境可能会要求Widget在模式间切换,例如从浮动模式先切换到应用程序模式,再切换到全屏模式。
为了方便开发Widget,Opera公司开发了Opera Widgets SDK,该SDK提供了开发工具、库、文档和实例。Opera Dragonfly是一个轻量级的开发工具,它可以调试JavaScript,查看CSS和HTML DOM。
5. JIL Widget
JIL Widget采用HTML、CSS、JavaScript编写的Web应用程序,后缀为“.wgt”。通过分析JIL Widget格式,可以发现JIL Widget与W3C Widget格式极为相似,所以开发者可以将基于W3C Widget格式应用很容易地转换成JIL Widget格式。
JIL Widget运行在JIL Widget引擎之上,JIL Widget引擎在支持Web标准的同时,也扩展了很多新的功能,这主要体现在以下两个方面。
(1)各种终端能力:JIL Widget引擎扩展了Telephony、PIM、Multimedia、Device、Messaging等对象,可以提供电话、通讯录、日程安排、多媒体、文件操作、系统信息等功能。
(2)运营商网络能力:通过扩展接口,可以获得位置、手机号码、在线状态等信息。
6. WRT Widget
Web Run-Time(WRT)是Symbian S60第三版Feature Pack 2平台引入的一个很重要的功能。Web Run-Time扩展了S60 Web浏览器,使之能支持Widget。它所支持的Widget是用标准Web技术开发的轻量级应用。
Web Run-Time的第一个版本专注于在移动终端上提供快速的信息呈现新方法。
从技术角度来看,WRT在流行的S60 Web浏览器中增加了一个Web程序的运行环境,使得S60设备可以运行Widget。事实上,几乎不花什么工夫就可以将桌面Widget迁移到WRT中来,
开发者可以使用现有的编写工具来创建和打包自己的应用。
在最新的S60第5版中整合了S60平台服务,可用标准的HTML和JavaScript技术创建更具个性和环境感知性的Widget。通过获取存储在本地设备上的信息,如日历和通讯录程序,Widget可以整合互联网和本地资源,提供一个全新的、个性化的服务体验。例如,通过访问设备上的GPS功能,Widget可以给用户提供更具相关性和环境感知性的信息。作为Web开发者,可以轻易地使用JavaScript扩展来访问这些新特性。
基于S60 V3.2平台的手机都支持WRT Widget,在Nokia的N97上,用户可以将自己喜欢的Widget应用放在待机屏幕上。目前,Widget应用成为Nokia OVI商店中一种重要的应用形式,可供用户下载的Widget应用有70多种。
一、用RelativeLayout进行纯代码布局的理论基础
1、RelativeLayout,顾名思义,就是以“相对”位置/对齐 为基础的布局方式。
2、android.widget.RelativeLayout 有个继承自android.view.ViewGroup.LayoutParams 的内嵌类 LayoutParams,使用这个类的实例
调用RelativeLayout.addView 就可以实现“相对布局”。 android.widget.RelativeLayout.LayoutParams 有一个构造函数:
RelativeLayout.LayoutParams(int w, int h),参数指定了子 View 的宽度和高度,这一点和其父类是一样的。而实现相对布局的关
键在它的 两个 addRule 方法上。anchor 参数指定可以是View 的 id(“相对于谁”)、RelativeLayout.TRUE(启用某种对齐方式) 或者
是-1(应用于某些不需要 anchor 的 verb);AddRule 方法的 verb 参数指定相对的“动作”(以下常量均定义于
android.widget.RelativeLayout中,为了简便不给出其全名):
3、ALIGN_BOTTOM、ALIGN_LEFT、 ALIGN_RIGHT、 ALIGN_TOP: 本 View 的 底边/左边/右边/顶边 和 anchor 指定的 View 的
底边/左边/右边/顶边 对齐。
ALIGN_WITH_PARENT_BOTTOM 、ALIGN_WITH_PARENT_LEFT 、 ALIGN_WITH_PARENT_RIGHT 、
ALIGN_WITH_PARENT_TOP : 和上面一组常量类似,只不过不需要再指定 anchor, 其 anchor 自动为 Parent View。
CENTER_HORIZONTAL、CENTER_IN_PARENT 、CENTER_VERTICAL : 如果 anchor 为 TRUE,在 Parent 中 水平居中/水平
和垂直均居中/垂直居中。
POSITION_ABOVE 、POSITION_BELOW 、 POSITION_TO_LEFT 、POSITION_TO_RIGHT : 本 View 位于 anchor 指定的 View
的上边/下边/左边/右边。
二、案例
1、布局文件如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF" >
<com.baidu.mapapi.map.MapView
android:id="@+id/baidu_map_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true" >
<RelativeLayout
android:id="@+id/anquan_map_l1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="5dp"
android:layout_marginTop="10dp" >
<ImageButton
android:id="@+id/but_of_lukuang"
android:layout_width="38.0dip"
android:layout_height="38.0dip"
android:background="@drawable/main_map_button_bg"
android:src="@drawable/maptraffic_icon_off" />
<ImageButton
android:id="@+id/btn_of_bobao"
android:layout_width="38.0dip"
android:layout_height="38.0dip"
android:layout_below="@id/but_of_lukuang"
android:layout_marginTop="5dp"
android:visibility="gone"
android:background="@drawable/main_map_button_bg"
android:src="@drawable/netfriend_bobao_n" />
<ImageButton
android:id="@+id/btn_of_layer"
android:layout_width="38.0dip"
android:layout_height="38.0dip"
android:layout_below="@+id/btn_of_bobao"
android:layout_marginTop="5dp"
android:background="@drawable/main_map_button_bg"
android:src="@drawable/main_map_icon_layer" />
2、代码如下
//得到
mapButtonRL = (RelativeLayout) findViewById(R.id.anquan_map_l1);
RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
lp1.addRule(RelativeLayout.BELOW, R.id.btn_of_layer);
showModeButton = new Button(this);
showModeButton.setText("全部显示");
showModeButton.setId(SHOW_MODE);
showModeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
mapButtonRL.addView(showModeButton, lp1);
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.BELOW, SHOW_MODE);
positionButton = new Button(this);
positionButton.setText("位置");
positionButton.setId(POSITION);
positionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
mapButtonRL.addView(positionButton, lp2);
1.概述
widget宿主端进程(比如Launcher) :AppWidgetService AppWidgetManager AppWidgetHostView AppWidgetHost
widget客户端进程:RmoteViews AppWidgetProviderInfo AppWidgetProvider(extends BroadcastReceiver)
2.相关类介绍
2.1.AppWidgetService.java
这是widget framework的核心类,是系统service之一,它承担着所有widget的
管理工作。Widget安装,删除,更新等等都需要经过AppWidgetService.它是开机就启动的.
在开机过程中,java层进程System Server启动后(这个进程管理着所有的系统service, 例如
activity manager service, windows manager service, power manager service等),会创建
AppWdigetService的实例,并调用它的SystemReady()方法,在这个方法里,它做了三件事:
1)遍历所有的安装包,找到符合条件ACTION=ACTION_APPWIDGET_UPDATE和的intent receiver,解析相关信息,保存到本地数据成员中。
2)读取本地文件数据:/data/system/appwidgets.xml,所有已安装到桌面的widget的信息都保存在这个文件里。读出来,也保存到本地数据成员里。
3)注册了三个消息:ACTION_BOOT_COMPLETED(系统启动到桌面就会发送此消息),ACTION_PACKAGE_ADDED(有新APK包安装到系统),ACTION_PACKAGE_REMOVED(有APK包被删除)。当系统启动到桌面后,AppWidgetService接收到了ACTION_BOOT_COMPLETED消息,它会去检查本地数据成员,如果有已经安装到桌面的widget,它会上发ACTION_APPWIDGET_ENABLED和ACTION_APPWIDGET_UPDATE消息。如果有widget设置了updatePeriodMillis的属性,它就会开始计时(这个是通过AlarmManager来实现的),到时间时,就会再次上发ACTION_APPWIDGET_UPDATE消息。
1、继承AppWidgetProvider
我们编写的桌面Widget需要提供数据更新,这里就需用用到AppWidgetProvider,它里面有一些系统回调函数。提供更新数据的操作。AppWidgetProvider是BrocastReceiver的之类,也就是说它其实本质是一个广播接收器。下面我们看看AppWidgetProvider的几个重要的回调方法:
复制代码
代码如下:
class WidgetProvider extends
AppWidgetProvider
{
private static final String
TAG="mythou_Widget_Tag";
// 没接收一次广播消息就调用一次,使用频繁
public void
onReceive(Context context, Intent intent)
{
Log.d(TAG,
"mythou--------->onReceive");
super.onReceive(context,
intent);
}
// 每次更新都调用一次该方法,使用频繁
public void
onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.d(TAG, "mythou--------->onUpdate");
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
//
没删除一个就调用一次
public void onDeleted(Context context, int[] appWidgetIds)
{
Log.d(TAG, "mythou--------->onDeleted");
super.onDeleted(context, appWidgetIds);
}
//
当该Widget第一次添加到桌面是调用该方法,可添加多次但只第一次调用
public void onEnabled(Context
context)
{
Log.d(TAG,
"mythou--------->onEnabled");
super.onEnabled(context);
}
// 当最后一个该Widget删除是调用该方法,注意是最后一个
public void
onDisabled(Context context)
{
Log.d(TAG,
"mythou--------->onDisabled");
super.onDisabled(context);
}
}
其中我们比较常用的是onUpdate和onDelete方法。我这里刷新时间使用了一个Service,因为要定时刷新服务,还需要一个Alarm定时器服务。下面给出我的onUpdate方法:
复制代码
代码如下:
public void onUpdate(Context context,
AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
super.onUpdate(context, appWidgetManager, appWidgetIds);
Time time = new
Time();
time.setToNow();
//使用Service更新时间
Intent intent = new
Intent(context, UpdateService.class);
PendingIntent pendingIntent =
PendingIntent.getService(context, 0, intent, 0);
//使用Alarm定时更新界面数据
AlarmManager alarm =
(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC, time.toMillis(true), 60*1000,
pendingIntent);
}
2、AndroidManifest.xml配置
复制代码
代码如下:
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<!-- AppWidgetProvider的注册
mythou-->
<receiver
android:label="@string/app_name_timewidget"
android:name="com.owl.mythou.TimeWidget">
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE">
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/time_widget_config">
<!-- 更新时间的后台服务
mythou-->
<service
android:name="com.owl.mythou.UpdateService">
AndroidManifest主要是配置一个receiver,因为AppWidgetProvider就是一个广播接收器。另外需要注意的是,里面需要提供一个action,这个是系统的更新widget的action。还有meta-data里面需要指定widget的配置文件。这个配置文件,需要放到res\xml目录下面,下面我们看看time_widget_config.xml的配置
3、appWidget配置:
复制代码
代码如下:
<?xml version="1.0"
encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/time_widget_layout"
android:minWidth="286dip"
android:minHeight="142dip"
android:updatePeriodMillis="0">
•android:initialLayout
指定界面布局的Layout文件,和activity的Layout一样
•android:minWidth
你的widget的最小宽度。根据Layout的单元格计算(72*格子数-2)
•android:minHeigh
你的widget的最小高度。计算方式和minwidth一样。(对这个不了解可以看我Launcher分析文章)
•android:updatePerioMillis
使用系统定时更新服务,单位毫秒。
这里需要说明android:updatePerioMillis的问题,系统为了省电,默认是30分钟更新一次,如果你设置的值比30分钟小,系统也是30分钟才会更新一次。对于我们做时间Widget来说,显然不靠谱。所以只能自己编写一个Alarm定时服务更新。
4、更新Widget的Service服务
复制代码
代码如下:
class UpdateService extends
Service
{
@Override
public void onStart(Intent intent, int
startId)
{
super.onStart(intent, startId);
UpdateWidget(this);
}
private void UpdateWidget(Context context)
{
//不用Calendar,Time对cpu负荷较小
Time time = new
Time();
time.setToNow();
int hour = time.hour;
int min = time.minute;
int second = time.second;
int year
= time.year;
int month = time.month+1;
int day =
time.monthDay;
String strTime = String.format("%02d:%02d:%02d
%04d-%02d-%02d", hour, min, second,year,month,day);
RemoteViews
updateView = new RemoteViews(context.getPackageName(),
R.layout.time_widget_layout);
//时间图像更新
String
packageString="org.owl.mythou";
String timePic="time";
int
hourHbit = hour/10;
updateView.setImageViewResource(R.id.hourHPic,
getResources().getIdentifier(timePic+hourHbit, "drawable",
packageString));
int hourLbit = hour%10;
updateView.setImageViewResource(R.id.hourLPic,
getResources().getIdentifier(timePic+hourLbit, "drawable",
packageString));
int minHbit = min/10;
updateView.setImageViewResource(R.id.MinuteHPic,
getResources().getIdentifier(timePic+minHbit, "drawable",
packageString));
int minLbit = min%10;
updateView.setImageViewResource(R.id.MinuteLPic,
getResources().getIdentifier(timePic+minLbit, "drawable",
packageString));
//星期几
updateView.setTextViewText(R.id.weekInfo,
getWeekString(time.weekDay+1));
//日期更新,根据日期,计算使用的图片
String datePic="date";
int year1bit = year/1000;
updateView.setImageViewResource(R.id.Year1BitPic,
getResources().getIdentifier(datePic+year1bit, "drawable",
packageString));
int year2bit = (year%1000)/100;
updateView.setImageViewResource(R.id.Year2BitPic,
getResources().getIdentifier(datePic+year2bit, "drawable",
packageString));
int year3bit = (year%100)/10;
updateView.setImageViewResource(R.id.Year3BitPic,
getResources().getIdentifier(datePic+year3bit, "drawable",
packageString));
int year4bit = year%10;
updateView.setImageViewResource(R.id.Year4BitPic,
getResources().getIdentifier(datePic+year4bit, "drawable",
packageString));
//月
int mouth1bit = month/10;
updateView.setImageViewResource(R.id.mouth1BitPic,
getResources().getIdentifier(datePic+mouth1bit, "drawable",
packageString));
int mouth2bit = month%10;
updateView.setImageViewResource(R.id.mouth2BitPic,
getResources().getIdentifier(datePic+mouth2bit, "drawable",
packageString));
//日
int day1bit = day/10;
updateView.setImageViewResource(R.id.day1BitPic,
getResources().getIdentifier(datePic+day1bit, "drawable",
packageString));
int day2bit = day%10;
updateView.setImageViewResource(R.id.day2BitPic,
getResources().getIdentifier(datePic+day2bit, "drawable",
packageString));
//点击widget,启动日历
Intent launchIntent =
new Intent();
launchIntent.setComponent(new
ComponentName("com.mythou.mycalendar",
"com.mythou.mycalendar.calendarMainActivity"));
launchIntent.setAction(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
PendingIntent
intentAction = PendingIntent.getActivity(context, 0,
launchIntent, 0);
updateView.setOnClickPendingIntent(R.id.SmallBase,
intentAction);
AppWidgetManager awg =
AppWidgetManager.getInstance(context);
awg.updateAppWidget(new
ComponentName(context, TimeWidgetSmall.class),
updateView);
开线程 用handle 进行刷新 想刷哪块就刷那块。
Handler
// 在onCreate()中开启线程
new Thread(new GameThread()).start();、
// 实例化一个handler
Handler myHandler = new Handler()
{
//接收到消息后处理
public void handleMessage(Message msg)
{
switch (msg.what)
{
case Activity01.REFRESH:
mGameView.invalidate(); //刷新界面
break;
}
super.handleMessage(msg);
}
};
class GameThread implements Runnable
{
public void run()
{
while (!Thread.currentThread().isInterrupted())
{
Message message = new Message();
message.what = Activity01.REFRESH;
//发送消息
Activity01.this.myHandler.sendMessage(message);
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
}
}