Android 桌面小组件 AppWidgetProvider

废话

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

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

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

预览图

注意事项

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

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以通过以下步骤在Android上动态添加桌面组件: 1. 创建小组件布局:首先,创建一个布局文件来定义小组件的外观。这可以在res/layout目录下的XML文件中完成。你可以使用不同的视图和布局来设计小组件。 2. 创建小组件提供者:创建一个扩展AppWidgetProvider类的Java类。这个类将负责处理小组件的生命周期事件,例如创建、更新和删除小组件。 3. 配置小组件提供者:在AndroidManifest.xml文件中,注册你的小组件提供者类。使用<receiver>标签来指定小组件提供者类,并使用<intent-filter>标签来定义处理小组件生命周期事件的操作。 4. 更新桌面:当用户将小组件拖放到桌面时,你需要在运行时动态添加小组件。为此,你可以使用AppWidgetManager类的updateAppWidget()方法来更新小组件视图。 下面是一个示例代码,展示如何动态添加桌面组件: ```java // 创建小组件布局 RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout); // 配置小组件点击事件 Intent intent = new Intent(context, MyWidgetProvider.class); intent.setAction("ACTION_WIDGET_CLICKED"); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); remoteViews.setOnClickPendingIntent(R.id.widget_button, pendingIntent); // 更新桌面组件 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); ComponentName componentName = new ComponentName(context, MyWidgetProvider.class); appWidgetManager.updateAppWidget(componentName, remoteViews); ``` 请注意,你需要根据你的需求适应以上代码,并根据你的小组件布局和逻辑进行修改。 希望这能帮助到你!如有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值