Android 桌面小组件(二)— 提示添加与启用配置页面

App中可能存在一些高频使用的功能,例如扫一扫、开启收付款码等。如果能够在不先打开App的情况下快速使用这些功能,将显著提升用户体验。

在上一篇文章Android 桌面小组件(一)--- 添加桌面小组件中介绍了如何创建桌面小组件并在桌面上添加。本文将进一步介绍提示用户添加小组件与启用小组件的配置页面。

提示用户添加小组件

App中配置好小组件之后,除了像上一篇文章中演示的那样在桌面直接添加小组件之外,从Android 8.0开始,系统支持App提示用户添加小组件到桌面。

具体步骤如下:

  1. AndroidManifest中声明App的小组件(在上一篇文件中已经介绍过,本文不再赘述)。
  2. 调用AppWidgetManager.requestPinAppWidget()方法,提示用户添加小组件。

调用requestPinAppWidget

调用AppWidgetManager.requestPinAppWidget()示例代码如下:

less 复制代码
class DesktopWidgetExampleActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = LayoutDesktopWidgetExampleActivityBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }

        binding.btnAddDesktopWidget.setOnClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                AppWidgetManager.getInstance(this).run {
                    // 先判断是否支持添加小组件
                    if (isRequestPinAppWidgetSupported) {
                        // 小组件信息
                        val widgetProvider = ComponentName(this@DesktopWidgetExampleActivity, DesktopWidgetExampleProvider::class.java)
                        // 添加成功的广播(如果不需要,可以用null)
                        val successCallback = PendingIntent.getBroadcast(this@DesktopWidgetExampleActivity, 0, Intent(this@DesktopWidgetExampleActivity, DesktopWidgetExampleProvider::class.java).apply { action = ACTION_PIN_WIDGET }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
                        // 请求添加小组件
                        requestPinAppWidget(widgetProvider, null, successCallback)
                    }
                }
            }
        }
    }
}

效果演示

如图:

启用小组件的配置页面

假如小组件有一些可配置项,系统支持使用一个独立的配置页面对小组件的可配置项进行管理。

具体步骤如下:

  1. 创建配置页面。
  2. AndroidManifest中声明配置页面。
  3. AppWidgetProviderInfo配置文件中声明配置页面。

创建配置页面

注意事项:

  • 小组件的宿主(例如桌面)会将小组件的id通过Intent传入。
  • Activity必须返回结果,并且结果必须包含传入的小组件id。

创建的配置页面示例代码如下:

kotlin 复制代码
class DesktopWidgetConfigExampleActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = LayoutDesktopWidgetConfigExampleActivityBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }

        if (!SpUtils.isInit()) {
            SpUtils.init(this)
        }

        val appWidgetId = intent?.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
            ?: AppWidgetManager.INVALID_APPWIDGET_ID

        // 先将结果设置为Activity.RESULT_CANCELED,如果没有顺利执行完整个流程,系统会取消添加widget。
        setResult(Activity.RESULT_CANCELED, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId))

        binding.btnSave.setOnClickListener {
            val targetWaterCount = binding.etInputTargetWaterCount.text.toString()
            updateWidget(this, AppWidgetManager.getInstance(this), appWidgetId, targetWaterCount)
            setResult(Activity.RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId))
            finish()
        }
    }

    private fun updateWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, targetWaterCount: String) {
        // 可能不是初次创建时打开配置,所以需要使用保存的数据。
        val currentTotalWaterCount = SpUtils.getInt("currentTotalWaterCount", 0)
        appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(context.packageName, R.layout.layout_desktop_widget_example).apply {
            setTextViewText(R.id.tv_target_water_count, "目标饮水量(杯):$targetWaterCount")
            setTextViewText(R.id.tv_example_content, "当前饮水量(杯):$currentTotalWaterCount")
            setOnClickPendingIntent(R.id.btn_plus, PendingIntent.getBroadcast(context, 0, Intent(context, DesktopWidgetExampleProvider::class.java).apply { action = ACTION_INCREASE }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE))
            setOnClickPendingIntent(R.id.btn_reduce, PendingIntent.getBroadcast(context, 0, Intent(context, DesktopWidgetExampleProvider::class.java).apply { action = ACTION_DECREASE }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE))
        })
    }
}

AndroidManfiest中添加配置页面

小组件的宿主会通过携带ACTION_APPWIDGET_CONFIGUREIntent打开配置页面,因此配置页面的intent-filter要添加对应的action,示例代码如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    ......

    <application
        ......
        >
            android:name=".androidapi.desktopwidgets.DesktopWidgetConfigExampleActivity"
            android:exported="false">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>
    </application>
</manifest>

在AppWidgetProviderInfo配置文件中声明配置页面

在AppWidgetProviderInfo配置文件中,使用android:configure属性来声明配置文件,示例代码如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="com.chenyihong.exampledemo.androidapi.desktopwidgets.DesktopWidgetConfigExampleActivity"
    android:widgetFeatures="reconfigurable"
    ...... />

PS: Android 9开始,增加了android:widgetFeatures属性,配置为reconfigurable时允许用户重新配置小组件,但是从Android 12开始才得到广泛支持。

效果演示

如图:

完整示例代码

所有演示代码已在示例Demo中添加。

ExampleDemo github

ExampleDemo gitee

相关推荐
诸神黄昏EX1 小时前
Android 分区相关介绍
android
大白要努力!2 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee2 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood2 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-5 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen7 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年15 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿17 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神18 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛18 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee