小部件,也叫微件,
它的介绍参考官网 应用 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
更新小部件。
最终效果: