如何在应用小部件中为 Android AnalogClock 设置时间?

How to set time to Android AnalogClock in app widget?(如何在应用小部件中为 Android AnalogClock 设置时间?)

本文介绍了如何在应用小部件中为 Android AnalogClock 设置时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何为Android AnalogClock设置自定义时间放置在app widget中?

How to set custom time to Android AnalogClock placed in app widget?

作为替代方案,我正在考虑覆盖默认的 AnalogClock 以通过代码设置时间.或者换句话说,我将创建从默认视图或模拟时钟扩展的自定义时钟.然后将我的自定义时钟放在小部件布局 UI 上.

As an alternative I was thinking to override default AnalogClock to set the time through codes. Or in other words, I would create my custom Clock which extends from default View or AnalogClock. Then put my custom Clock on widget layout UI.

这可能吗?恐怕我们受限于 RemoteViews 来拥有我们自己的自定义组件.

Is this possible? I'm afraid we are limited on RemoteViews to have our own custom Component.

更新:这是我最初遵循 vArDo 给出的解决方案的错误日志.

UPDATE: This is error log I have following the solution given by vArDo at first.

推荐答案

简介

无法在应用小部件中包含自定义视图.根据文档只能使用一小部分预定义的视图子集在布局中.因此,正如您所提到的,您无法创建自己的 AnalogClock 视图(正如我在其他答案中所展示的那样)并将其包含在应用小部件中.

Intro

It's not possible to include custom views in app widgets. According to documentation only small predefined subset of views can be used in layout. So, as you've mentioned, it's not possible to create your own AnalogClock view (as I've shown in my other answer) and include it in app widget.

但是,还有其他方法可用于在应用小部件中创建自定义 AnalogClock.在这样的实现中,直接在 AnalogClock 中设置时间应该没有问题.

However there are other means that can be used to create custom AnalogClock in app widget. In such implementation setting time directly in AnalogClock should not be a problem.

实现此目的的方法之一是将自定义 AnalogClock 绘制到 ImageView(这是应用小部件中允许的视图之一).重复 PendingIntent 使用 Service 每 60 秒绘制一次 ImageView(通过 RemoteViews).

One of the ways to accomplish this is draw custom AnalogClock into ImageView (which is one of the allowed views in app widgets). Repeating PendingIntent is using Service to draw into ImageView every 60 seconds (via RemoteViews).

请记住,每 60 秒通过 RemoteViews 调用 Service 操作来更新 app 小部件 对电池的消耗并不那么轻.Jelly Bean 示例实现在夜间使用了 2% 的电池(屏幕关闭,平均网络强度).但是,我每 15 秒更新一次屏幕,这对于模拟时钟来说是不必要的.因此粗略估计,每分钟更新一次的应用小部件在夜间会消耗大约 0.5% 的电池电量 - 这对您的用户来说可能意义重大.

Keep in mind that invoking Service action to update app widget via RemoteViews every 60 seconds is not so lightweight on battery. On Jelly Bean example implementation used 2% of battery during night (screen off, average network strength). However, I've been updating the screen every 15 seconds, which is not necessary when it comes to analog clocks. So a rough estimate would be that an app widget with one update per minute will consume about 0.5% of battery juice during night - this might be significant for your users.

首先,您必须创建 Service,它将绘制到您视图中的 ImageView 中.绘图代码几乎取自 AnalogClock 实现并根据需要重新排列.首先,没有检查时间是否已经改变 - 这是因为 Service 只有在我们决定时才会被调用(例如每 60 秒).秒变是我们从方法中的资源创建Drawable.

First of all you'll have to create Service that will draw into ImageView present in your view. Drawing code is pretty much taken from AnalogClock implementation and rearrange according to needs. First of all, there are no checks on whether time has changed - this is because Service will be invoked only when we decide too (e.g. every 60 seconds). Seconds change is that we create Drawables from resources in the method.

另一个变化是代码使用 Canvas 将模拟时钟绘制到本地 Bitmap 中:

Another change is that code draws analog clock into local Bitmap using Canvas:

    // 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 了解详情).在我们的例子中,只有一个变化,它是通过特殊方法完成的:

Update is drawn into ImageView via RemoteViews. Changes are aggregated and then pushed into app widget on home screen - see docs for details). In our case there is only one change, and it is done through special method:

    remoteViews.setImageViewBitmap(R.id.imageView1, appWidgetBitmap);

请记住,要使代码正常工作,您必须声明可用于绘制模拟时钟的可用空间.根据 widget_provider.xml 中声明的内容,您可以确定 ImageView 的尺寸.请注意,在绘图之前,您必须将 dp 单位转换为 px 单位:

Bare in mind that for code to work you have to declare what is available space for drawing your analog clock. Based on what is declare in your widget_provider.xml, you can determine the dimensions of ImageView. Note that you'll have to convert dp unit to px unit, before drawing:

    // 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子类的完整代码.

I've pasted full code of Service subclass here.

您还可以在顶部找到负责设置显示时间的代码 - 根据需要进行修改:

Also at the top you'll find the code that is responsible for setting time that will be displayed - modify as needed:

    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 和应用小部件,例如:

Also don't forget to declare this Service and app widget in your AndroidManifest.xml file, e.g.:

    <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"/>

这篇博文.

计划更新在 AppWidgetProvider 子类的 onUpdate() 方法中完成 - 在 AndroidManifest.xml 文件中声明的 on.当应用小部件从主屏幕移除时,我们还需要移除 PendingIntent.这是在重写的 onDisabled() 方法中完成的.请记住声明将调用每个方法之一的两个操作:APPWIDGET_UPDATEAPPWIDGET_DISABLED.请参阅上面 AndroidManifest.xml 的摘录.

Scheduling updates is done in onUpdate() method of your AppWidgetProvider subclass - the on that is declare in your AndroidManifest.xml file. We'll also need to remove PendingIntent when app widget is removed from home screen. This is done in overridden onDisabled() method. Remember to declare both actions that will call one of each methods: APPWIDGET_UPDATE and APPWIDGET_DISABLED. See excerpt from AndroidManifest.xml above.

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 秒发生一次重复事件,第一次更新立即发生.

Last line of onUpdate() method schedules a repeating event to happen every 60 seconds, with first update to happen right away.

使用上面的代码只调用一次服务的最简单方法是及时安排单个事件而不是重复一个.用以下行简单替换 alarmManager.setRepeating() 调用:

Using above code the simplest way to invoke service only once is to schedule single event in time instead of repeating one. Simple replace alarmManager.setRepeating() call with following line:

alarmManager.set(AlarmManager.RTC, time.getTime().getTime(), service);

结果

以下是自定义 AnalogClock 应用小部件的屏幕截图,该小部件仅设置了一次时间(注意模拟时钟时间与状态栏中显示的时间之间的差异):

Results

Below is a screen shot with custom AnalogClock app widget that had time set only once (note difference between analog clock time and time shown in status bar):

这篇关于如何在应用小部件中为 Android AnalogClock 设置时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:如何在应用小部件中为 Android AnalogClock 设置时间?

基础教程推荐