Android Launcher之AppWidgets(一)
2015-07-26 17:41
204 查看
了解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:
AnalogClock Button Chronometer ImageButton ImageView ProgressBar TextView ViewFlipper ListView GridView StackView AdapterViewFlipper
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 APP优化知识图谱
- 走进科学 WAF(Web Appllication Firewall)
- iOS弹幕基本实现及原理介绍
- Objective-C Properties 详解
- unity, 在surface shader中访问顶点色
- android 定义 程序 Scheme 接收特定URI开启Activity
- 【Unity NGUI游戏开发之二】TweenPosition位移动画(一):不相对于Anchor的位移动画
- iOS中获取UIWebView的各种信息
- Swift学习笔记(六)——变量的默认初始化值的问题
- Android 自定义ViewGroup(自定义布局容器)
- iOS 数组遍历删除元素的问题
- Android中的多线程编程(二)Handler的原理(附源码)
- Swift学习笔记(五)——在Background中启用控制台Console Output模式
- Objective-C设计模式——中介者Mediator(对象去耦)
- Android ListView简单实用
- android 沉浸式状态栏(2)
- MAC下Android Studio的安装
- Android中TextView文字居中与垂直靠左居中
- Android学习笔记_SQLite数据库存储
- iOS编程——Swift实现多段CAShapeLayer动画