简介
无法在应用小部件中包含自定义视图。 According to documentation 在布局中只能使用一小部分预定义的视图子集。因此,正如您所提到的,您无法创建自己的 AnalogClock 视图(正如我在其他答案中所展示的那样)并将其包含在应用小部件中。
但是,还有其他方法可用于在应用小部件中创建自定义 AnalogClock。在这样的实现中直接在AnalogClock中设置时间应该没有问题。
简答
实现此目的的方法之一是将自定义AnalogClock 绘制到ImageView(这是应用小部件中允许的视图之一)。重复 PendingIntent 是使用 Service 每 60 秒绘制一次 ImageView(通过 RemoteViews)。
电池使用注意事项
请记住,每 60 秒通过RemoteViews 调用Service 操作以更新应用程序小部件 对电池的消耗并不那么轻。 Jelly Bean 示例实现在夜间使用了 2% 的电池(屏幕关闭,平均网络强度)。但是,我每 15 秒更新一次屏幕,这对于模拟时钟来说是不必要的。因此粗略估计,每分钟更新一次的应用小部件在夜间会消耗大约 0.5% 的电池电量 - 这对您的用户来说可能意义重大。
解决方案详情
更改绘图服务
首先,您必须创建Service,它将绘制到您视图中的ImageView。绘图代码几乎取自AnalogClock 实现并根据需要重新排列。首先,没有检查时间是否已更改 - 这是因为 Service 只有在我们也做出决定时才会被调用(例如每 60 秒)。秒变是我们从方法中的资源创建Drawables。
另一个变化是代码使用Canvas将模拟时钟引入本地Bitmap:
// Creating Bitmap and Canvas to which analog clock will be drawn.
Bitmap appWidgetBitmap = Bitmap.createBitmap(availableWidth, availableHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(appWidgetBitmap);
通过RemoteViews 将更新引入ImageView。更改会被汇总,然后推送到主屏幕上的应用小部件中 - 有关详细信息,请参阅 docs)。在我们的例子中,只有一个变化,它是通过特殊方法完成的:
remoteViews.setImageViewBitmap(R.id.imageView1, appWidgetBitmap);
请记住,要使代码正常工作,您必须声明可用于绘制模拟时钟的可用空间。根据widget_provider.xml 中声明的内容,您可以确定ImageView 的尺寸。请注意,在绘图之前,您必须将dp 单位转换为px 单位:
// You'll have to determine tour own dimensions of space to which analog clock is drawn.
final int APP_WIDGET_WIDTH_DP = 210; // Taken from widget_provider.xml: android:minWidth="210dp"
final int APP_WIDGET_HEIGHT_DP = 210; // Taken from widget_provider.xml: android:minHeight="210dp"
final int availableWidth = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_WIDGET_WIDTH_DP, r.getDisplayMetrics()) + 0.5f);
final int availableHeight = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_WIDGET_HEIGHT_DP, r.getDisplayMetrics()) + 0.5f);
我已经粘贴了Service子类here的完整代码。
您还可以在顶部找到负责设置显示时间的代码 - 根据需要进行修改:
mCalendar = new Time();
// Line below sets time that will be displayed - modify if needed.
mCalendar.setToNow();
int hour = mCalendar.hour;
int minute = mCalendar.minute;
int second = mCalendar.second;
mMinutes = minute + second / 60.0f;
mHour = hour + mMinutes / 60.0f;
另外不要忘记在您的AndroidManifest.xml 文件中声明此Service 和应用小部件,例如:
<receiver
android:name="TimeSettableAnalogClockAppWidget"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_DISABLED"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_provider"/>
</receiver>
<service android:name="TimeSettableAnalogClockService"/>
this blog post 中简要介绍了有关使用服务更新应用小部件的最重要信息。
调用绘图
计划更新是在您的AppWidgetProvider 子类的onUpdate() 方法中完成的——在您的AndroidManifest.xml 文件中声明的方法。当应用小部件从主屏幕移除时,我们还需要移除 PendingIntent。这是在重写的onDisabled() 方法中完成的。请记住声明将调用每个方法之一的两个操作:APPWIDGET_UPDATE 和 APPWIDGET_DISABLED。请参阅上述AndroidManifest.xml 的摘录。
package com.example.anlogclocksettimeexample;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
public class TimeSettableAnalogClockAppWidget extends AppWidgetProvider {
private PendingIntent service = null;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// super.onUpdate(context, appWidgetManager, appWidgetIds);
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final Calendar time = Calendar.getInstance();
time.set(Calendar.SECOND, 0);
time.set(Calendar.MINUTE, 0);
time.set(Calendar.HOUR, 0);
final Intent intent = new Intent(context, TimeSettableAnalogClockService.class);
if (service == null) {
service = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
alarmManager.setRepeating(AlarmManager.RTC, time.getTime().getTime(), 1000 * 60 /* ms */, service);
}
@Override
public void onDisabled(Context context) {
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(service);
}
}
onUpdate() 方法的最后一行安排每 60 秒发生一次重复事件,并立即发生第一次更新。
只画一次AnalogClock
使用上面的代码只调用一次服务的最简单方法是及时安排单个事件,而不是重复一个。使用以下行简单替换 alarmManager.setRepeating() 调用:
alarmManager.set(AlarmManager.RTC, time.getTime().getTime(), service);
结果
以下是自定义AnalogClock 应用小部件的屏幕截图,该小部件仅设置了一次时间(注意模拟时钟时间与状态栏中显示的时间之间的差异):