Android 添加自己的时钟小部件

小部件,也叫微件,

它的介绍参考官网 应用 widget 概览 https://developer.android.google.cn/develop/ui/views/appwidgets/overview?hl=zh-cn

直接上图,原生系统上,时钟应用的小部件效果。

我也整一个。

1.创建小部件布局文件

这个文件就是最终显示在桌面的小部件的样式。

res/layout/time_widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="6dp"
    android:paddingBottom="6dp"
    android:gravity="center"
    android:orientation="vertical">

    <TextClock
        android:id="@+id/tv_date"
        android:layout_width="match_parent"
        android:layout_height="22dp"
        android:format12Hour="yyyy/MM/dd E"
        android:format24Hour="yyyy/MM/dd E"
        android:gravity="center"
        android:textSize="17sp"
        android:textColor="#DBE1FF" />

    <TextClock
        android:id="@+id/tv_time"
        android:layout_width="match_parent"
        android:layout_height="101dp"
        android:format12Hour="h:mm"
        android:format24Hour="HH:mm"
        android:gravity="center"
        android:textColor="#DBE1FF"
        android:textSize="@dimen/textclock_time_size" />

</LinearLayout>

使用 TextClock 显示日期、时间,很方便,它会自己更新,不需要添加刷新逻辑。

2.创建小部件配置文件

AppWidgetProviderInfo 对象定义了 widget 的基本特性。

res/xml/time_widget.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/time_widget_layout"
    android:minHeight="110dp"
    android:minWidth="110dp"
    android:resizeMode="vertical|horizontal"
    android:previewImage="@drawable/pic_beauty"
    android:updatePeriodMillis="3000" />
  • android:initialLayout :指向小部件布局文件 。
  • android:minHeight 、android:minWidth :小部件原始占用区域大小,也是最小占用区域大小,我的示例占用的是 2 x 2 。
  • android:resizeMode="vertical|horizontal" :是否支持调整大小,这个是横向、纵向都支持。
  • android:previewImage :小部件的预览图。
  • android:previewLayout:小部件预览页面的布局。本例只设置了预览图,没有添加布局,有需要自己添加即可。
  • android:updatePeriodMillis :小部件更新时间间隔。

小部件的大小要根据实际情况计算,参考官网示例

参考 Android Developers 应用微件设计指南

3.实现AppWidgetProvider

3.1 在清单中声明自定义的 AppWidgetProvider

AppWidgetProvider 本质是 receiver 。

        <receiver
            android:name=".appwidget.TimeWidgetProvider"
            android:exported="true"
            android:label="TimeWidgetLabel">
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/time_widget" />

            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
        </receiver>
  • android:name=".appwidget.TimeWidgetProvider" :要实现的自定义 AppWidgetProvider
  • <meta-data/> 里的 android:name 是默认格式,不要改;android:resource 指向小部件配置文件。
  • <intent-filter> 里的是默认格式,不要修改。

3.2 实现自定义的 AppWidgetProvider

创建 TimeWidgetProvider ,继承 AppWidgetProvider

public class TimeWidgetProvider extends AppWidgetProvider {

    public static final String TAG = TimeWidgetProvider.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.d(TAG, "onReceive : " + intent.getAction());
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.d(TAG, "onUpdate");
    }
}

onReceive 里会接收到这些广播:

  • android.appwidget.action.APPWIDGET_ENABLED :小部件第一次被添加到桌面时触发。如果添加同一个小部件两次,第二次添加不会触发。对应 onEnabled 方法。
  • android.appwidget.action.APPWIDGET_UPDATE :小部件被添加到桌面或者小部件更新时触发。对应 onUpdate 方法。
  • android.appwidget.action.APPWIDGET_DELETED :小部件被删除时触发。如果同一个小部件有多个,删一个触发一次。对应 onDeleted方法。
  • android.appwidget.action.APPWIDGET_DISABLED :同一个小部件,最后一个被删除时触发。对应 onDisabled方法。
  • android.appwidget.action.APPWIDGET_UPDATE_OPTIONS :用户调整小部件大小时触发。对应 onAppWidgetOptionsChanged 方法。

到这里,初版已OK。

3.3 更新小部件

使用 RemoteViews 更新调整UI 、添加点击事件,然后调用 AppWidgetManager.updateAppWidget 即可。

	private RemoteViews remoteViews;
	private AppWidgetManager mAppWidgetManager;
    private int[] mAppWidgetIds;

	public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.d(TAG, "onUpdate");
        mAppWidgetManager = appWidgetManager;
        mAppWidgetIds = appWidgetIds;
        Log.d(TAG, "onUpdate appWidgetIds:" + Arrays.toString(mAppWidgetIds));
        createRemoteViews(context);
    }

    private void createRemoteViews(Context context) {
        if (remoteViews == null) {
            Log.d(TAG, "createRemoteViews");
            remoteViews = new RemoteViews(context.getPackageName(), R.layout.time_widget_layout);//注释1

            Intent intent = new Intent("android.settings.DATE_SETTINGS");
            intent.setPackage("com.android.settings");
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            
            PendingIntent pendingIntent1, pendingIntent2;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
                pendingIntent1 = PendingIntent.getActivity(context, 101, intent, PendingIntent.FLAG_IMMUTABLE);
                pendingIntent2 = PendingIntent.getActivity(context, 102, intent, PendingIntent.FLAG_IMMUTABLE);
            } else {
                pendingIntent1 = PendingIntent.getActivity(context, 101, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                pendingIntent2 = PendingIntent.getActivity(context, 102, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
            remoteViews.setOnClickPendingIntent(R.id.tv_time, pendingIntent1);
            remoteViews.setOnClickPendingIntent(R.id.tv_date, pendingIntent2);//注释2

            for (int id:mAppWidgetIds) {
                mAppWidgetManager.updateAppWidget(id, remoteViews);//注释3
            }

        }

注释1 :通过 RemoteViews 找到 小部件布局文件,

RemoteViews 提供了很多方法更新小部件,如

  • setTextViewText(@IdRes int viewId, CharSequence text) :根据 id 设置 TextView 。
  • setImageViewResource(@IdRes int viewId, @DrawableRes int srcId) :根据 id 设置 ImageView。
  • setOnClickPendingIntent(@IdRes int viewId, PendingIntent pendingIntent) :根据 id 设置点击事件。

本例用的 TextClock ,会自动更新时间,就没有加 RemoteViews 的逻辑。

注释2 :使用 PendingIntent 为各个控件添加点击事件。如果不加,默认打开应用首页。本例打开原生设置的 日期和时间 。

注释3 :调用 AppWidgetManager.updateAppWidget 更新小部件。

最终效果:

相关推荐
大白要努力!29 分钟前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟1 小时前
Android音频采集
android·音视频
小白也想学C2 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程3 小时前
初级数据结构——树
android·java·数据结构
闲暇部落5 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX7 小时前
Android 分区相关介绍
android
大白要努力!8 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee8 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood8 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-11 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记