Widget的简单使用详解
2016-11-09 15:27
399 查看
Widget是安卓的一个桌面小工具组件—窗口小部件,是微型应用程序视图,可以嵌入到其他应用程序(如主屏幕)和接收定期更新。
使用步骤:
1、创建一个类继承 AppWidgetProvider 并重写相应方法 默认实现了onReceive 方法。
2、在清单文件进行注册。
3、在res目录下新建xml文件夹 配置widget相关信息。
4、创建widget展示布局。
5、创建widget的配置文件(可选)。
6、更新数据。
大概就是上述的一些步骤。如果在添加的widget之前需要进行一些配置 则需要实现第5步。
第一步:创建类继承AppWidgetProvider
2、清单文件注册
3、创建配置信息 目录 res/xml/xxx.xml
以上这些信息并不是必须 是可以选择的。updatePeriodMillis的时间最小限定在了30分钟,对于有些app来说可能时间太久了,那么我们可以将updatePeriodMillis的时间设置为0,然后通过自定义service去更新widget。
Demo中的配置信息如下:
4、创建widget的展示布局,这个就根据设计,自己去码布局了。
Demo中布局文件内容如下:
就是简单的来个TextView来显示文案。
Demo中不需要对在添加widget进行配置所以不需要第5步。
这样简单的widget使用就完成了。
最后就是更新widget数据了,为了满足自定义时间可以对widget进行更新,我们采取使用service的方式进行。同样,创建类继承Service。
Demo中的service代码如下:
在service的oncreate方法中创建一个Timer定时器,这个定时器每个5秒钟就会执行updateWidget方法,在updateWidget方法里面实现了具体的更新widget的逻辑。通过RemoteViews去加载布局文件 在通过setTextViewText等方法实现对控件的控制。
这个地方使用了PendingIntent ,PendingIntent 其实和Intent效果是一样的,都是充当信使的作用,但是区别就是 PendingIntent 定义的事件是提前预知的,就是不知道事件什么时候发生,但是只要发生了,就要执行PendingIntent 定义的逻辑。
最后通过AppWidgetManager.getInstance(this).updateAppWidget(componentName, remoteViews);去更新widget组件的信息。
这样看,大概一个widget的功能就已经完成了,创建了,数据也可以更新了,但是有一个问题就是,我们使用的是service去更新widget,那么我们创建的service是一个后台进程,后台进程的优先级比较低,当手机内存不足的时候 就会优先kill掉这些进程。这个时候,widget在桌面上就不会得到更新了。那么怎么解决呢?
这里的思路是提升service进程的优先级,将其提升为前台进程,这样就可以最大程度的保证service不会被系统kill掉了。
在service的oncreate方法中创建一个通知,这样就将此service由后台进程变为前台进程了。代码如下:
在oncreate方法中调用此方法即可。 Demo源码中startForeground(1, notification); 第一个参数写的是0 这个是不行的 谷歌文档提示 must not be 0 , 写0之后就not working了。但是这样,当SDK<18时,通知栏不会显示该通知,当SDK>=18时,通知栏就会显示该通知 了。关于这个问题请移步到消除前台进程通知 本人未做实践,不过的确是一种思路。
这样基本就完成了widget的创建了。这个时候可能又会问了,万一此时service还是被kill了怎么办呢?
一种方式是我们重写service的onStartCommand方法 并返回START_STICKY,这样如果是系统自动将service kill了,在一段时间后 在内存比较充裕的心情下 系统会自动启动这个service的。
另外一种方式就是通过广播来监听此service,安卓系统提供了一个时间改变的广播接受者— ACTION_TIME_TICK 每隔一分钟 就会发送一个广播。由于此广播是系统级别的广播,所以不可以通过清单文件配置监听,只能通过代码动态的创建监听。
创建一个类继承BroadcastReceiver 并且重写onReceive 方法 ,这样每隔一分钟 onReceive方法就会执行一次,在onReceive方法中判断service是否已经在运行,如果没有在运行就去开启service,否则就上面操作都不需要做。
然后在需要的地方 动态的注册此广播:
这样 整个widget的功能就基本完善了。如有不足 请指正! 谢谢
我是源码
使用步骤:
1、创建一个类继承 AppWidgetProvider 并重写相应方法 默认实现了onReceive 方法。
2、在清单文件进行注册。
3、在res目录下新建xml文件夹 配置widget相关信息。
4、创建widget展示布局。
5、创建widget的配置文件(可选)。
6、更新数据。
大概就是上述的一些步骤。如果在添加的widget之前需要进行一些配置 则需要实现第5步。
第一步:创建类继承AppWidgetProvider
public class MyWidget extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); } /** * 第一个widget被添加调用 * @param context */ @Override public void onEnabled(Context context) { super.onEnabled(context); context.startService(new Intent(context, WidgetService.class)); } /** * widget被添加 || 更新时调用 * @param context */ @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); context.startService(new Intent(context, WidgetService.class)); } /** * 最后一个widget被删除时调用 * @param context */ @Override public void onDisabled(Context context) { super.onDisabled(context); context.stopService(new Intent(context, WidgetService.class)); } /** * widget被删除时调用 * @param context * @param appWidgetIds */ @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); } }
2、清单文件注册
<receiver android:name=".MyWidget"> <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" /> //widget的配置信息文件 </receiver>
3、创建配置信息 目录 res/xml/xxx.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp" //widget的最小宽度 android:minHeight="40dp"//widget的最小高度 android:updatePeriodMillis="86400000"//更新的频率 google规定最小时间为30分钟 android:previewImage="@drawable/preview" //widget的预览图 即在添加是看到的图片 一般为logo android:initialLayout="@layout/example_appwidget" // widget的布局文件 android:configure="com.example.android.ExampleAppWidgetConfigure" //widget配置activity android:resizeMode="horizontal|vertical" //widget是否可以水平 竖直缩放 android:widgetCategory="home_screen|keyguard"> //widget可以被添加主屏幕或者锁屏 </appwidget-provider>
以上这些信息并不是必须 是可以选择的。updatePeriodMillis的时间最小限定在了30分钟,对于有些app来说可能时间太久了,那么我们可以将updatePeriodMillis的时间设置为0,然后通过自定义service去更新widget。
Demo中的配置信息如下:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/example_appwidget" android:minHeight="80dp" android:minWidth="250dp" android:previewImage="@mipmap/ic_launcher" android:updatePeriodMillis="0" android:widgetCategory="home_screen|keyguard"> </appwidget-provider>
4、创建widget的展示布局,这个就根据设计,自己去码布局了。
Demo中布局文件内容如下:
<?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="horizontal"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/bg_icon" android:gravity="center_vertical" android:orientation="horizontal" android:paddingLeft="30dp" android:paddingRight="30dp"> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000" android:textSize="16sp" /> <TextView android:id="@+id/tv_money" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000" android:textSize="16sp" /> </LinearLayout> <Button android:id="@+id/btn_refound" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:gravity="right|center" android:text="点击" android:textColor="#000" android:textSize="16sp" /> </LinearLayout> </LinearLayout>
就是简单的来个TextView来显示文案。
Demo中不需要对在添加widget进行配置所以不需要第5步。
这样简单的widget使用就完成了。
最后就是更新widget数据了,为了满足自定义时间可以对widget进行更新,我们采取使用service的方式进行。同样,创建类继承Service。
Demo中的service代码如下:
public class WidgetService extends Service { private Timer mTimer; private SimpleDateFormat mFormat; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); mFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { updateWidget(WidgetService.this); } }, 0, 5000); } private void updateWidget(Context context) { RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.example_appwidget); long millis = System.currentTimeMillis(); String format = mFormat.format(new Date(millis)); remoteViews.setTextViewText(R.id.tv_date, "日 期:" + format); remoteViews.setTextViewText(R.id.tv_money, "毫秒值:" + millis); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); remoteViews.setOnClickPendingIntent(R.id.btn_refound, pendingIntent); ComponentName componentName = new ComponentName(this, MyWidget.class); AppWidgetManager.getInstance(this).updateAppWidget(componentName, remoteViews); } @Override public void onDestroy() { super.onDestroy(); mTimer.cancel(); mTimer = null; stopForeground(true); } }
在service的oncreate方法中创建一个Timer定时器,这个定时器每个5秒钟就会执行updateWidget方法,在updateWidget方法里面实现了具体的更新widget的逻辑。通过RemoteViews去加载布局文件 在通过setTextViewText等方法实现对控件的控制。
这个地方使用了PendingIntent ,PendingIntent 其实和Intent效果是一样的,都是充当信使的作用,但是区别就是 PendingIntent 定义的事件是提前预知的,就是不知道事件什么时候发生,但是只要发生了,就要执行PendingIntent 定义的逻辑。
最后通过AppWidgetManager.getInstance(this).updateAppWidget(componentName, remoteViews);去更新widget组件的信息。
这样看,大概一个widget的功能就已经完成了,创建了,数据也可以更新了,但是有一个问题就是,我们使用的是service去更新widget,那么我们创建的service是一个后台进程,后台进程的优先级比较低,当手机内存不足的时候 就会优先kill掉这些进程。这个时候,widget在桌面上就不会得到更新了。那么怎么解决呢?
这里的思路是提升service进程的优先级,将其提升为前台进程,这样就可以最大程度的保证service不会被系统kill掉了。
在service的oncreate方法中创建一个通知,这样就将此service由后台进程变为前台进程了。代码如下:
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void improvePriority() { PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, WidgetService.class), 0); Notification notification = new Notification.Builder(this) .setContentTitle("Foreground Service") .setContentText("Foreground Service Started.") .setSmallIcon(R.mipmap.ic_launcher).build(); notification.contentIntent = contentIntent; startForeground(1, notification); }
在oncreate方法中调用此方法即可。 Demo源码中startForeground(1, notification); 第一个参数写的是0 这个是不行的 谷歌文档提示 must not be 0 , 写0之后就not working了。但是这样,当SDK<18时,通知栏不会显示该通知,当SDK>=18时,通知栏就会显示该通知 了。关于这个问题请移步到消除前台进程通知 本人未做实践,不过的确是一种思路。
这样基本就完成了widget的创建了。这个时候可能又会问了,万一此时service还是被kill了怎么办呢?
一种方式是我们重写service的onStartCommand方法 并返回START_STICKY,这样如果是系统自动将service kill了,在一段时间后 在内存比较充裕的心情下 系统会自动启动这个service的。
@Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; }
另外一种方式就是通过广播来监听此service,安卓系统提供了一个时间改变的广播接受者— ACTION_TIME_TICK 每隔一分钟 就会发送一个广播。由于此广播是系统级别的广播,所以不可以通过清单文件配置监听,只能通过代码动态的创建监听。
创建一个类继承BroadcastReceiver 并且重写onReceive 方法 ,这样每隔一分钟 onReceive方法就会执行一次,在onReceive方法中判断service是否已经在运行,如果没有在运行就去开启service,否则就上面操作都不需要做。
public class WidgetBroadcastReceiver extends BroadcastReceiver { public static final String SERVICE_NAME = "com.ppdai.widgetdemo.WidgetService"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_TIME_TICK)) { if (!isServiceWork(context, SERVICE_NAME)) { context.startService(new Intent(context, WidgetService.class)); } } } /** * 判断service是否在运行 * @param mContext * @param serviceName * @return */ public boolean isServiceWork(Context mContext, String serviceName) { boolean isWork = false; ActivityManager myAM = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(100); if (myList.size() <= 0) { return false; } for (int i = 0; i < myList.size(); i++) { String mName = myList.get(i).service.getClassName().toString(); if (mName.equals(serviceName)) { isWork = true; break; } } return isWork; } }
然后在需要的地方 动态的注册此广播:
WidgetBroadcastReceiver mReceiver = new WidgetBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); registerReceiver(mReceiver, filter);
这样 整个widget的功能就基本完善了。如有不足 请指正! 谢谢
我是源码
相关文章推荐
- mdadm使用详解及RAID 5简单分析
- mdadm使用详解及RAID 5简单分析
- QTableWidget的简单使用
- IOS学习:UITableView使用详解3 分组表的简单使用
- Log4j 2.0在开发中的高级使用详解—配置简单的控制台输出(三)
- 详解Android中AsyncTask的使用(简单易懂)
- qt QDockWidget QStackWidget的简单使用
- 桌面小控件appwidget的简单使用
- QListWidget和QListWidgetItem的简单使用
- QListWidget和QListWidgetItem的简单使用
- 使用GTK与Glade创建一个简单的列表的图形详解
- 最简单实用的MongoDB安装教程:在CentOS中使用 yum 安装MongoDB及服务器端配置详解
- qt QDockWidget QStackWidget的简单使用
- QListWidget和QListWidgetItem的简单使用
- SVN使用教程简单配置篇详解
- 打造支持apk下载和html5缓存的 IIS(配合一个超简单的android APP使用)详解
- 最简单实用的MongoDB安装教程:在CentOS中使用 yum 安装MongoDB及服务器端配置详解
- 百度编辑器UEEDITOR使用简单介绍 UEditor表单提交和后台交互详解 最后更新对应的版本:1.2.5.1 教程描述: 富文本编辑器的使用开发中,表单提交有多种场景,编辑器初始化有新增文章和编辑
- Android APK反编译就这么简单 详解(附图)和使用AXMLPrinter2.jar批量反编译xml文件
- mdadm使用详解及RAID 5简单分析