Android 之窗口小部件高级篇--App Widget 之 RemoteViews - 跨到对岸去
在之前的一篇博文( Android 之窗口小部件详解--App Widge t)中,已经介绍了App Widget的基本用法和简单实例。这篇主要讲解 App Widget 的高级内容,即通过 RemoteViews 去管理Widget的中GridView、ListView、StackView等内容。在学习本篇之前,建议读者先掌握 App Widget 的基本知识。
1 RemoteViews等相关类的介绍
下面先简单介绍RemoteViews、RemoteViewsService、RemoteViewsFactory。
1.1 RemoteViews
顾名思义,它是一个远程视图。App Widget中的视图,都是通过RemoteViews表现的。
在RemoteViews的构造函数中,通过传入layout文件的id来获取 “layout文件对应的视图(RemoteViews)”;然后,调用RemoteViews中的方法能对layout中的组件进行设置(例如,可以调用setTextViewText()来设置TextView组件的文本,可以调用setOnClickPendingIntent() 来设置Button的点击响应事件)。
因此,我们可以将 “RemoteViews 看作是 layout文件中所包含的全部视图的集合”。
1.2 RemoteViewsService
RemoteViewsService,是管理RemoteViews的服务。
一般,当App Widget 中包含“GridView、ListView、StackView等”集合视图时,才需要使用RemoteViewsService来进行更新、管理。(集合视图是指GridView、ListView、StackView等包含子元素的视图)
RemoteViewsService更新“集合视图”的一般步骤是:
(01) 通过setRemoteAdapter来设置 “RemoteViews对应RemoteViewsService”。
(02) 之后在RemoteViewsService中,实现RemoteViewsFactory接口。然后,在RemoteViewsFactory接口中对“集合视图”的各个子项进行设置(“集合视图”的各个子项:例如,GridView的每一个格子都是一个子项;ListView中的每一列也是一个子项)。
因此,我们可以将 “RemoteViewsService 看作是 管理layout中集合视图的服务”。
1.3 RemoteViewsFactory
通过RemoteViewsService中的介绍,我们可以了解“RemoteViewsService是通过RemoteViewsFactory来具体管理layout中集合视图的”,即“RemoteViewsFactory管理集合视图的实施者”。
RemoteViewsFactory是RemoteViewsService中的一个接口。RemoteViewsFactory提供了一系列的方法管理“集合视图”中的每一项。例如:
(01)RemoteViews getViewAt(int position)
通过getViewAt()来获取“集合视图”中的第position项的视图,视图是以RemoteViews的对象返回的。
(02)int getCount()
通过getCount()来获取“集合视图”中所有子项的总数。
因此,我们可以将 “RemoteViewsFactory 看作是 layout中集合视图管理的具体实施者”。
2 实例介绍
实现一个App Widget,App Widget可缩放,且包含“3个组成部分”。
第1部分:是一个TextView文本,标题内容是“Sky Wang”。
第2部分:是一个Button按钮。点击按钮,会弹出一个Toast提示框,提示响应了Button点击事件。
第3部分:是一个GridView视图。GridView的每一个格子包含“图片”和“文本”两部分数据。点击GridView中的每一个格子,会弹出响应的提示语。
manifest代码如下 :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.skywang.test" 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" > <receiver android:name=".GridWidgetProvider"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <!-- GridWidgetProvider接收点击gridview的响应事件 --> <action android:name="com.skywang.test.COLLECTION_VIEW_ACTION" /> <!-- GridWidgetProvider接收点击bt_refresh的响应事件 --> <action android:name="com.skywang.test.BT_REFRESH_ACTION" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_provider"/> </receiver> <service android:name=".GridWidgetService" android:permission="android.permission.BIND_REMOTEVIEWS" /> </application> </manifest>
说明:
(01) GridWidgetProvider.java 是 AppWidgetProvider的继承类 ,而且AppWidgetProvider的配置文件是 widget_provider.xml 。
(02) GridWidgetProvider.java 除了 响应 App Widget 的更新事件 (android.appwidget.action.APPWIDGET_UPDATE)之外;
也会“ 响应App Widget包含的GridView的点击事件 (com.skywang.test.COLLECTION_VIEW_ACTION) ” 和“ App Widget包含的按钮的点击事件 (com.skywang.test.BT_REFRESH_ACTION) ”。
(03) GridWidgetService.java 是 RemoteViewsService的继承类 。
widget_provider.xml代码如下 :
widget_provider.xml是AppWidgetProvider对应配置文件
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="180dp" android:minHeight="180dp" android:previewImage="@drawable/preview" android:initialLayout="@layout/widget_layout" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen"> </appwidget-provider>
说明:
(01) android:minWidth="180dp" 表明 " Widget支持的最小宽度是3格 "
(02) android:minHeight="180dp" 表明 " Widget支持的最小高度是3格 "
(03) android:initialLayout="@layout/widget_layout" 表明 " Widget对应的布局文件是widget_layout.xml "
(04) android:previewImage="@drawable/preview" 表明 " Widget对应的预览图片是preview.png "
(05) android:resizeMode="horizontal|vertical" 表明 " Widget支持水平和竖直伸缩 "
(06) android:widgetCategory="home_screen" 表明 " Widget只能添加到桌面上,而不能添加到锁屏界面上 "
widget_layout.xml代码如下 :
widget_layout.xml是App Widget的布局文件
<?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" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dip" android:layout_marginRight="8dip" > <TextView android:id="@+id/tv_head" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:gravity="left|center_vertical" android:textSize="24sp" android:text="SkyWang" /> <Button android:id="@+id/bt_refresh" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:gravity="right|center_vertical" android:textSize="18sp" android:text="Refresh" /> </RelativeLayout> <GridView android:id="@+id/gridview" android:layout_width="match_parent" android:layout_height="match_parent" android:numColumns="auto_fit" android:verticalSpacing="4dip" android:horizontalSpacing="4dip" android:columnWidth="80dip" android:gravity="center" /> </LinearLayout>
GridWidgetProvider.java代码如下 :
GridWidgetProvider.java是AppWidgetProvider的继承类
package com.skywang.test;import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.RemoteViews; import android.widget.Toast; import com.skywang.test.R; /** * @desc App Widget高级功能测试程序 * @author skywang * */ public class GridWidgetProvider extends AppWidgetProvider { private static final String TAG = "SKYWANG"; public static final String BT_REFRESH_ACTION = "com.skywang.test.BT_REFRESH_ACTION"; public static final String COLLECTION_VIEW_ACTION = "com.skywang.test.COLLECTION_VIEW_ACTION"; public static final String COLLECTION_VIEW_EXTRA = "com.skywang.test.COLLECTION_VIEW_EXTRA"; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.d(TAG, "GridWidgetProvider onUpdate"); for (int appWidgetId:appWidgetIds) { // 获取AppWidget对应的视图 RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout); // 设置响应 “按钮(bt_refresh)” 的intent Intent btIntent = new Intent().setAction(BT_REFRESH_ACTION); PendingIntent btPendingIntent = PendingIntent.getBroadcast(context, 0, btIntent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setOnClickPendingIntent(R.id.bt_refresh, btPendingIntent); // 设置 “GridView(gridview)” 的adapter。 // (01) intent: 对应启动 GridWidgetService(RemoteViewsService) 的intent // (02) setRemoteAdapter: 设置 gridview的适配器 // 通过setRemoteAdapter将gridview和GridWidgetService关联起来, // 以达到通过 GridWidgetService 更新 gridview 的目的 Intent serviceIntent = new Intent(context, GridWidgetService.class); rv.setRemoteAdapter(R.id.gridview, serviceIntent); // 设置响应 “GridView(gridview)” 的intent模板 // 说明:“集合控件(如GridView、ListView、StackView等)”中包含很多子元素,如GridView包含很多格子。 // 它们不能像普通的按钮一样通过 setOnClickPendingIntent 设置点击事件,必须先通过两步。 // (01) 通过 setPendingIntentTemplate 设置 “intent模板”,这是比不可少的! // (02) 然后在处理该“集合控件”的RemoteViewsFactory类的getViewAt()接口中 通过 setOnClickFillInIntent 设置“集合控件的某一项的数据” Intent gridIntent = new Intent(); gridIntent.setAction(COLLECTION_VIEW_ACTION); gridIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, gridIntent, PendingIntent.FLAG_UPDATE_CURRENT); // 设置intent模板 rv.setPendingIntentTemplate(R.id.gridview, pendingIntent); // 调用集合管理器对集合进行更新 appWidgetManager.updateAppWidget(appWidgetId, rv); } super.onUpdate(context, appWidgetManager, appWidgetIds); } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); Log.d(TAG, "GridWidgetProvider onReceive : "+intent.getAction()); if (action.equals(COLLECTION_VIEW_ACTION)) { // 接受“gridview”的点击事件的广播 int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); int viewIndex = intent.getIntExtra(COLLECTION_VIEW_EXTRA, 0); Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show(); } else if (action.equals(BT_REFRESH_ACTION)) { // 接受“bt_refresh”的点击事件的广播 Toast.makeText(context, "Click Button", Toast.LENGTH_SHORT).show(); } super.onReceive(context, intent); } }
说明:
(01) RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
通过上面的语句, 获取widget_layout.xml对应的 RemoteViews ;进而通过RemoteViews对 widget_layout.xml中的各个元素进行管理。
(02) rv.setOnClickPendingIntent(R.id.bt_refresh, btPendingIntent);
通过上面的语句,设置 点击“按钮(bt_refresh)”时会触发的Intent 。从而对按钮点击事件进行处理。
(03) rv.setRemoteAdapter(R.id.gridview, serviceIntent);
通过上面的语句, 设置 “GridView(gridview)” 的远程适配器 ,serviceIntent是 GridWidgetService 的Intent。
从而在通过GridWidgetService对gridview进行管理。
(04) PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, gridIntent, PendingIntent.FLAG_UPDATE_CURRENT);
通过上面的语句,设置响应 “GridView(gridview)” 点击事件的 intent模板 。关于“Intent模板”,后面在" 关于'GridView'的点击事件 "会详细说明。
(05) if (action.equals(COLLECTION_VIEW_ACTION)) ...
通过上面的语句,响应“GridView”的点击事件。
(06) if (action.equals(BT_REFRESH_ACTION)) ...
通过上面的语句,响应“按钮(bt_refresh)”的点击事件。
关于 “GridView”的点击事件。这里详细说明以下!
像GridView这样的集合控件,不能单单像按钮一样,通过setOnClickPendingIntent() 来设置它的点击事件的Intent;而要通过以下两步来进行。
第一,设置GridView的点击响应事件的“Intent模板”。
这是通过 setPendingIntentTemplate() 来进行设置的。
这样做的目的有两个:首先, 设置Intent模板。 因为GridView有许多子项,它们这些子项都 统一 的要响应父亲的 Intent模板 。其次, 传递附件参数(例如,App Widget的ID) ,因为App Widget可以设置许多widget,每一个Widget的ID都不同,而且它们显示的内容可能不同(例如,不同大小的Widget显示不同大小的文字)。
第二, 设置GridView子项的点击响应事件的Intent。
设置通过 setOnClickFillInIntent() 来进行设置的。
这样做的首要目的,是 设置 GridView的子项所包含的信息 (例如,点击的 GridView子项的索引值 )。
通过这两步的设置之后,点击GridView中具体每一项的所产生的事件就是 “第一”和“第二”步中Intent的合集(组合) 。也就是说, 点击“GridView中某一个子项”所产生的Intent,同时包含了“通过 setPendingIntentTemplate 传递的Intent数据”和“通过 setOnClickFillInIntent 传递的Intent数据” 。 理解这一点对理解这个RemoteView的原理至关重要!!!
GridWidgetService.java的代码如下:
package com.skywang.test;import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent; import android.widget.RemoteViews; import android.widget.RemoteViewsService; import android.util.Log; import java.util.List; import java.util.ArrayList; import java.util.HashMap; public class GridWidgetService extends RemoteViewsService{ private static final String TAG = "SKYWANG"; @Override public RemoteViewsService.RemoteViewsFactory onGetViewFactory(Intent intent) { Log.d(TAG, "GridWidgetService"); return new GridRemoteViewsFactory(this, intent); } private class GridRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private Context mContext; private int mAppWidgetId; private String IMAGE_ITEM = "imgage_item"; private String TEXT_ITEM = "text_item"; private ArrayList<HashMap<String, Object>> data ; private String[] arrText = new String[]{ "Picture 1", "Picture 2", "Picture 3", "Picture 4", "Picture 5", "Picture 6", "Picture 7", "Picture 8", "Picture 9" }; private int[] arrImages=new int[]{ R.drawable.p1, R.drawable.p2, R.drawable.p3, R.drawable.p4, R.drawable.p5, R.drawable.p6, R.drawable.p7, R.drawable.p8, R.drawable.p9 }; /** * 构造GridRemoteViewsFactory * @author skywang */ public GridRemoteViewsFactory(Context context, Intent intent) { mContext = context; mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); Log.d(TAG, "GridRemoteViewsFactory mAppWidgetId:"+mAppWidgetId); } @Override public RemoteViews getViewAt(int position) { HashMap<String, Object> map; Log.d(TAG, "GridRemoteViewsFactory getViewAt:"+position); // 获取 grid_view_item.xml 对应的RemoteViews RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.grid_view_item); // 设置 第position位的“视图”的数据 map = (HashMap<String, Object>) data.get(position); rv.setImageViewResource(R.id.itemImage, ((Integer)map.get(IMAGE_ITEM)).intValue()); rv.setTextViewText(R.id.itemText, (String)map.get(TEXT_ITEM)); // 设置 第position位的“视图”对应的响应事件 Intent fillInIntent = new Intent(); fillInIntent.putExtra(GridWidgetProvider.COLLECTION_VIEW_EXTRA, position); rv.setOnClickFillInIntent(R.id.itemLayout, fillInIntent); return rv; } /** * 初始化GridView的数据 * @author skywang */ private void initGridViewData() { data = new ArrayList<HashMap<String, Object>>(); for (int i=0; i<9; i++) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put(IMAGE_ITEM, arrImages[i]); map.put(TEXT_ITEM, arrText[i]); data.add(map); } } @Override public void onCreate() { Log.d(TAG, "onCreate"); // 初始化“集合视图”中的数据 initGridViewData(); } @Override public int getCount() { // 返回“集合视图”中的数据的总数 return data.size(); } @Override public long getItemId(int position) { // 返回当前项在“集合视图”中的位置 return position; } @Override public RemoteViews getLoadingView() { return null; } @Override public int getViewTypeCount() { // 只有一类 GridView return 1; } @Override public boolean hasStableIds() { return true; } @Override public void onDataSetChanged() { } @Override public void onDestroy() { data.clear(); } } }
说明:
(01) public RemoteViewsService.RemoteViewsFactory onGetViewFactory(Intent intent) ...
通过上面的语句,返回一个RemoteViewsFactory对象。
RemoteViewsService是一个服务,通过之前对RemoteViewsService的介绍。我们知道,它 管理layout中集合视图的服务。
RemoteViewsService管理layout中集合视图的服务,是通过 RemoteViewsFactory 实现的;那么它是如何实现的呢?
RemoteViewsService是“通过 onGetViewFactory() 接口返回一个 RemoteViewsFactory 对象” 来实现的。
(02) public void onCreate() ...
在onCreate()中进行 RemoteViewsFactory 的初始化工作 。
(03) public int getCount() ...
通过getCount()返回 “集合视图”中的数据项的总数。
(04) public RemoteViews getViewAt(int position) ...
通过上面的语句,返回 一个“集合视图”中具体每一项的视图。
getViewAt()是非常重要的函数! 我们对"集合视图"中每一项的初始化都是在getViewAt()中进行设置的 。
grid_view_item.xml的代码如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/itemLayout" android:layout_height="match_parent" android:layout_width="match_parent"> <ImageView android:id="@+id/itemImage" android:layout_width="80dip" android:layout_height="60dip" android:layout_centerHorizontal="true" android:scaleType="fitXY" /> <TextView android:id="@+id/itemText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/itemImage" android:layout_centerHorizontal="true" android:text="TextView01"/> </RelativeLayout>
点击下载: 源代码
点击查看更多内容:
1. Android 之窗口小部件详解--App Widget
2. sky wang博客索引
实例效果图:
转载于:https://www.cnblogs.com/Free-Thinker/p/4422415.html
Android 之窗口小部件高级篇--App Widget 之 RemoteViews - 跨到对岸去相关推荐
- Android 之窗口小部件详解--App Widget
1 App Widget简介 App Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新.你可以通过一个App Widge ...
- Android -窗口小部件开发(App Widgets) 3部分
原文地址:http://blog.csdn.net/iefreer/article/details/4626274. (一) 应用程序窗口小部件App Widgets 应用程序窗口小部件(Widget ...
- Android应用小工具(窗口小部件)
Widget是可以在其他应用程序被嵌入和接收定期更新的微型应用程序视图. 在创建一个应用程序窗口小部件,需要满足以下条件: AppWidgetProviderInfo--描述元数据为应用窗口小部件,如 ...
- android学习笔记---61_Widgets,窗口小部件的制作...
2013/5/20 61_Widgets ------------------ Java技术qq交流群:JavaDream:2515720721.Widgets就是指窗口小部件,就是浮动在窗口中的部件 ...
- Android 如何预置桌面上的应用程序图标、快捷方式图标或者窗口小部件?
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- html 添加窗口小部件,如何:为自定义窗口小部件定义主题(样式)项
波斯汪 是的,有一种方法:假设您有一个小部件的属性声明(在中attrs.xml): 声明用于样式参考的属性(在中attrs.xml): 为小部件声明一组默认属性值(在中styles.xml ...
- 桌面窗口小部件添加List
桌面窗口小部件添加List @Author GQ 2018年07月08日 练习使用添加窗口小部件功能 长按桌面-添加窗口小部件-找到Flag-拖动到屏幕 效果图 流程 创建 MyAppWidgetPr ...
- Unity3D Editor 编辑器扩展2 选取物体、撤销操作和窗口小部件的显示
环境:Unity2017.2 语言:C# 总起: 今天主要介绍以下内容: 1.通过MenuCommand.Selection获取选中对象: 2.通过Undo编写可撤销的命令: 3.窗口小部件的显示. ...
- python设置窗口焦点_python – 当窗口小部件失去焦点时,我如何拦截
我有一个QPlainTextEdit,并希望在失去焦点时处理内容.我已经看到我可以使用focusChanged事件或使用focusOutEvent虚拟功能执行此操作. 我不知道如何使用新语法传递参数( ...
最新文章
- mysql第四章_MySQL必知必会--第二章~第四章--MySQL简介
- AJAX的post请求与上传文件
- UA MATH563 概率论的数学基础 鞅论初步1 条件期望
- https 页面中引入 http 资源的解决方式
- slack通知本地服务器_通过构建自己的Slack App学习无服务器
- 工作32:get之前打印
- 靶形数独(信息学奥赛一本通-T1447)
- Navicat for oracle 提示 cannot load oci dll,193的解决方法
- java并发编程(8)-- 线程 阻塞队列 生产者消费者 lock synchronized
- matlab求椭圆周长,用matlab计算椭圆周长及牛顿迭代的matlab完成
- 学习手册--没必要装模做样,只有你自己最清楚
- java Workbook接口 提供的方法
- 服务器内存延迟,内存、延迟等性能全面测试
- 经纬财富:吴忠白银投资五种方式
- 服务器虚拟地址是,两台服务器对外虚拟地址
- smartq ten3 android4,智器(SmartQ)Ten3 MID视频测试评测-ZOL中关村在线
- linux网络编程一
- 【LVS+Keepalived】 LVS+Keepalived实现tcp、udp负载均衡及HA高可用
- 垃圾的Android-ListView-setEmptyView
- 手机文件夹与电脑文件夹实时同步
热门文章
- 图:DFS(深度优先搜索)图解分析代码实现
- mysql逆向工程_Mybatis+Mysql逆向工程
- 【实验】DHCP、NAT配置案例
- 如何合理使用 CPU 管理策略,提升容器性能?
- 2020双11,Dubbo3.0 在考拉的超大规模实践
- 从函数计算架构看 Serverless 的演进与思考
- 戴尔硬盘保护增强套件_拆解戴尔服务器,看看内部构造与普通计算机的区别
- 计算机领域认知个人陈述,计算机专业个人陈述十九
- fetch 自动加cookie_如何在shell中动态获取chrome浏览器的cookie信息
- python2.x和3.x为什么不兼容_Python中使用AES算法(解决Python2.x和3.x下运行不兼容问题)...