1、App widget介绍
App Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新。你可以通过一个App Widget Provider来发布一个Widget。
2、AppWidgetProvider
AppWidgetProvider 继承自 BroadcastReceiver,它能接收 widget 相关的广播,例如 widget 的更新、删除、开启和禁用等。
AppWidgetProvider中的广播处理函数如下:
onUpdate()
当 widget 更新时被执行。
同样,当用户首次添加 widget 时,onUpdate() 也会被调用,这样 widget 就能进行必要的设置工作(如果需要的话) 。但是,如果定义了 widget 的 configure属性(即android:config,后面会介绍),那么当用户首次添加 widget 时,onUpdate()不会被调用;之后更新 widget 时,onUpdate才会被调用。
onAppWidgetOptionsChanged()
当 widget 被初次添加 或者 当 widget 的大小被改变时,执行onAppWidgetOptionsChanged()。你可以在该函数中,根据 widget 的大小来显示/隐藏某些内容。可以通过 getAppWidgetOptions() 来返回 Bundle 对象以读取 widget 的大小信息,Bundle中包括以下信息:
OPTION_APPWIDGET_MIN_WIDTH -- 包含 widget 当前宽度的下限,以dp为单位。
OPTION_APPWIDGET_MIN_HEIGHT -- 包含 widget 当前高度的下限,以dp为单位。
OPTION_APPWIDGET_MAX_WIDTH -- 包含 widget 当前宽度的上限,以dp为单位。
OPTION_APPWIDGET_MAX_HEIGHT -- 包含 widget 当前高度的上限,以dp为单位。
onAppWidgetOptionsChanged() 是 Android 4.1 引入的。
onDeleted(Context, int[])
当 widget 被删除时被触发。
onEnabled(Context)
当第1个 widget 的实例被创建时触发。也就是说,如果用户对同一个 widget 增加了两次(两个实例),那么onEnabled()只会在第一次增加widget时触发。
onDisabled(Context)
当最后1个 widget 的实例被删除时触发。
onReceive(Context, Intent)
接收到任意广播时触发,并且会在上述的方法之前被调用。
总结,AppWidgetProvider 继承于 BroadcastReceiver。实际上,App Widge中的onUpdate()、onEnabled()、onDisabled()等方法都是在 onReceive()中调用的;是onReceive()对特定事情的响应函数
3、AppWidgetProvider
AppWidgetProviderInfo描述一个App Widget元数据,比如App Widget的布局,更新频率,以及AppWidgetProvider 类。这个应该在XML里定义。下面以XML示例来对AppWidgetProviderInfo中常用的类型进行说明。
示例XML:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="250dp"
android:minHeight="150dp"
android:minResizeWidth="250dp"
android:minResizeHeight="150dp"
android:updatePeriodMillis="0"
android:previewImage="@drawable/appwidget_digital_clock_preview"
android:initialLayout="@layout/new_digital_appwidget"
android:resizeMode="vertical|horizontal"
android:widgetCategory="keyguard|home_screen"
>
</appwidget-provider>
示例说明:
minWidth 和minHeight
它们指定了App Widget布局需要的最小区域。
缺省的App Widgets所在窗口的桌面位置基于有确定高度和宽度的单元网格中。如果App Widget的最小长度或宽度和这些网格单元的尺寸不匹配,那么这个App Widget将上舍入(上舍入即取比该值大的最接近的整数——译者注)到最接近的单元尺寸。
注意:app widget的最小尺寸,不建议比 “4x4” 个单元格要大。关于app widget的尺寸,后面在详细说明。
minResizeWidth 和 minResizeHeight
它们属性指定了 widget 的最小绝对尺寸。也就是说,如果 widget 小于该尺寸,便会因为变得模糊、看不清或不可用。 使用这两个属性,可以允许用户重新调整 widget 的大小,使 widget 的大小可以小于 minWidth 和 minHeight。
注意:(01) 当 minResizeWidth 的值比 minWidth 大时,minResizeWidth 无效;当 resizeMode 的取值不包括 horizontal 时,minResizeWidth 无效。
(02) 当 minResizeHeight 的值比 minHeight 大时,minResizeHeight 无效;当 resizeMode 的取值不包括 vertical 时,minResizeHeight 无效。
updatePeriodMillis
它定义了 widget 的更新频率。实际的更新时机不一定是精确的按照这个时间发生的。建议更新尽量不要太频繁,最好是低于1小时一次。 或者可以在配置 Activity 里面供用户对更新频率进行配置。 实际上,当updatePeriodMillis的值小于30分钟时,系统会自动将更新频率设为30分钟!关于这部分,后面会详细介绍。
注意: 当更新时机到达时,如果设备正在休眠,那么设备将会被唤醒以执行更新。如果更新频率不超过1小时一次,那么对电池寿命应该不会造成多大的影响。 如果你需要比较频繁的更新,或者你不希望在设备休眠的时候执行更新,那么可以使用基于 alarm 的更新来替代 widget 自身的刷新机制。将 alarm 类型设置为 ELAPSED_REALTIME 或 RTC,将不会唤醒休眠的设备,同时请将 updatePeriodMillis 设为 0。
initialLayout
指向 widget 的布局资源文件
configure
可选属性,定义了 widget 的配置 Activity。如果定义了该项,那么当 widget 创建时,会自动启动该 Activity。
previewImage
指定预览图,该预览图在用户选择 widget 时出现,如果没有提供,则会显示应用的图标。该字段对应在 AndroidManifest.xml 中 receiver 的 android:previewImage 字段。由 Android 3.0 引入。
autoAdvanceViewId
指定一个子view ID,表明该子 view 会自动更新。在 Android 3.0 中引入。
resizeMode
指定了 widget 的调整尺寸的规则。可取的值有: "horizontal", "vertical", "none"。"horizontal"意味着widget可以水平拉伸,“vertical”意味着widget可以竖值拉伸,“none”意味着widget不能拉伸;默认值是"none"。Android 3.1 引入。
widgetCategory
指定了 widget 能显示的地方:能否显示在 home Screen 或 lock screen 或 两者都可以。它的取值包括:"home_screen" 和 "keyguard"。Android 4.2 引入。
initialKeyguardLayout
指向 widget 位于 lockscreen 中的布局资源文件。Android 4.2 引入。
4、APP widget布局说明
4.1、添加到lock srceen中
默认情况下(即不设置android:widgetCategory属性),Android是将widget添加到 home screen 中。
但在Android 4.2中,若用户希望 widget 可以被添加到lock screen中,可以通过设置 widget 的 android:widgetCategory 属性包含keyguard来完成。
当你把 widget 添加到lock screen中时,你可能对它进行定制化操作,以区别于添加到home screen中的情况。 你能够通过 getAppWidgetOptions() 来进行判断 widget 是被添加到lock screen中,还是home screen中。通过 getApplicationOptions() 获取 Bundle对象,然后读取 Bundle 的OPTION_APPWIDGET_HOST_CATEGORY值:若值为 WIDGET_CATEGORY_HOME_SCREEN, 则表示该 widget 被添加到home screen中; 若值为 WIDGET_CATEGORY_KEYGUARD,则表示该 widget 被添加到lock screen中。
另外,你应该为添加到lock screen中的 widget 单独使用一个layout,可以通过 android:initialKeyguardLayout 来指定。而 widget 添加到home screen中的layout则可以通过 android:initialLayout 来指定。
4.2、支持的布局控件
Widget并不支持所有的布局和控件,而仅仅只是支持Android布局和控件的一个子集。
(01) App Widget支持的布局:
FrameLayout
LinearLayout
RelativeLayout
GridLayout
(02) App Widget支持的控件:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
5、示例
1、Androidmanifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.skywang.widget" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <!-- 声明widget对应的AppWidgetProvider --> <receiver android:name=".ExampleAppWidgetProvider" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="com.skywang.widget.UPDATE_ALL"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/example_appwidget_info" /> </receiver> <service android:name=".ExampleAppWidgetService" > <intent-filter> <action android:name="android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE" /> </intent-filter> </service> </application> </manifest>
说明:
(01) ExampleAppWidgetProvider是继承于的AppWidgetProvider类,用来响应widget的添加、删除、更新等操作。
(02) android.appwidget.action.APPWIDGET_UPDATE,必须要显示声明的action!因为所有的widget的广播都是通过它来发送的;要接收widget的添加、删除等广播,就必须包含它。
(03) action android:name="com.skywang.widget.UPDATE_ALL,这是我自己添加了,是为了接收服务所发送的更新图片的广播。
(04) <meta-data> 指定了 AppWidgetProviderInfo 对应的资源文件
android:name -- 指定metadata名,通过android.appwidget.provider来辨别data。
android:resource -- 指定 AppWidgetProviderInfo 对应的资源路径。即,xml/example_appwidget_info.xml。
(05) ExampleAppWidgetService 是用于更新widget中的图片的服务。
(06) android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE 用于启动服务的action。
2、编辑AppWidgetProviderInfo 对应的资源文件
在当前工程下新建xml目录(若xml目录不存在的话);并在xml目录下新建example_appwidget_info.xml文件
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="180dp" android:minHeight="180dp" android:previewImage="@drawable/preview" android:initialLayout="@layout/example_appwidget" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen|keyguard"> <!-- android:minWidth : 最小宽度 android:minHeight : 最小高度 android:updatePeriodMillis : 更新widget的时间间隔(ms),"86400000"为1个小时 android:previewImage : 预览图片 android:initialLayout : 加载到桌面时对应的布局文件 android:resizeMode : widget可以被拉伸的方向。horizontal表示可以水平拉伸,vertical表示可以竖直拉伸 android:widgetCategory : widget可以被显示的位置。home_screen表示可以将widget添加到桌面,keyguard表示widget可以被添加到锁屏界面。 android:initialKeyguardLayout : 加载到锁屏界面时对应的布局文件 --> </appwidget-provider>
说明:
(01) android:previewImage,用于指定预览图片。即搜索到widget时,查看到的图片。若没有设置的话,系统为指定一张默认图片。
(02) android:updatePeriodMillis 更新widget的时间间隔(ms)。
3、编辑widget布局文件
新建layout/example_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" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="HomeScreen Widget" /> <Button android:id="@+id/btn_show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Show" /> </LinearLayout> <ImageView android:id="@+id/iv_show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"/> </LinearLayout>
4、编辑ExampleAppWidgetProvider.java
public class ExampleAppWidgetProvider extends AppWidgetProvider { private static final String TAG = "ExampleAppWidgetProvider"; private boolean DEBUG = false; // 启动ExampleAppWidgetService服务对应的action private final Intent EXAMPLE_SERVICE_INTENT = new Intent("android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE"); // 更新 widget 的广播对应的action private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL"; // 保存 widget 的id的HashSet,每新建一个 widget 都会为该 widget 分配一个 id。 private static Set idsSet = new HashSet(); // 按钮信息 private static final int BUTTON_SHOW = 1; // 图片数组 private static final int[] ARR_IMAGES = { R.drawable.sample_0, R.drawable.sample_1, R.drawable.sample_2, R.drawable.sample_3, R.drawable.sample_4, R.drawable.sample_5, R.drawable.sample_6, R.drawable.sample_7, }; // onUpdate() 在更新 widget 时,被执行, @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.d(TAG, "onUpdate(): appWidgetIds.length="+appWidgetIds.length); // 每次 widget 被创建时,对应的将widget的id添加到set中 for (int appWidgetId : appWidgetIds) { idsSet.add(Integer.valueOf(appWidgetId)); } prtSet(); } // 当 widget 被初次添加 或者 当 widget 的大小被改变时,被调用 @Override public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { Log.d(TAG, "onAppWidgetOptionsChanged"); super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); } // widget被删除时调用 @Override public void onDeleted(Context context, int[] appWidgetIds) { Log.d(TAG, "onDeleted(): appWidgetIds.length="+appWidgetIds.length); // 当 widget 被删除时,对应的删除set中保存的widget的id for (int appWidgetId : appWidgetIds) { idsSet.remove(Integer.valueOf(appWidgetId)); } prtSet(); super.onDeleted(context, appWidgetIds); } // 第一个widget被创建时调用 @Override public void onEnabled(Context context) { Log.d(TAG, "onEnabled"); // 在第一个 widget 被创建时,开启服务 context.startService(EXAMPLE_SERVICE_INTENT); super.onEnabled(context); } // 最后一个widget被删除时调用 @Override public void onDisabled(Context context) { Log.d(TAG, "onDisabled"); // 在最后一个 widget 被删除时,终止服务 context.stopService(EXAMPLE_SERVICE_INTENT); super.onDisabled(context); } // 接收广播的回调函数 @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); Log.d(TAG, "OnReceive:Action: " + action); if (ACTION_UPDATE_ALL.equals(action)) { // “更新”广播 updateAllAppWidgets(context, AppWidgetManager.getInstance(context), idsSet); } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) { // “按钮点击”广播 Uri data = intent.getData(); int buttonId = Integer.parseInt(data.getSchemeSpecificPart()); if (buttonId == BUTTON_SHOW) { Log.d(TAG, "Button wifi clicked"); Toast.makeText(context, "Button Clicked", Toast.LENGTH_SHORT).show(); } } super.onReceive(context, intent); } // 更新所有的 widget private void updateAllAppWidgets(Context context, AppWidgetManager appWidgetManager, Set set) { Log.d(TAG, "updateAllAppWidgets(): size="+set.size()); // widget 的id int appID; // 迭代器,用于遍历所有保存的widget的id Iterator it = set.iterator(); while (it.hasNext()) { appID = ((Integer)it.next()).intValue(); // 随机获取一张图片 int index = (new java.util.Random().nextInt(ARR_IMAGES.length)); if (DEBUG) Log.d(TAG, "onUpdate(): index="+index); // 获取 example_appwidget.xml 对应的RemoteViews RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.example_appwidget); // 设置显示图片 remoteView.setImageViewResource(R.id.iv_show, ARR_IMAGES[index]); // 设置点击按钮对应的PendingIntent:即点击按钮时,发送广播。 remoteView.setOnClickPendingIntent(R.id.btn_show, getPendingIntent(context, BUTTON_SHOW)); // 更新 widget appWidgetManager.updateAppWidget(appID, remoteView); } } private PendingIntent getPendingIntent(Context context, int buttonId) { Intent intent = new Intent(); intent.setClass(context, ExampleAppWidgetProvider.class); intent.addCategory(Intent.CATEGORY_ALTERNATIVE); intent.setData(Uri.parse("custom:" + buttonId)); PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0 ); return pi; } // 调试用:遍历set private void prtSet() { if (DEBUG) { int index = 0; int size = idsSet.size(); Iterator it = idsSet.iterator(); Log.d(TAG, "total:"+size); while (it.hasNext()) { Log.d(TAG, index + " -- " + ((Integer)it.next()).intValue()); } } } }
说明:
(01) 当我们创建第一个widget到桌面时,会执行onEnabled()。在onEnabled()中通过 context.startService(EXAMPLE_SERVICE_INTENT) 启动服务ExampleAppWidgetService。服务的作用就是每隔5秒发送一个ACTION_UPDATE_ALL广播给我们,用于更新widget中的图片。
仅仅当我们创建第一个widget时才会启动服务,因为onEnabled()只会在第一个widget被创建时才执行。
(02) 当我们删除最后一个widget到桌面时,会执行onDisabled()。在onDisabled()中通过 context.stopService(EXAMPLE_SERVICE_INTENT) 终止服务ExampleAppWidgetService。
仅仅当我们删除最后一个widget时才会终止服务,因为onDisabled()只会在最后一个widget被删除时才执行。
(03) 本工程中,每添加一个widget都会执行onUpdate()。例外情况:在定义android:configure的前提下,第一次添加widget时不会执行onUpdate(),而是执行android:configure中定义的activity。
(04) onReceive()中,处理两个广播:更新桌面的widget 以及 响应按钮点击广播。
当收到ACTION_UPDATE_ALL广播时,调用updateAllAppWidgets()来更新所有的widget。
当收到的广播的categery为Intent.CATEGORY_ALTERNATIVE,并且scheme为BUTTON_SHOW时,对应是按钮点击事件。按钮的监听是在updateAllAppWidgets()中注册的。
参考:http://blog.csdn.net/sasoritattoo/article/details/17616597
精彩科技工作室
相关推荐
开发Android窗口小部件首先需要了解的是App Widget框架。它允许开发者创建可以在主屏幕上显示的自定义组件,并通过AppWidgetProvider类进行管理。AppWidgetProvider类似于BroadcastReceiver,会在特定事件(如系统...
本教程将引导你实现一个Android小窗口部件,特别是一个时钟部件,这对于熟悉Android开发和提高用户体验至关重要。 首先,我们需要创建一个新的Android项目,并确保在`AndroidManifest.xml`文件中声明支持App Widget...
为大家分享一套Android widget窗口小部件用法实例集源码,在这个例子中,将涉及到一些大家非常熟悉而且非常有用的窗体UI组件的用法,比如Button按钮、textView、Editview编辑框、Checkbox选择框、RadioGroup单选框、...
AppWidget 即桌面小部件,也叫桌面控件,就是能直接显示在Android系统桌面上的小程序,先看图: 图中我用黄色箭头指示的即为AppWidget,一些用户使用比较频繁的程序,可以做成AppWidget,这样能方便地使用。典型...
**AppWidget窗口小部件** AppWidget是Android操作系统中的一种特性,它允许开发者创建可以在用户主屏幕上展示的小型应用组件,用户无需打开应用就能与之交互。AppWidget是Android系统为用户提供快捷服务和信息预览...
通过以上步骤,你可以创建一个基本的Android窗口小工具。随着对AppWidgetAPI的深入理解和实践,你可以构建出更复杂、功能更丰富的窗口小工具,为用户提供更佳的使用体验。在实际开发中,还要考虑性能优化、兼容性...
这里我们将详细介绍如何实现一个基础的Android窗口小部件,它能从手机储存卡中随机选取一张图片进行显示。 首先,创建一个新的Android项目,选择"Empty Activity"模板。然后,我们需要为窗口小部件添加一个新的组件...
AppWidget的创建与管理涉及多个类和方法,理解这些组件和它们之间如何交互对于开发一个功能完备的窗口小部件至关重要。需要注意的是,尽管在创建AppWidget时可以利用大量的在线资料和文档,但具体的实现和运用还需要...
启动器应用通常需要广泛的测试,因为它涉及到许多与Android系统交互的行为,包括但不限于应用图标、窗口小部件(Widgets)、多窗口模式等等。只有确保Launcher应用在不同的设备和Android版本上都能正常工作,才能...
在Android系统中,小窗口(通常称为悬浮窗或小部件)是一种可以在主屏幕或其他应用程序之上显示的用户界面组件。这种特性使得用户可以同时处理多个任务,增强了多任务处理能力。在Android开发中,实现小窗口功能主要...
在Android开发中,应用程序窗口小部件(App Widgets)是一种可以在用户主屏幕上展示信息,并能与用户交互的小型应用视图。它们通常用于提供快速访问功能或者实时信息更新,如天气预报、音乐播放控制等。本篇文章将...
总之,`DirectSelect`下拉菜单小部件是Android开发中的一个强大工具,它提供了许多原生`Spinner`所不具备的功能,能够帮助开发者创造出更具吸引力的用户界面。通过熟练掌握`DirectSelect`的使用,可以提升应用的交互...
在Android开发中,可拖动窗口(Draggable Window)是一种用户界面组件,允许用户通过触摸手势自由移动窗口在屏幕上的位置。这样的功能常见于浮动小部件、弹出对话框或者自定义视图中,可以增强应用的交互性和用户...
在Android系统中,"android悬浮窗口可拖动"是一个常见的需求,主要用于实现类似桌面小部件、聊天头部或者应用辅助工具等功能。悬浮窗口通常基于系统的`WindowManager`服务进行创建,可以独立于当前活跃的应用界面...
在Android开发中,窗口部件(Widgets)是构建用户界面的核心元素。这些部件使得用户能够与应用程序进行交互。本文主要探讨Android中最基础的两种窗口部件:TextView(标签)和Button(按钮),以及如何在XML布局文件...
在Android开发中,构建用户界面(UI)是一个关键任务,而使用基本的窗口部件是构建UI的基础。Android提供了丰富的窗口部件库,其中包含了诸如TextView、Button等常用的元素。本篇文章将详细探讨如何在Android中使用...
在Android开发中,"悬浮窗口"(通常称为悬浮小部件或系统层窗口)是一种能够覆盖在其他应用之上显示自定义内容的UI元素。这种技术在许多应用中都有应用,例如屏幕录制工具、音乐播放器控制界面等。"Android悬浮窗口...
在Android系统中,Activity是应用程序的基本构建块,它代表...以上就是关于"android窗口式activity"的相关知识点,包括其实现原理、创建步骤、应用场景以及注意事项。希望对你在项目开发中使用窗口式Activity有所帮助。
Delphi中的当前版本可以在不崩溃的情况下运行窗口小部件,但是无法加载通过窗口小部件代码中通过RemoteViews对象进行更新的TextView。 要测试项目在哪里进行,请运行Delphi项目并进行调试以编译到设备。 这会将App...