【android开发】桌面小挂件( APP Widgets )
APP小挂件指的是一个小型的应用View控件,他可以嵌入到其他应用程序中(比如说桌面),并接受定期的更新。你可以通过Widget Provider来自己发布一个。一个可以持有其他App小挂件的应用组件叫做AppWidget host。
下图表示一个音乐应用的挂件;
此文章将会描述怎么使用AppWidget provider去发布一个应用挂件。创建挂件牵涉到的类是:
android.appwidget.AppWidgetHost |
一、基本的描述
要创建一个App挂件,你需要做到下面的工作。
- AppWidgetProviderInfo对象
描述App挂件的元数据,比如说:挂件的布局,更新频率,和AppWidgetProvider类。你需在XML文件中定义
- 继承AppWidgetProvider类
定义一个基于广播事件的允许动态改变挂件界面的方法,通过他,当你的应用挂件更新、变得可用、变得不可用、被删除的事件你都可以收到。
- View Layout
在XML文件中定义一个挂件的初始布局文件。
此外,你可以实现一个挂件配置 Activity,他是一个可选的Activity,当用户要添加你的挂件时,他会启动用于给用户设置和更改挂件的设置。
下面将介绍怎么去设置一个这样的组件
在Manifest中申明一个挂件
首先需要在Manifest中声明AppWidgetProvider类。比如说:
<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>
<receiver>元素需要android:name属性,他指的是挂件使用的AppWidgetProvider
<intent-filter>必须要包含一个<action>元素,并且包含android:name属性。这个属性表示AppWidgetProvider接受ACTION_APPWIDGET_UPDATE广播。这是你唯一需要明确指定的广播接受,在必要的时候AppWidgetManager将会自动发送广播给所有挂件。
<meta-data>元素定义了AppWidgetProviderInfo资源,他需要有下面几个属性:
- Android:name----定义元数据的名字,此例中使用“android.appwidget.provider”来定义这个数据,作为AppWidgetProviderInfo的描述信息
- android:resource---定义AppWidgetProviderInfo资源的地址
添加AppWidgetProviderInfo元数据
AppWidgetProviderInfo定义了一个挂件的基本品质,比如说:最小布局尺寸、他初始化的布局资源、更新挂件的频率、和configurationActivity在创建的时候加载。在XML文件中定义AppWidgetProviderInfo对象只需要使用<appwidget-provider>元素,保存在res/xml文件夹中。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"android:minWidth="40dp"android:minHeight="40dp"android:updatePeriodMillis="86400000"android:previewImage="@drawable/preview"android:initialLayout="@layout/example_appwidget"android:configure="com.example.android.ExampleAppWidgetConfigure"android:resizeMode="horizontal|vertical"android:widgetCategory="home_screen">
</appwidget-provider>
下面是<appwidget-provider>的属性概览:
- minWidth和minHeight的值表示挂件需要的最小空间。默认情况下,挂件的位置是基于主屏幕中的具有宽高的方格子(可以看做将屏幕划分为很多个矩阵格子)。如果挂件的宽和高与方格子不匹配,挂件的尺寸将计算到与方格子相近的尺寸。
【注意:为了使你的挂件能方便的跨设备,你最好将你的最小尺寸设置为低于4 X 4】
- minResizeWidth和minResizeHeight属性指定了挂件的绝对最小尺寸,这个值应该定义为,当你的挂件出现字迹不清与不可用时的最小尺寸。使用这些属性允许用户去重新设置挂件的尺寸,可能会小于你使用minWidth与minHigh的值。这个特性从 android 3.1开始
- updatePeriodMillis属性定义了挂件多久被AppWidgetProvider调用onUpdate()刷新一次,事实上不能保证准时刷新(会有一定的出入),我们建议刷新频率不要太高,以便保持电量。当然你也可以允许用户自己去设置刷新的频率,有的人可能会设置15分钟刷新,当然也有人可能设置为一天只刷新4次。
【如果设备正在休眠,但是到了刷新你挂件的时间,设备将会被激活,然后去执行更新操作。如果你一个多小时的刷新一次可能不会对电池造成什么影响,但是,如果你刷新频率比较高,或者可能不需要挂件在休眠的时候刷新,那么你可以基于alarm刷新,他将不会激活设备。实现方式:使用你的AppWidgetProvider需要接受的Intent设置一个alarm(使用AlarmManager类),将alarm的类型设置为ELAPSED_REALTIME或者RTC,这样只有在设备唤醒的情况下才通知你的挂件刷新,最后你需要将你的updatePeriodMillis设置为 0 】
- initialLayout属性指向一个定义挂件布局的XML文件。
- configure定义了一个Activity,在用户添加挂件的时候他将会启动,以便用户去设置挂件的配置。这是可选操作。(具体怎么创建后面将会介绍到)
- previewImage属性,用作预览的图片。当用户还未添加挂件的时候通过此图片让用户大致知道你的挂件。此属性与在Manifest中的<receiver>中的android:previewImage是一样的。(具体怎么设置后面将会介绍)
- autoAdvanceViewId属性定义了可以被挂件宿主自增加的子View的id。在android 3.0中被引进。
- resizeMode属性指定了挂件被重定义大小的规则。你可以使用此挂件让你的挂件在主屏幕中可以被调节大小---水平,垂直,同时。用户长按挂件可以去显示调节把柄,然后拖拽把柄可以改变大小(还是以方格子为单元)。resizeMode的值包括:“horizontal”、“vertical”、“none”。如果你要设置为水平、垂直都可以改变,那么这样设置“horizontal | vertical”。在android 3.1中引进
- minResizeHeight属性定义了那些可以被调节大小的挂件的最小的高度(使用dp做单位)。如果挂件比minHeight大或者挂件调节大小并不可用时,此字段将没有作用。在android 4.0中引进
- minResizeWidth属性定义了最小的宽度(使用dp作单位),与minResizeHeight类似的作用
- widgetCategory属性表示你的挂件是否可以展示在主屏幕(home_screen)中,锁屏界面(keyguard)中,或者同时。只有在android 5.0以下的才可以使用锁屏界面的挂件(keyguard),在android5.0或以上只能使用 home_screen
参考类android.appwidget.AppWidgetProviderInfo以了解更多
创建挂件的布局文件
你必须在XML文件中定义个初始化的布局文件,保存在你工程的res/layout/目录下。你可以使用下面的View对象来设计你的布局。但是,在你设计你的布局之前,你最好先阅读并理解挂件的设计指导方针AppWidget Design Guidelines:(见右侧)
创建挂件非常简单,如果你熟悉Layout的话。然而,你必须知道的是,挂件的布局使基于RemoteViews(android.widget.RemoteViews)的,不是所有的布局和view控件他都支持。
他们支持的layout如下:
- FrameLayout
- LinearLayout
- RelativeLayout
- GridLayout
他们支持的控件如下:(他们的衍生品不支持)
- AnalogClock【时钟控件】
- Button
- Chronometer【计时器】
- ImageButton
- ImageView
- ProgressBar
- TextView
- ViewFlipper
- ListView
- GridView
- StackView
- AdapterViewFlipper
(他们都在android.widget包下。)
RemoteView也支持使用ViewStub(android.view.ViewStub)进行运行时懒加载。
为挂件添加margin
我们的控件一般不需要延生到边缘,也不要和其他挂件挤在一起了。因此我们需要在边界上加上一个的空白。那就是“Margin”或者“padding”
在android4.0以后,系统将在挂件之间自动加上padding。我们不需要自己手动的添加。如果低于4.0的,我们自己加上就行了。
例子如下:
<FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:padding="@dimen/widget_margin"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"android:background="@drawable/my_widget_background">…</LinearLayout></FrameLayout>
资源文件如下:
res/values/dimens.xml文件中这样定义:
<dimen name="widget_margin">8dp</dimen>
res/values-v14/dimens.xml文件中这样定义(由于系统默认给我们加了,我们再添加)
<dimen name="widget_margin">0dp</dimen>
另外一个可选操作则是:为有margin的布局做一个.9.png图片,为没有margin的布局做一个.9.png图片。
使用AppWidgetProvider类
类AppWidgetProvider继承了BroadCastReceiver类,方便其接受来自挂件广播的事件。AppWidgetProvider只接受与挂件相关的广播事件,比如说:挂件更新了、被删除了、挂件变得可用、挂件不可用等。当这些事件发生了,下面的对应方法将会被调用:
onUpdate (Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
每当过了updatePeriodMillis设置的时间后,此方法被调用来更新挂件。当挂件被添加时,此方法也会被调用。因此,他在挂件被创建的时候会被执行,例如:如果需要,为View定义一个事件handler并启动一个临时Service。但是,如果你创建了一个configuration Activity,创建挂件的时候,此方法将不会被调用。当configuration Activity执行完毕后,此方法将被调用(进行第一次更新)
onAppWidgetOptionsChanged (Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)
此方法在被第一次放置的时候,或者挂件大小被改变后被调用。你可以使用此回调函数去基于挂件的尺码范围去展示或者隐藏内容,你可以通过调用getAppWidgetOptions(intappWidgetId)方法去获取到尺码范围,他的返回值是一个Bundle类型的对象,包含了下面的内容:
- AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
包含了当前挂件的最小宽度尺寸(使用dp为单位)。
- AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
包含了当前挂件的最小高度尺寸(使用dp为单位)。
- AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
包含了当前挂件的最大宽度尺寸(使用dp为单位)。
- AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
包含了当前挂件的最大高度尺寸(使用dp为单位)。
【需要注意的是:此API是在16引出的(android4.1以上)。】
onDeleted (Context context, int[] appWidgetIds)
此方法将在挂件被删除的时候调用
onEnabled (Context context)
在挂件对象实例第一次创建的时候调用。比如说:用户添加了两个你的实例,此方法也只被调用一次。有的事情只需要执行一次的:例如:创建数据库。可以再次进行。
onDisabled (Context context)
此方法将在你所有的挂件实例中的最后对象被删除后调用。用来为onEnable()做一些清除工作。
onReceive (Context context, Intent intent)
此方法是为broadcast所调用,并且优先于上面几个回调方法。一般你不需要实现此方法。因为APPWidgetProvider默认实现了这个方法。
最重要的APPWidgetprovider的回调方法是:onUpdate(),因为他在每次添加到宿主屏幕的时候都会调用(除非你使用了configuration Activity)。如果你的挂件需要接受一些用户的交互事件,那你需要在此会掉方法中注册事件的handler,如果你的挂件不用创建临时的文件或者数据库,或者执行其他的清理工作,那么onUpdate()方法也许就是你唯一需要实现的方法了。比如说:如果你想一个拥有button的挂件,当他被点击的时候你就启动一个Activity,那么你可以这么做。【当然,如果你不想启动Activity,你想出发某个时间,你可以使用PendingIntent来发送广播,广播接收器收到后,通过Intent的Action来执行不同的代码】
public class ExampleAppWidgetProvider extends AppWidgetProvider {public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {final int N = appWidgetIds.length;// Perform thisloop procedure for each App Widget that belongs to this providerfor (int i=0; i<N; i++) {int appWidgetId = appWidgetIds[i];// Create anIntent to launch ExampleActivityIntent intent = new Intent(context, ExampleActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);// Get the layoutfor the App Widget and attach an on-click listener// to the buttonRemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);views.setOnClickPendingIntent(R.id.button, pendingIntent);// Tell theAppWidgetManager to perform an update on the current app widgetappWidgetManager.updateAppWidget(appWidgetId, views);}}
}
上面的APPWidgetProvider就只是定义了onUpdate()方法,此方法用来定义一个启动Activity的PendingIntent,然后使用setOnclickPendingIntent(int,PendingIntent)绑定到挂件的Button上。
注意:上面的代码是在一个循环中,他们迭代 appWidgetIds数组的每个实例,每个实例都是一个挂件的ID,通过这种方式,如果用户创建了多个挂件实例(例如在主屏幕创建相同的两个挂件),他们全部都将更新,因为,他们使用的updatePeriodMillis使用的是同一个实例。比如说:如果刷新频率被设置为两个小时,同时,桌面上有两个相同的挂件,第二个挂件在第一个挂件出现的一个小时后添加的。他们最终的结果就是,一旦到了第一个挂件刷新时(也就是第一个挂件添加两个小时后),第二个挂件也一起刷新,但是到了第二个挂件刷新时,将不起任何作用(直接忽视了第二个挂件的周期,以第一个为准)
注意: |
由于APPWidgetProvider是扩展自BroadcastReceiver,因此,不能保证在回调方法返回后你的进程将继续保持运行(BroadcastReceiver的生命周期),如果你需要做一些时间比较长的任务,你最好在onUpdate()中开启一个Service,然后在Service中更新界面。例子见右边--》 |
接收Widget广播Intents
使用APPWidgetProvider仅仅是个便利的做法,如果你喜欢,你也可以将挂件直接继承BroadcastReceiver,或者复写APPWidgetProvider的onRecerve(Context,Intent)方法。有几个Intent你需要注意的:
- ACTION_APPWIDGET_UPDATE
- ACTION_APPWIDGET_DELETED
- ACTION_APPWIDGET_ENABLED
- ACTION_APPWIDGET_DISABLED
- ACTION_APPWIDGET_OPTIONS_CHANGED
创建挂件Configuration Activity
如果你想让用户在新添加你的挂件前先做一些设置。那么你可以创建一个Configuration Activity,此Activity将会在挂件添加前启动,以便于用户为挂件做设置,比如说:挂件颜色、挂件大小、挂件更新频率等。
Configuration Activity应该像一般的Activity一样在manifest中申明,只是需要加上
<actionandroid:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>以便接受Intent的过滤器而已。比如说:
<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter><action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/></intent-filter>
</activity>
同时,我们需要在APPWidgetProviderInfo的XML文件中的android:configure中添加上此Activity。比如:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"...android:configure="com.example.android.ExampleAppWidgetConfigure"... >
</appwidget-provider>
记得 android:configure 的值需要类的全称!
上面的内容就是你启动ConfigurationActivity需要做的所有内容。剩下的需要你做的事情就在这个实际的Activity中,当你在实现Activity的时候,下面有两点重要的事情你需要记住:
- 调用Configuration Activity后,Configuration Activity应该返回结果,结果中应该包含绑定的挂件的ID,将ID保存到Intent中的EXTRA_APPWIDGET_ID中。
- 如果你创建了Configuration Activity,挂件的onUpdate()方法将不会被调用(当Configuration Activity被启动的时候,系统将不会发送ACTION_APPWIDGET_UPDATE广播),请求APPWidgetManager更新挂件就将是Configuration Activity的职责。当然,以后onUpdate()方法会被正常调用。
从Configuration Activity更新挂件
当挂件使用了Configuration Activity,那么当Configuration Activity处理完毕后,第一次更新挂件将是Configuration Activity的职责。当然,你可以直接从APPWidgetManager请求更新。
下面有一些关于更新挂件和关闭ConfigurationActivity的程序的总结
1、首先通过Intent得到挂件的ID
Intent intent = getIntent();
Bundle extras =intent.getExtras();
if (extras !=null) {mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
}
2、执行你的Configuration Activity交互(一般是用户设置等)
3、当设置完毕,通过getInstance(Context)获取到APPWidgetManager对象
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
4、通过拥有布局的RemoteViews对象调用updateAPPWidget(int,RemoteViews)来更新挂件。
RemoteViews views = newRemoteViews(context.getPackageName(),R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
5、最后返回Intent、关闭Activity
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
小知识:
当你的ConfigurationActivity被创建时,如果你在最后返回的值是:RESULT_CANCELED(或者用户并没有与你的Configuration Activity有太多交互,而是直接结束了此Activity,也会返回这样的值)。那么挂件会接受到这样的事件,最终,他将不会被添加到宿主中。
设置预览图
Android 3.0引进了previewImage字段。他可以定义一个挂件的预览图,方便用户直接筛选,如果此字段没定义,那么系统将使用挂件的图片作为预览。
具体定义如下:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"...android:previewImage="@drawable/preview">
</appwidget-provider>
【为了帮助你为你的挂件创建挂件预览图,安卓模拟器包含了一个应用,叫做“Widget Preview”,启动这个应用,选择你的挂件,设置好后,“Widget Preview”将会给你生成预览图,这时候你保存下来,放到你的工程中做为资源图就好了】
带集合物件的挂件
Android 3.0引入了带集合物件的挂件,这些种类的挂件使用RemoteViewsService来将后台远程的数据展示出来,比如:来自content Provider的数据。使用RemoteViewsService提供数据,使用下面的View控件展示内容,我们称这样的View为: “collection views”
- ListView
在一个垂直方向滚动显示条目的列表。
- GridView
和ListView相似,只是在水平方向展示的条目多于1条(ListView为1条)。
- StackView
类似于将很多卡牌堆叠在一起的View(有点像名片盒),
- AdapterViewFlipper
一个由ViewAnimator做后台支撑的adapter,他可以在两个或多个View之间动画。但是同一时间只能有一个child可以被显示。
上面有说道:这些collectionView将显示来自其他地方的数据。这就意味着,他们要使用一个Adapter去绑定他们的数据到UI上,一个适配器绑定了一些条目,而这些条目是将一堆数据分别存放到分别的View对象中。
由于这些collectionview是基于adapter的,所有安卓框架必须要包含额外的建筑样式去支持这样的挂件。在挂件的上下文中,adapter被RemoteViewsFactory所取代,它仅仅对Adapter做了很小的包装,当其中一个条目被请求,那么RemoteViewsFactory将会创建并返回此条目(作为一个RemoteViews对象返回),如果你要包含collection View到你的挂件中,你必须要实现两个类:android.widget.RemoteViewsService和
android.widget.RemoteViewsService.RemoteViewsFactory |
RemoteViewsService是一个允许适配器“远程”请求RemoteViews对象的服务
RemoteViewsFactory是一个在collection View和其对应的数据的一个接口,
public class StackWidgetService extends RemoteViewsService {@Overridepublic RemoteViewsFactory onGetViewFactory(Intent intent) {return new StackRemoteViewsFactory(this.getApplicationContext(), intent);}
}class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {//... include adapter-likemethods here. See the StackView Widget sample.}
二、示例应用
此例子由10堆叠起来的View组成,展示的名称由0!到9!,此例子有下面一些行为:
- 可以像查看堆叠的相册一样,向下向上翻阅----使用的StackView
- 如果用户没与之交互,他会自己滚动。因为在xml文件中使用了android:atutoAdvanceViewId = "@id/stack_view"。
- 如果用户点击了其中的一个View,那么他将会吐丝:“你点击了 **”。其中**是根据你点击的索引不同而不同。
<service android:name="MyWidgetService"
...
android:permission="android.permission.BIND_REMOTEVIEWS" />
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><StackView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/stack_view"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:loopViews="true" /><TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/empty_view"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:background="@drawable/widget_item_background"android:textColor="#ffffff"android:textStyle="bold"android:text="@string/empty_view_text"android:textSize="20sp" />
</FrameLayout>
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {// update each ofthe app widgets with the remote adapterfor (int i = 0; i < appWidgetIds.length; ++i) {//Set up the intent that starts the StackViewService, which will//provide the views for this collection.Intent intent = new Intent(context, StackWidgetService.class);//Add the app widget ID to the intent extras.intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));//Instantiate the RemoteViews object for the app widget layout.RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);//Set up the RemoteViews object to use a RemoteViews adapter.//This adapter connects//to a RemoteViewsService through the specified intent.//This is how you populate the data.rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);//The empty view is displayed when the collection has no items.//It should be in the same layout used to instantiate the RemoteViews//object above.rv.setEmptyView(R.id.stack_view, R.id.empty_view);////Do additional processing specific to this app widget...//appWidgetManager.updateAppWidget(appWidgetIds[i], rv); }super.onUpdate(context, appWidgetManager, appWidgetIds);
}
class StackRemoteViewsFactory implements
RemoteViewsService.RemoteViewsFactory {private staticfinal intmCount =10;private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();private Context mContext;private int mAppWidgetId;public StackRemoteViewsFactory(Context context, Intent intent) {mContext =context;mAppWidgetId =intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);}public void onCreate() {//In onCreate() you setup any connections / cursors to your data source. Heavylifting,//for example downloading or creating content etc, should be deferred toonDataSetChanged()//or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.for(int i = 0; i < mCount; i++) {mWidgetItems.add(new WidgetItem(i + "!"));}...}
...
public RemoteViews getViewAt(int position){// Construct aremote views item based on the app widget item XML file,// and set thetext based on the position.RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);...// Return theremote views object.return rv;
}
- StackWidgetProvider(APPWidgetProvider的子类)创建一个PendingIntent,并拥有一个自定义action,叫做:TOAST_ACTION。
- 当用户点击了一个view,我们将会广播这个Intent。
- 这个广播将会传递给StackWidgetProvider的onReceive()方法,然后小挂件将会显示点击该View的Toast信息。每个item的数据是由RemoteViewsService的RemoteViewsFactory提供。
public class StackWidgetProvider extends AppWidgetProvider {public staticfinal String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION";public staticfinal String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM";...// Called whenthe BroadcastReceiver receives an Intent broadcast.// Checks to seewhether the intent's action is TOAST_ACTION. If it is, the app widget// displays aToast message for the current item.@Overridepublic void onReceive(Context context, Intent intent) {AppWidgetManager mgr = AppWidgetManager.getInstance(context);if(intent.getAction().equals(TOAST_ACTION)) {int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();}super.onReceive(context, intent);}@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {//update each of the app widgets with the remote adapterfor(int i = 0; i < appWidgetIds.length; ++i) {// Sets up the intent that points to the StackViewServicethat will// provide the views for this collection.Intent intent = new Intent(context, StackWidgetService.class);intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);// When intents are compared, the extras are ignored, so weneed to embed the extras// into the data so that the extras will not be ignored.intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);// The empty view is displayed when the collection has noitems. It should be a sibling// of the collection view.rv.setEmptyView(R.id.stack_view, R.id.empty_view);// This section makes it possible for items to haveindividualized behavior.// It does this by setting up a pending intent template.Individuals items of a collection// cannot set up their own pending intents. Instead, thecollection as a whole sets// up a pending intent template, and the individual itemsset a fillInIntent// to create unique behavior on an item-by-item basis.Intent toastIntent = new Intent(context, StackWidgetProvider.class);// Set the action for the intent.// When the user touches a particular view, it will havethe effect of// broadcasting TOAST_ACTION.toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,PendingIntent.FLAG_UPDATE_CURRENT);rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);appWidgetManager.updateAppWidget(appWidgetIds[i], rv);}super.onUpdate(context, appWidgetManager, appWidgetIds);}
}
class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {private staticfinal intmCount =10;private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();private Context mContext;private int mAppWidgetId;public StackRemoteViewsFactory(Context context, Intent intent) {mContext =context;mAppWidgetId =intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);}// Initialize thedata set.publicvoidonCreate(){// In onCreate() you set up any connections / cursors toyour data source. Heavy lifting,// for example downloading or creating content etc, shouldbe deferred to onDataSetChanged()// or getViewAt(). Taking more than 20 seconds in this callwill result in an ANR.for (int i = 0; i < mCount; i++) {mWidgetItems.add(new WidgetItem(i + "!"));}...}...//Given the position (index) of a WidgetItem in the array, use the item's textvalue in//combination with the app widget item XML file to construct a RemoteViewsobject.publicRemoteViews getViewAt(int position) {// position will always range from 0 to getCount() - 1.// Construct a RemoteViews item based on the app widgetitem XML file, and set the// text based on the position.RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);// Next, set a fill-intent, which will be used to fill inthe pending intent template// that is set on the collection view inStackWidgetProvider.Bundle extras = new Bundle();extras.putInt(StackWidgetProvider.EXTRA_ITEM, position);Intent fillInIntent = new Intent();fillInIntent.putExtras(extras);// Make it possible to distinguish the individual on-click// action of a given itemrv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);...// Return the RemoteViews object.return rv;}...}
【android开发】桌面小挂件( APP Widgets )相关推荐
- android窗口小挂件,Android小挂件(APP Widgets)设计指导
应用小挂件(也叫做窗口小挂件)在android1.5的时候被第一次引出,后来再android3.0和android3.1中得到了极大的发展,他们可以展示一些应用的常用信息或者一些相关的信息到桌面上,标 ...
- 【Android】开发桌面小插件(一)
转载自:http://www.eoeandroid.com/thread-71208-1-1.html 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget)是微小的应用程序视图 ...
- Android开发日志打卡APP(二)
Android开发日志打卡APP(二) 文章目录 Android开发日志打卡APP(二) 前言 开发过程 一.背景和标题 二.日志图标 三.日志弹框 前言 在之前的文章中,准备工作已经完成,现在我们将 ...
- Android开发详解之App升级程序一点通
Android开发详解之App升级程序一点通 结束语 UpdateManager.java import java.io.File; import java.io.FileOutputStream; ...
- Android开发日志打卡APP(一)
Android开发日志打卡APP(一) 文章目录 Android开发日志打卡APP(一) 简介 界面展示 内容总结 1.控件 2.布局 3.技术 开发过程 准备工作 启动页面 底部导航栏 简介 初 ...
- android开发 实现动态获得app的cpu占有率并导出文件的两种方法。
android开发 实现动态获得app的cpu占有率并导出文件的两种方法. 最近在做学校实验室的项目的时候,师兄要求我对app的性能进行评估,主要是从电量.cpu占有率.python模型的响应时间三者 ...
- (转)解决android开发人员,手机app图标显示不正确问题
(转)解决android开发人员,手机app图标显示不正确问题 参考文章: (1)(转)解决android开发人员,手机app图标显示不正确问题 (2)https://www.cnblogs.com/ ...
- Python Tkinter 实现桌面小挂件
许多朋友喜欢使用桌面便签功能,实际上就是一个桌面小挂件,类似上图所示的软件(图片来自网络) 类似的悬浮小挂件效果,实际上在Python最简单的GUI库Tkinter中就能实现.然而在Tkinter的官 ...
- android AppWidgetProvider开发桌面小工具
什么是桌面小工具 就是你写好代码后 双指缩放桌面屏幕(三星手机出现小组件,华为出现窗口小工具)点击之后会出现你继承AppWidgetProvider所编写的界面 效果: 代码: /** * Impl ...
最新文章
- Matlab与数据结构 -- 如何获取完整文件名
- Ubuntu共享WiFi(AP)给Android方法
- 计算机文件无法显示后缀,但一般情况下电脑默认是看不到文件格式显示(也就是文件扩展名)...
- OpenGL绘制二个不同颜色的三角形的实例
- ubuntu常见问题解决方法
- 每个施加在HTML元素上的Angular Directive,运行时都会生成一个新的实例
- ICCV 2019 | 基于全局类别表征的小样本学习
- 几种测量app启动时间的方式
- 自定义插入子串、删除子串、替换子串函数
- 「07」回归的诱惑:深入浅出逻辑回归
- scikit keras_使用Scikit-Learn,Scikit-Opt和Keras进行超参数优化
- 写出线程同步相关的方法,以银行账号存储款为例
- 自制Flash电子相册
- 如何分辨usb压枪芯片是无后座压枪还是键鼠模拟压枪
- 小马哥讲Spring核心编程思想 - 第二章 重新认识IoC
- PPT打印预览无背景
- matlab在编辑器中写完了怎么运行,在编辑器中运行测试
- 写给想做互联网产品经理的师弟师妹们一些话
- 计算机太极之光,且看今朝,刚柔并济,叱咤风云
- neighbors.kneighbors_graph的原理和应用
热门文章
- 【图论算法】深度优先搜索的应用
- 教你用一句代码简单实现雪景动画,不用任何元件
- 数据结构 - 线性表(顺序表)C语言代码实现-处理整型数据(附详细解释)。 _清风明月
- SAP FICO AFAB 科目要求一个成本会计分配(消息号 KI235)
- Excel(XLSX/XLS)文件内容导入WEB表单,纯前端无需服务器支持,开源Chrome扩展 v0.0.8,附下载,附github链接(2021/5/8更)
- iOS/swift之常用正则
- 101个python小代码 (6)设计一个重量转换器,输入以‘g’为单位的数字后返回换算成‘kg’的结果。
- 成都邮储信息技术岗实习面试准备
- css左侧投影_css3投影讲解、投影
- mysql导入数据load data infile用法