草庐IT

Android 桌面小组件

Sruur 2024-01-06 原文

创建桌面小组件

  • 创建AppWidgetProvider类
    创建一个AppWidgetProvider类,桌面小组件在更新、启用、停用和删除应用微件时收到广播。而AppWidgetProvider继承BroadcastReceiver,并且专门对小组件进行了一定的广播过滤,因此我们需要创建一个自定义的AppWidgetProvider类用来处理小组件相关的操作。
package com.example.widgetdemo

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log

const val TAG = "MyWidgetProvider"

class MyWidgetProvider : AppWidgetProvider() {

   /**
    *  每次收到广播之后就会调用该函数,会在其他方法之前进行回调。一般可以不实现
    *  默认的 AppWidgetProvider 实现会过滤所有应用微件广播并视情况调用上述方法
    */
   override fun onReceive(context: Context?, intent: Intent?) {
       super.onReceive(context, intent)
       Log.d(TAG, "invoke onReceive......")
   }

   /**
    * 调用此方法可以按 AppWidgetProviderInfo 中的 updatePeriodMillis 属性定义的时间间隔来更新应用微件
    * 当用户添加应用微件时也会调用此方法,所以它应执行基本设置,如定义视图的事件处理脚本以及根据需要启动临时的 Service。
    * 不过,如果您已声明配置 Activity,则当用户添加应用微件时不会调用此方法,但会调用它来执行后续更新。
    * 由配置 Activity 负责在配置完成后执行首次更新。
    */
   override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) {
       super.onUpdate(context, appWidgetManager, appWidgetIds)
       Log.d(TAG, "invoke onUpdate......")
   }

   /**
    * 当首次放置微件时以及每当调整微件的大小时,会调用此方法。您可以使用此回调来根据微件的大小范围显示或隐藏内容
    */
   override fun onAppWidgetOptionsChanged(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetId: Int, newOptions: Bundle?) {
       super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
       Log.d(TAG, "invoke onAppWidgetOptionsChanged......")

   }

   /**
    * 每次从应用微件托管应用中删除应用微件时,都会调用此方法。
    */
   override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
       super.onDeleted(context, appWidgetIds)
       Log.d(TAG, "invoke onDeleted......")
   }

   /**
    * 首次创建应用微件的实例时,会调用此方法。例如,如果用户添加应用微件的两个实例,只有首次添加时会调用此方法。
    * 如果您需要打开一个新的数据库或执行只需要对所有应用微件实例执行一次的其他设置,则此方法非常合适。
    */
   override fun onEnabled(context: Context?) {
       super.onEnabled(context)
       Log.d(TAG, "invoke onEnabled......")
   }

   /**
    * 从应用微件托管应用中删除了应用微件的最后一个实例时,会调用此方法。
    * 您应使用此方法来清理在 onEnabled(Context) 中完成的所有工作,如删除临时数据库。
    */
   override fun onDisabled(context: Context?) {
       super.onDisabled(context)
       Log.d(TAG, "invoke onDisabled......")
   }

   override fun onRestored(context: Context?, oldWidgetIds: IntArray?, newWidgetIds: IntArray?) {
       super.onRestored(context, oldWidgetIds, newWidgetIds)
       Log.d(TAG, "invoke onRestored......")
   }
}
  • 注册广播
    在AndroidManifest.xml文件中注册receiver,并且指定广播接收的类为我们上面定义的类,其中label字段的值表示在小组件列表中可以展示出的小组件的名称,同时指定action为"android.appwidget.action.APPWIDGET_UPDATE",并且添加一个与小组件相关配置信息的文件my_appwidget_info
  <receiver
            android:name=".MyWidgetProvider"
            android:exported="false"
            android:label="我的卡片">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/my_appwidget_info" />
        </receiver>
  • my_appwidget_info.xml文件
    initialLayout:小组件的初始化布局
    minWidth:小组件最小宽度
    minHeight:小组件最小高度
    previewImage:小组件在应用组件列表中的预览图
    resizeMode:是否可以调整大小,none|horizontal|vertical;固定大小|水平方向|垂直方向
    updatePeriodMillis:更新间隔,多久更新一次,单位MS,官方限制30分钟,以减少耗电
    widgetCategory:小组件可以展示在哪里,一般为主界面
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/layout_my_card_widget"
    android:minWidth="80dp"
    android:minHeight="40dp"
    android:previewImage="@drawable/widget_icon"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen">
