android person类_把你的程序放到桌面——Android桌面部件Widget

312bfa49be5c0e334361fbf0e3a3d8eb.gif

Android 桌面小部件是我们经常看到的,比如时钟、天气、音乐播放器等等。
它可以让 App 的某些功能直接展示在桌面上,极大的增加了用户的关注度。

首先纠正一个误区:
当 App 的小部件被放到了桌面之后,并不代表你的 App 就可以一直在手机后台运行了。该被杀,它还是会被杀掉的。
所以如果你做小部件的目的是为了让程序常驻后台,那么你可以死心了。

但是!!!
虽然它还是能被杀掉,但是用户能看的见它了啊,用户可以点击就打开我们的 APP,所以还是很不错的。

Android 桌面小部件可以做什么?

小部件可以做什么呢?也就是我们需要实现什么功能。

  • 展示。每隔 N 秒/分钟,刷新一次数据;

  • 交互。点击操作 App 的数据;

  • 打开App。打开主页或指定页面。

这三个功能,大概就能满足我们绝大部分需求了吧。

实现桌面小部件需要什么?

如果你从来没有做过桌面部件,那肯定总是感觉有点慌,无从下手,毫无逻辑。
所以,实现它到底需要什么呢?

  • 先声明 Widget 的一些属性。在 res 新建 xml 文件夹,创建 appwidget-provider 标签的 xml 文件。

  • 创建桌面要显示的布局。 在 layout 创建 app_widget.xml。

  • 然后来管理 Widget 状态。实现一个继承 AppWidgetProvider 的类。

  • 最后在 AndroidManifest.xml 里,将 AppWidgetProvider类 和 xml属性 注册到一块。

  • 通常我们会加一个 Service 来控制 Widget 的更新时间,后面再讲为什么。

做完这些,如果不出错,就完成了桌面部件。
其实挺简单的,下面就让我们来看看具体的实现吧。

实现一个桌面计数器

先上效果图:

3355ee30607b73630b87079b3b810cc4.gif

1. 声明 Widget 的属性

在 res 新建 xml 文件夹,创建一个 app_widget.xml 的文件。
如果 res 下没有 xml 文件,则先创建。

app_widget.xml 内容如下:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"android:initialLayout="@layout/app_widget"android:minHeight="110dp"android:minWidth="110dp"android:previewImage="@mipmap/ic_launcher"android:resizeMode="horizontal|vertical"android:widgetCategory="home_screen|keyguard">

属性的注释在上面写的很清楚了,这里需要说两点。

  • 关于宽度和高度的数值定义是很有讲究的,在桌面其实是按照“格子”排列的。
    看 Google 给的图。上面我们代码定义 110dp 也就是说,它占了2*2的空间。

df6431e1218ef7ac188da9c6e259b95b.png
  • 第二点很重要。有个 updatePeriodMillis 属性,更新widget的时间间隔(ms)。
    官方给提供了小部件的自动更新时间,但是却给了限制,你更新的时间必须大于30分钟,如果小于30分钟,那默认就是30分钟。
    可以我们就是要5分钟更新啊,怎么办呢?
    所以就不能使用这个默认更新,我们要自己来通过发送广播控制更新时间,也就是一开始总步骤里面第4步,加一个 Service 来控制 Widget 的更新时间,这个在最后一步添加。

2. 创建布局文件

在 layout 创建 app_widget.xml 文件。

<?xml  version="1.0" encoding="utf-8"?>

这里要注意的就是 桌面部件并不支持 Android 所有的控件
支持的控件如下:

App Widget支持的布局:

3. 管理 Widget 状态

这里代码看起来可能有点多,先听我讲几个逻辑,再来看代码。

  • Android 的各种东西都有自己的生命周期,Widget 也不例外,它有几个方法来管理自己的生命周期。

d9c1854498ebd9daa4f22ca38cd7232c.png
  • 同一个小部件是可以添加多次的,所以更新控件的时候,要把所有的都更新。

  • onReceive() 用来接收广播,它并不在生命周期里。但是,其实 onReceive() 是掌控生命周期的。
    如下是 onReceive() 父类的源码,右边是每个广播对应的方法。
    上面我画的生命周期的图,也比较清楚。

66c844d431a10cb7575394cf85231fc0.png

然后我们再来看代码。
新建一个 WidgetProvider 类,继承 AppWidgetProvider。
主要逻辑在 onReceive() 里,其他的都是生命周期切换时,所处理的事情。
我们在下面分析 onReceive()。

public 
onReceive(Context context, Intent intent)

它传了两个值回来,Context 是跳转、发广播用的。
我们用来判断的是 Intent ,这里用到了 Intent 的两种方式。

Intent 作为信息传递者。
它要把信息传给谁,可以有三个匹配依据:一个是action,一个是category,一个是data。

String ACTION_UPDATE_ALL = "com.lyl.widget.UPDATE_ALL";
这个最后会在 AndroidManifest.xml 里面注册时写进去。
当每隔 N 秒/分钟,就发送一次这个广播,更新所有UI。

intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)
是广播事件里携带的 Intent 里设置的,用来匹配。
点击“恢复”按钮,计数器清零。

然后是 updateAllAppWidgets() 这个方法,更新 UI。
更新 UI 用到了一个新东西——RemoteViews。

怎么来理解 RemoteViews 呢?
因为,桌面部件并不像平常布局直接展示,它需要通过某种服务去更新UI。但是我们的App怎么能去控制桌面上的布局呢?
所以就需要有一个中间人,类似传递者。
我告诉传递者,你让他把我的 R.id.widget_txt ,更新成 “hello world”。
你让他把我的 R.id.widget_btn_open 按钮点击之后去响应 PendingIntent 这件事。
RemoteViews 就是承担着一个这样的角色。

然后再去理解代码,是不是稍微好一点了?

4. 最后就是 Service 控制 Widget 的更新时间

说好的 当每隔 N 秒/分钟,就发送一次这个广播。
那到底在哪发呢?也就是我们刚开始说的,用 Service 来控制时间。

新建一个 WidgetService 类,继承 Service。代码如下:

/**
 * 控制 桌面小部件 更新
 */

在 onCreate 开启一个计时线程,每1秒发送一个广播,广播就是我们自己定义的类型。

5. 在 AndroidManifest.xml 注册 桌面部件 和 服务

然后就只剩最后一步了,注册相关信息

相应的注释都在上面,如果我们的App进程被杀掉,服务也被关掉,那就没办法更新UI了。
也可以再创建一个 BroadcastReceiver 监听系统的各种动态,来唤醒我们的通知服务,这就属于进程保活了。

至此,以上代码写完,如果不出问题,运行之后直接去桌面看小工具,我们的App就在里面了,可以添加到桌面。

对于需要定时更新的桌面部件,保证自己的服务在后台运行也是一件比较重要的事情。
这个我们还是可以好好做一下,毕竟用户都已经愿意把我们的程序放到桌面上,所以只要友好的引导用户给你一定的权限,存活概率还是很大。
再不济,让用户主动点开App,也不失为一种办法。

好的创意才能造就好的App,代码只是实现。

最后放上项目地址:
https://github.com/Wing-Li/Widget

大家都在看

GitHub 上优质项目整理,不只 Android

Retrofit结合Lifecycle, 将Http生命周期管理到极致

关于 Java 的静态工厂方法,看这一篇就够了!

打开Flutter动画的另一种姿势——Flare

欢迎前往安卓巴士博客区投稿,技术成长于分享

期待巴友留言,共同探讨学习

1ba93369e08da4b6f502b3c713fc564a.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值