Android 桌面小组件 AppWidgetProvider

废话

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

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

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

预览图

注意事项

1、UI 适配

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

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

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

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

2、更新时间

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

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

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

3、点击事件

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

扫描二维码关注公众号,回复: 14779782 查看本文章

点击跳转页面需要用到 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

猜你喜欢

转载自blog.csdn.net/qq_33601179/article/details/127704060
今日推荐