Android Launcher之AppWidgets(一)
来源:互联网 发布:百年树人网络研修平台 编辑:程序博客网 时间:2024/06/08 03:21
了解AppWidget
学习资料:http://developer.android.com/guide/topics/appwidgets/index.html
基础
创建一个AppWidget,需要:
AppWidgetProviderInfo 对象 用于描述AppWidget的元数据,例如AppWidget的layout(布局),更新频率(update frequency),以及AppWidgetProvider类的,这些都应该定义在一个XML中.
AppWidgetProvider 类的实现 该类定义了AppWidget的一些基于广播事件的基本方法,用于提供AppWidget的编程接口,当App Widgets进行updated、enabled、disabled和deleted操作时可以通过这些基本方法来接收广播(Broadcasts)
View Layout 视图布局 视图布局用于定义AppWidget的初始布局,布局文件应建在工程文件夹的/res/layout目录下
此外,还可以定义一个App Widget configuration Activity,当用户创建Widget时可通过该Activity对App Widget 进行一些设置
在Android Manifest文件中声明一个App Widget
谷歌的开发文档中给的一个例子:
<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="ExampleAppWidgetProvider">: <receiver>必须要声明android:name属性,该属性用来说明AppWidget使用的AppWidgetProvider.class <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter>: <intent-filter>元素必须包含一个带android:name属性的<action>,例子中的这个属性表明 AppWidgetProvider可以接受 ACTION_APPWIDGET_UPDATE 广播 <meta-data android:name="android.appwidget.provider" android:resource="@xml/example_appwidget_info" />: <meat-data>用于描述AppWidgetProviderInfo的resource,该标签需要定义两个属性: android:name="android.appwidget.provider"用于将该数据识别成对AppWidgetProviderInfo的描述 android:resource="@xml/example_appwidget_info" 用于声明AppWidgetProviderInfo的本地资源 </receiver>-->
添加AppWidgetProviderInfo的Metadata(元数据)
在res/xml目录下创建一个XML文件,该文件用于定义AppWidget的一些基本属性:
<!--<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp":最小宽度 android:minHeight="40dp":最小高度 android:updatePeriodMillis="86400000":更新频率 android:previewImage="@drawable/preview":定义AppWidget的预览图片 android:initialLayout="@layout/example_appwidget":定义AppWidget使用的初始布局 android:configure="com.example.android.ExampleAppWidgetConfigure" :定义AppWidget的设置文件 android:resizeMode="horizontal|vertical":设置可以让用户调整插件大小,horizontal|vertical表示宽和高都可以调整 android:widgetCategory="home_screen":设置AppWidget是否可以显示在home_screen上或者lock_screen上 ></appwidget-provider>-->
创建AppWidget的布局
在res/layout目录下创建一个布局文件,用于设计AppWidget.由于AppWidget layout是基于RemoteViews类,只支持四个布局方式:
FrameLayout//框架布局LinearLayout//线性布局RelativeLayout//相对布局GridLayout//网格布局
只支持以下的AppWidget classes:
AnalogClockButtonChronometerImageButtonImageViewProgressBarTextViewViewFlipperListViewGridViewStackViewAdapterViewFlipper
AppWidget的箱模型:
给AppWidget添加Margins(边距)
1.设置app的targetSdkVersion版本,至少要14;可能是因为版本低的不支持.
2.创建一个引用了dimension resource来设置margins的布局:
<?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:padding="@dimen/widget_margin" > <ImageButton android:id="@+id/ImageButtonClick" android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" > </ImageButton></LinearLayout>
引用dimension resource: android:padding=”@dimen/widget_margin”
3.在两个地方创建dimensions resources,一个在res/values
目录下,另一个在res/values-14
目录下
res/values/dimens.xml
用于自定义AppWidget的margins(边距):
<dimen name="widget_margin">8dp</dimen>
res/values-14/dimens.xml
用于设置值为0 的margins的AppWidget:
<dimen name="widget_margin">0dp</dimen>
使用AppWidgetProvider类
AppWidgetProvider
类继承自BroadcastReceiver
类,使用该类便于处理AppWidget broadcasts,AppWidgetProvider只接收和AppWidget有关的广播:updated,deleted,enabled,和disabled等.当这些广播事件发生时,AppWidgetProvider会触发相应的回调方法.
onUpdate()
onUpdate()
会根据设置的updatePeriodMillis
属性来间歇性更新AppWidget.当用户添加AppWidget时也会调用该方法,所以onUpdate()应该用来完成一些AppWidget的基本设置,例如定义Views的event handlers(事件处理器)、当需要时开启一个暂时性的服务.但是如果已经声明了一个configuration Activity,当用户添加AppWidget时候系统不会立即调用onUpdate(),而是通过调用创建的configuration Activity来执行第一次更新,而onUpdate()则在AppWidget的后续更新时才会被调用.
onAppWidgetOptionsChanged()
onAppWidgetOptionsChanged()
方法在两个情况下会被调用,一个是AppWidget第一次被添加的时候,另一个是当AppWidget大小被调整后.可以通过该方法显示或者隐藏AppWidget尺寸范围内的内容.可以调用getAppWidgetOptions()
方法来获取AppWidget的size rangs(大小范围),getAppWidgetOptions()
方法会返回一个Bundle(包),这个Bundle包含以下内容:
OPTION_APPWIDGET_MIN_WIDTH :包含AppWidget当前的最小宽度,单位为dp
OPTION_APPWIDGET_MIN_HEIGHT :包含AppWidget当前的最小高度,单位为dp
OPTION_APPWIDGET_MAX_WIDTH :包含AppWidget当前的最大宽度,单位为dp
OPTION_APPWIDGET_MAX_HEIGHT :包含AppWidget当前的最大高度,单位为dp
ps:这个方法在Android4.1版本以上才有
onDeleted(Context context, int[] appWidgetIds)
当AppWidget被删除时会被调用.
onEnabled(Context context)
用户在桌面添加第一个AppWidget调用该方法.第一个是指从无到有的过程,也就是说不管之前是否添加过,然后又删了.只要用户添加AppWidget的时候桌面上没有这个AppWidget,就会调用该方法.
onDisabled(Context context)
最后一个AppWidget被删除时调用该方法.可以重写这个方法以处理一些事情,比如清除使用痕迹,删除临时database.
onReceive(Context context, Intent intent)
上述几个方法调用之后都会调用这个方法,这个方法可以不用自己实现.AppWidgetProvider已经封装好.
之前写的一个例子:
package com.example.mylauncher;import android.annotation.SuppressLint;import android.annotation.TargetApi;import android.app.PendingIntent;import android.appwidget.AppWidgetManager;import android.appwidget.AppWidgetProvider;import android.content.Context;import android.content.Intent;import android.os.Build;import android.os.Bundle;import android.util.Log;import android.widget.RemoteViews;import android.widget.RemoteViews.RemoteView;public class myAppWidgetProvider extends AppWidgetProvider{ @Override public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { // TODO Auto-generated method stub super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); } @Override public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) { // TODO Auto-generated method stub super.onRestored(context, oldWidgetIds, newWidgetIds); } // receiver默认的消息接收重载 @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub super.onReceive(context, intent); Log.i("myLog", "onReceive"); } // 到了时间间隔的时候接收到的消息,appwidgetservice发过来的消息 @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // TODO Auto-generated method stub super.onUpdate(context, appWidgetManager, appWidgetIds); Log.i("myLog", "onUpdate"); // 更新远程的界面(launcher里的我的"所有的"appwidget) for (int i = 0; i < appWidgetIds.length; i++) { Log.i("myLog", i + "");// i+""这样子写会先把i转换成String然后再和后面的""拼接 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.layout_mywidget);// 远程视图 // views.setTextViewText(R.id.ImageButtonClick, // "myClick");//这行代码会造成widget不能正常更新而显示"Problem Loading Widget" views.setImageViewResource(R.id.ImageButtonClick, R.drawable.preview);// 设置Widget中ImageButton的图片 Log.i("myLog", "更新了图片"); // 加入事件:当点击widget时产生的 事件,这里设置的是跳转到mainActivity views.setOnClickPendingIntent(R.id.ImageButtonClick, PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0)); appWidgetManager.updateAppWidget(appWidgetIds[i], views);// 更新视图 } } // 删除一个appwidget得到消息 @Override public void onDeleted(Context context, int[] appWidgetIds) { // TODO Auto-generated method stub super.onDeleted(context, appWidgetIds); Log.i("myLog", "onDeleted"); Log.i("myLog","context:"+ context.toString()); } // 第一次add widget 的时候接收的消息 @Override public void onEnabled(Context context) { // TODO Auto-generated method stub super.onEnabled(context); Log.i("myLog", "onEnabled"); } // 最后一个app widget被删除的时候接收到的消息 @Override public void onDisabled(Context context) { // TODO Auto-generated method stub super.onDisabled(context); Log.i("myLog", "onDisabled"); }}
>ps:所有的AppWidget实例的更新频率只能由一个updatePeriodMills的控制,即被用户添加到桌面的AppWidget实例只能同时更新.原因是AppWidgetProvider继承自BroadcastReceiver,不能保证app的进程在上述的回调函数返回结果之后保持运行(可以看一下BroadcastReceiver的生命周期).解决此问题的途径是,考虑在onUpdate()中开启一个Service,在这个Service中实现AppWidget自己的update.而且不用担心AppWidgetProvider会因为ANR错误(Application Not Responding)而关闭.
在AppWidget中运行Service的例子:http://code.google.com/p/wiktionary-android/source/browse/trunk/Wiktionary/src/com/example/android/wiktionary/WordWidget.java
接收AppWidget广播的Intents
AppWidgetProvider
只是一个便利的类,如果想要直接接收AppWidget广播,可以自己写一个BroadcastReceiver
或者重写onReceive(Context,Intent)
方法.其中需要注意的Intent Action有:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
创建AppWidget Configuration Activity
在Android Manifest文件中声明
<!-- 注册一个AppWidgetConfiguration Activity --> <activity android:name=".myAppWidgetConfig" > <intent-filter > <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter> </activity>
在AppWidgetProviderInfo xml文件中声明android:configure属性:
注意两点:
1.AppWidget host每次调用configuration Activity时,configuration Activity要通过Intent extras返回一个包含AppWidget ID(EXTRA_APPWIDGET_ID
)的结果;
2.AppWidget被创建的时候,如果configuration Activity打开了的话onUpdate()
是不会被调用的,AppWidget第一次的更新将由configuration Activity去完成,而第二次以后才会调用onUpdate()
来更新AppWidget.
从configuration Activity中更新AppWidget
直接从AppWidgetManager请求更新:
1.从发起configuration Activity的Intent获取AppWidget ID
public class myAppWidgetConfig extends Activity{ private int mAppWidgetId=0; ...... @Override protected void onCreate(Bundle savedInstanceState) { ...... //从启动AppWidgetConfiguration的intent获取AppWidget ID Intent intent=getIntent(); Bundle extras=intent.getExtras(); if (extras!=null) { mAppWidgetId=extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); Log.i("myLog", "获取到ID:"+mAppWidgetId); } ...... }}
2.执行 AppWidget configuration
3.执行完AppWidget configuration之后,调用getInstance(Context)
方法获取一个AppWidgetManager实例
AppWidgetManager appWidgetManager=AppWidgetManager.getInstance(getBaseContext());//获取一个AppWidgetManager实例
4.调用updateAppWidget(int,Remoteviews)
用RemoteViews布局来更新AppWidget
RemoteViews views=new RemoteViews(getBaseContext().getPackageName(), R.layout.layout_mywidget);//通过RemoteViews视图更新AppWidget views.setOnClickPendingIntent(R.id.btnDrumToTestActivity, PendingIntent.getActivity(getBaseContext(), 0, new Intent(getBaseContext(), Test2Activity.class), 0));appWidgetManager.updateAppWidget(mAppWidgetId, views);
5.创建return Intent用于返回结果,关闭configuration Activity
Intent resultValue=new Intent();resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);setResult(RESULT_OK, resultValue);finish();
configuration Activity的一个例子:
http://wptrafficanalyzer.in/blog/android-app-widget-with-configuration-activity/
设置Preview Image
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:previewImage="@drawable/preview"></appwidget-provider>
20150726 By Xiong
- Android Launcher之AppWidgets(一)
- Android Launcher之AppWidgets(二)
- Android AppWidgets开发详解
- android M Launcher之LauncherModel (一)
- Android launcher - Launcher(一)
- AppWidgets
- Android Launcher浅析(一)
- Android之Launcher
- Android AppWidgets的原理和应用
- Android AppWidgets的简单说明与应用
- Android HomeScreen Launcher解析(一)
- android Launcher研究(一)
- Android launcher之总体分析
- Android基础之Launcher开发
- Android Launcher 之源码下载
- Android Launcher研究一(编译Android4.0 launcher 源码 上)
- android在eclipse上搭建环境---android launcher移植之(一)
- android 1.6 launcher研究之launcher的左右滑动
- Linux文件rwx属性
- NYOJ 73 比大小
- Code Forces 559 C. Gerald and Giant Chess(组合数学+dp)
- 音乐播放器
- Socket收发数据浅析
- Android Launcher之AppWidgets(一)
- 在没有谷歌的时候怎样获取Android源码
- 创建crontab后,注意环境变量问题
- java:Properties类的相关知识
- [网络流24题] 03 最小路径覆盖问题(有向无环图最小路径覆盖,网络最大流)
- OS 线程模型 及JVM线程模型的实现
- C++中inline内联函数
- px与dp
- eclipse工具的使用心得