jjzjj

Android 桌面小组件 AppWidgetProvider

她叫我小渝 2023-07-25 原文

废话

桌面小组件,绝对是小程序中的小程序,说白了就是任何复杂一丁点的操作都不适合做成桌面小组件。

所以这里采用的演示的例子,就只有一个白色圆角背景,外加一个文本框,显示文字。

小组件的教程网上一搜一大堆,所以我这里主要就是介绍一些坑的地方,跟大致处理流程,具体细节还得看其他大神的骚操作。

预览图

注意事项

1、UI 适配

小组件的宽高是可以支持用户自行调整的,只需简单的设置最低宽高,但是可调整的最小粒度是根据手机的 icon 为标准,这样就会导致一个比较难处理的点。

如果手机是 4x 布局的,即一行可以显示 4 个 APP 图标,那调节的粒度就是 90dp(理想情况下),实际情况的话,还得考虑小组件的固定边距,这个边距,不同牌子的手机可能还不一样。

如果手机是 5x 布局的,即一行可以显示 5 个 APP 图标……

解决方案:小组件数量无限制,用户也是用就加不用就不加,所以解决方案就简单粗暴一点,你能想到的适配尺寸,每种尺寸搞一个,用户自己选择合适的尺寸就好。大、中、小、大中、中小、微小、超大等乱七八糟的,全部一股脑上。

2、更新时间

更新时间为主动更新和定时更新;

主动更新:即在 APP 中可以动态更新这个桌面小组件,这种情况更新没有时间限制。

定时更新:小组件需要展示的数据可能已经发生了变化,但是 APP 已经被系统杀死了,无法主动更新数据,就会导致小组件展示的数据可能是已过期的或者是旧的,这时候就可以用到小组件的定时更新功能,但是这个定时更新有一个限制,基于省电逻辑,最快的更新周期为 30 分钟。(如果是再 onUpdate 方法中写个定时器定时更新,抱歉,不行,会被系统杀死,杀死之后小组件不会消失,而是一直显示最后一次更新时候的状态,直到下一次更新数据,类似于电子水墨屏的逻辑。)

3、点击事件

我这里图省事,只用了最简单的,点击整个小组件直接调起 APP,所以其他复杂一点的点击事件的处理方法我就不懂了。

点击跳转页面需要用到 PendingIntent,这玩意的 Flag 有很多种模式,具体可以查看文章底部的参考文档,坑就坑在这个 Flag,31 之后的系统有改动,会报错,所以 31 的系统需要用 PendingIntent.FLAG_IMMUTABLE,具体看代码。

4、调起 APP

通过 PendingIntent 就可以直接调起 APP 的相关页面,不过这里也有坑,假设你 APP 的启动页面是 MainActivity 页面,点击小组件你就让它跳转到 MainActivity 页面走正常的 APP 启动流程,就等同于是点击小组件就能启动 APP,哪怕 APP 被杀死了,也不影响启动(听着好像没毛病)。

坑就坑在于,通过这种方式打开的 APP,他…… 他不走 Application 类,也就是你如果是在 Application 中初始化了某些东西,但是 APP 已经被系统杀死了,这时候你再点击小组件启动 APP,就会发现,好多组件用不了(没初始化)。

我这里图省事的做法就是把 Application 的所有需要初始化的东西都放 MainActivity 里面初始化了(但是 Content 还是用的 Application,而不是用 MainActivity)。

开搞

需求

一个小组件,居中显示一个文本,点击可进入 APP

1、准备一个布局文件 widget_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lly_bg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_test"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="测试" />

</LinearLayout>

附上背景文件 bg_test.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--    背景色-->
    <solid android:color="#ffffff" />
    <!--    圆角-->
    <corners android:radius="20dp" />
</shape>

2、res 文件夹下新建一个 xml 文件夹,新建 app_widget_test.xml 配置文件

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="360dp"
    android:minHeight="120dp"
    android:updatePeriodMillis="1800000"
    android:previewImage="@drawable/ic_widget_big"
    android:initialLayout="@layout/widget_test"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>

minWidth、minHeight    最小宽高

updatePeriodMillis    更新周期

previewImage    添加桌面小组件时候显示的预览图

initialLayout    布局

widgetCategory    home_screen 是代表的桌面小组件,其他参数自行百度了

3、合适的地方新建一个 TestAppWidget 类,继承 AppWidgetProvider


/**
 * 桌面小组件
 *
 * @author Admin
 */
public class TestAppWidget extends AppWidgetProvider {

    /**
     * 每次窗口小部件被更新都调用一次该方法(创建、时间到更新周期都会调起这里)
     */

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        //更新数据
        updateWidgetView(context, UUID.randomUUID().toString());
    }

    /**
     * 接收窗口小部件点击时发送的广播
     */

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
    }

    /**
     * 每删除一次窗口小部件就调用一次
     */

    @Override

    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
    }

    /**
     * 当最后一个该窗口小部件删除时调用该方法
     */

    @Override

    public void onDisabled(Context context) {
        super.onDisabled(context);

    }

    /**
     * 当该窗口小部件第一次添加到桌面时调用该方法
     */

    @Override

    public void onEnabled(Context context) {
        super.onEnabled(context);

    }

    /**
     * 当小部件大小改变时
     */

    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    }

    /**
     * 当小部件从备份恢复时调用该方法
     */

    @Override

    public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
        super.onRestored(context, oldWidgetIds, newWidgetIds);
        ALog.e("当小部件从备份恢复时调用该方法");
    }

    /**
     * 更新桌面小组件数据用,APP中也可以在任意地方传入任意数据进来主动更新小组件数据
     */
    public static void updateWidgetView(Context context, String str) {
        //初始化RemoteViews
        ComponentName componentName = new ComponentName(context, TestAppWidget.class);
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_test);

        //点击事件,点击跳转到MainActivity页面
        Intent startActivityIntent = new Intent(context, MainActivity.class);
        PendingIntent processInfoIntent;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
            //31,Android11以上系统
            processInfoIntent = PendingIntent.getActivity(context, 0, startActivityIntent, PendingIntent.FLAG_IMMUTABLE);
        } else {
            processInfoIntent = PendingIntent.getActivity(context, 0, startActivityIntent, PendingIntent.FLAG_ONE_SHOT);
        }
        remoteViews.setOnClickPendingIntent(R.id.lly_bg, processInfoIntent);

        //更新文本数据
        remoteViews.setTextViewText(R.id.tv_test, str);

        //开始更新视图
        AppWidgetManager awm = AppWidgetManager.getInstance(context);
        awm.updateAppWidget(componentName, remoteViews);
    }

}

