发布时间:2016年6月26日 / 分类:App
widgets应该是android平台上最有用的特性之一了吧,将应用的数据利用一个小视图嵌入其他应用(如桌面),可以快速地获得app的状态而不用启动app。
最近用react-native写了一个todo app,感觉将计划利用widgets直接在桌面上展现出来会十分的方便,于是就给这个app加了widgets。
当然rn没有给我们提供这方面的支持,毕竟是android端的东西,只能直接写原生了。虽然很久没写过原生android了,但是看看文档还是没有问题的。
appwidgets基础
首先可以看看官方文档吧,虽然它里面讲的东西在Android studio上点击New->Widget->App widget就能帮你生成好,不过看看文档了解下配置参数和AppWidgetProvider的原理还是不错的。
由于我这里要用到ListView,还是要按照文档的内容进行一下修改,主要就下面的5个文件(还要在AndroidManifest.xml注册接收者和服务):
- TodoWidget: AppWidgetProvider的子类,用来生成和更新根view。
- TodoWidgetService: 用来生成ListView中每项视图的服务
- todo_widget.xml: 每项视图的layout文件
- todos_widget.xml: 根视图的layout文件
- todo_widget_info.xml: 配置文件
这些按照文档里的写就可以了,不过有点需要注意的是如果你选了最小宽度为4格,minWidth会给你填上250dp,实际在android上运行的时候这个widget的最小宽度是3格。我去github上看到一个开源app里appwidiget的配置文件里看到4格写的是294dp,试了下这个数值没什么问题就直接用了。
appwidgets里得到app的数据
appwidgets里使用的数据最好是持久化的,如果你想让widgets自动更新的话(widgets默认会有一个更新周期)。RN里提供了持久化api-AsyncStorage,那么原生app中怎么从中得到数据呢?
看看AsyncStorage的源码里的这条语句
// Use RocksDB if available, then SQLite, then file storage.
var RCTAsyncStorage = RCTAsyncRocksDBStorage || RCTAsyncSQLiteStorage || RCTAsyncFileStorage;
可见一般情况下在android平台上是通过SQLite进行存取的。
另外在源码的/ReactAndroid/src/main/java/com/facebook/react/modules/storage/ReactDatabaseSupplier.java中我们可以得到数据库名、表名和表中每列的名称。实际上AsyncStorage只是将k-v值直接保存在一张表里而已。
那么从数据库中读取数据应该是很简单了,只要在每次更新时进行读取就行了,也就是在RemoteViewsFactory的onCreate和onDataSetChanged中读取。(ps:你当然可以通过网络请求来获取数据,rn使用了okhttp+fresco,你可以在app中直接使用这两个库)
由于我们在rn中保存时一般是转成json字符串的,所以读取后还需要解析成对象才能使用,这里用一个你会熟悉的解析库就行了。(RN自带了一个jackson-core库,不过这个用起来不是那么方便,你可以再引入jackson-databind就会好用很多了)
另外有一点要注意的是,app在没有保存任何东西的情况下,AsyncStorage对应的表是不会创建的,这时候直接读取这张表肯定会报错。
app响应点击事件
设置点击事件的PendingIntent就按照文档的写法就可以了,如果要在rn中得到点击传递的参数的话,还要写一个原生模块来获取当前Activity的Intent对象并去除数据。
这个部分可以参考react-native-system-notification这个开源库里的写法,不过在点击时app是不会再前台运行的(目前来说是这样的),所以点击后只要startActivity并在Intent中带上FLAG_ACTIVITY_CLEAR_TOP这个flag就行了。然后在rn中通过原生模块获得Intent中带的数据。
具体完成的样子就是这样了

点击后的跳转