</appwidget-provider>

第一个桌面小组件就创建好了,效果如下:

事件交互

  • 更新小组件
    onUpdate方法会在用户首次往桌面添加小组件的时候调用,并且appWidgetIds属性标识了当前AppWidgetProvider管理几个小组件,因此在该方法中对小组件进行首次更新,我们可以在此处获取数据并更新小组件相关信息。
    布局预览:

    我们模拟一个场景,当用户首次想桌面添加小组件时,需要从网络拉取数据并更新到小组件当中,并且设置两个Button的点击事件。由于是演示,因此简单启动一个定时任务,来模拟网络耗时操作,实际情况比较复杂,需考虑各种case。
override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
        Log.d(TAG, "invoke onUpdate......")
        val timer = Timer()
        val timerTask: TimerTask = object : TimerTask() {
            override fun run() {//定时任务
                appWidgetIds?.forEach {
                    val pendingIntent: PendingIntent =
                        Intent(context, SecondActivity::class.java).let { intent ->
                            PendingIntent.getActivity(context, 0, intent, 0)
                        }
                    val views: RemoteViews =
                        RemoteViews(context?.packageName, R.layout.layout_my_card_widget).apply {
                            setTextViewText(R.id.name, "来自Code")//设置小组件TextView的值
                            //添加LeftButton的点击事件
                            setOnClickPendingIntent(R.id.btnLeft, getSelfPendingIntent(context, LEFT_BUTTON_CLICK))
                            //添加RightButton的点击事件
                            setOnClickPendingIntent(R.id.btnRight, getSelfPendingIntent(context, RIGHT_BUTTON_CLICK))
                        }
                        //执行更新
                    appWidgetManager?.updateAppWidget(appWidgetIds, views)
                }
            }
        }
        timer.schedule(timerTask, 1000)
    }

因为小组件与app之间的交互是基于广播的,因此我们定义点击事件时需要自定义intent并且通过action进行点击事件的区分

	private const val LEFT_BUTTON_CLICK = "widget_left_button"
	private const val RIGHT_BUTTON_CLICK = "widget_right_button"

    private fun getSelfPendingIntent(context: Context?, action: String): PendingIntent {
        val intent = Intent(context, javaClass)//javaClass = this.class
        intent.action = action
        return PendingIntent.getBroadcast(context, 0, intent, 0)
    }

接下来就是当我们点击小组件,然后回调到onReceive方法中对事件进行处理了

override fun onReceive(context: Context?, intent: Intent?) {
        super.onReceive(context, intent)
        Log.d(TAG, "invoke onReceive......")
        intent?.action?.let { action ->
            context?.let {
                val manager = AppWidgetManager.getInstance(context)
                val remoteViews = RemoteViews(context.packageName, R.layout.layout_my_card_widget)
                if (action == LEFT_BUTTON_CLICK) {
                	//设置一个颜色
                    remoteViews.setTextColor(R.id.name, Color.parseColor("#FF03DAC5"))
                    manager.updateAppWidget(ComponentName(context, MyWidgetProvider::class.java), remoteViews)
                } else if (action == RIGHT_BUTTON_CLICK) {
                	//将颜色改回来
                    remoteViews.setTextColor(R.id.name, Color.parseColor("#FF000000"))
                    manager.updateAppWidget(ComponentName(context, MyWidgetProvider::class.java), remoteViews)
                    Toast.makeText(context, "点击右边按钮", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

布局文件比较单,到此关于小组件的简单使用就已经完成了。

  • 注意事项:小组件的布局只支持RemoteViews,具体如下:

    参考官方文档:Google开发文档

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

  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)?谢谢!

随机推荐