4、AndroidManifest.xml 中配置小组件,与 Activity 页面同级

        <receiver
            android:name=".TestAppWidget"
            android:exported="false">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/app_widget_test" />
        </receiver>

参考文章

https://blog.csdn.net/weixin_43499030/article/details/90264915

https://blog.csdn.net/weixin_43499030/article/details/90264915

有关Android 桌面小组件 AppWidgetProvider的更多相关文章

  1. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  2. ruby - 我可以使用 ruby​​ 创建桌面应用程序吗? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我想知道是否可以使用ruby​​创建桌面应用程序以及缺点,你能举个例子吗?在Windows中使用的应用程序谢谢

  3. ruby - 如何在 selenium webdriver - ruby​​ 中自动化桌面通知 - 2

    我正在尝试使用ruby​​中的seleniumwebdriver从gmail桌面通知中获取数据 最佳答案 开箱即用的想法,用Selenium截屏并用OCR处理图像?https://github.com/suyesh/ocr_space我假设Selenium只允许您与页面数据交互。 关于ruby-如何在seleniumwebdriver-ruby​​中自动化桌面通知,我们在StackOverflow上找到一个类似的问题: https://stackoverflo

  4. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  5. ipv6外网能ping通,但无法访问服务(自建网站,远程桌面等) - 2

    1.当前环境及情况说明宽带:电信、光猫桥接、路由器拨号ipv6地址:在各大网站都能ping通这个ipv6地址,本机也能访问ipv6的网站问题:其它外网电脑除了能ping通这个ipv6地址之外什么都访问不了2.可能出现问题的原因本机防火墙拦截了(关闭防火墙也是一样的)×光猫防火墙拦截了(试了不行,貌似桥接后跟光猫就没关系了)×路由器防火墙拦截了(用的是小米AX6000,IPV6配置的地方有个防火墙没有关闭)√运营商拦截了(根据最终效果测试,80端口、443端口被拦截无法使用,尽量用些不常用的端口)×3.路由器设置(关闭IPV6防火墙) 不同路由器可能设置不同,根据情况处理,我这里做为一个参考关闭

  6. Android 10.0 设置默认launcher后安装另外launcher后默认Launcher失效的功能修复 - 2

    1.前言 在10.0的系统rom定制化开发中,在系统中有多个launcher的时候,会在开机进入launcher的时候弹窗launcher列表,让用户选择进入哪个launcher,这样显得特别的不方便所以产品开发中,要求用RoleManager的相关api来设置默认Launcher,但是在设置完默认Launcher以后,在安装一款Launcher的时候,默认Launcher就会失效,在系统设置的默认应用中Launcher选项就为空,点击home键的时候会弹出默认Launcher列表,让选择进入哪个默认Launcher.所以需要从安装Launcher的流程来分析相关的设置。来解决问题设置默认La

  7. AiBote 2022 新研发的自动化框架,支持 Android 和 Windows 系统。速度非常快 - 2

    Ai-Bot基于流行的Node.js和JavaScript语言的一款新自动化框架,支持Windows和Android自动化。1、Windowsxpath元素定位算法支持支持Windows应用、.NET、WPF、Qt、Java和Electron客户端程序和ie、edgechrome浏览器2、Android支持原生APP和H5界面,元素定位速度是appium十倍,无线远程自动化操作多台安卓设备3、基于opencv图色算法,支持找图和多点找色,1080*2340全分辨率找图50MS以内4、内置免费OCR人工智能技术,无限制获取图片文字和找字功能。5、框架协议开源,除官方node.jsSDK外,用户可

  8. Android Gradle 7.1+新版本依赖变化 - 2

    前一段时间由于工作需要把可爱的小雪狐舍弃了,找到了小蜜蜂。但是新版本的小蜜蜂出现了很多和旧版本不一样的位置。1.功能位置迁移,原来在工程build.gradle的buildscript和allprojects移动至setting.gradle并改名为pluginManagement和dependencyResolutionManagement。里面的东西依旧可以按照原来的copy过来。pluginManagement{repositories{gradlePluginPortal()google()mavenCentral()}}dependencyResolutionManagement{r

  9. ruby - Ruboto 的最佳教程(适用于 Android 的 ruby​​)? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我几乎用完了Ruby,但现在想试试Ruboto,android上的ruby​​。谷歌未能给我足够的(几乎没有结果)。所以任何人都可以分享一些关于Ruboto的教程。

  10. c# - 从桌面开发转向 Web 开发 - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭7年前。Improvethisquestion到目前为止,我所有的编程经验都是桌面开发(主要是C/C++和OpenGL/DirectX),但我有兴趣尝试一些Web开发。我正在考虑的两个方向是RubyonRails和ASP.net。哪个应用最广泛?拥有哪种技能更有市场值(value)?谢谢!

随机推荐