Как установить время на Android AnalogClock в виджете приложения?
Как установить пользовательское время для Android AnalogClock
размещены в виджете приложения?
В качестве альтернативы я думал переопределить AnalogClock по умолчанию, чтобы установить время с помощью кодов. Или, другими словами, я бы создал свои собственные часы, которые расширяются от просмотра по умолчанию или AnalogClock. Затем поместите мои пользовательские часы в пользовательский интерфейс виджета.
Это возможно? Боюсь, что у нас в RemoteViews есть свой собственный компонент.
ОБНОВЛЕНИЕ: Это журнал ошибок, который я имею вначале после решения, данного vArDo.
2 ответа
вступление
Невозможно включить пользовательские представления в виджеты приложения. В соответствии с документацией в макете может использоваться только небольшое предопределенное подмножество видов. Итак, как вы упомянули, невозможно создать свой собственный AnalogClock
просмотреть (как я показал в моем другом ответе) и включить его в виджет приложения.
Однако есть и другие средства, которые можно использовать для создания пользовательских AnalogClock
в виджете приложения. В такой реализации время установки прямо в AnalogClock
не должно быть проблемой.
Короткий ответ
Одним из способов сделать это является рисование на заказ AnalogClock
в ImageView
(который является одним из разрешенных видов в виджетах приложения). повторяющий PendingIntent
использует Service
втянуть в ImageView
каждые 60 секунд (через RemoteViews
).
Обратите внимание на использование батареи
Имейте в виду, что вызывая Service
действие по обновлению виджета приложения через RemoteViews
каждые 60 секунд не так легко на батарее. В примере реализации Jelly Bean использовалось 2% батареи ночью (выключенный экран, средняя мощность сети). Тем не менее, я обновляю экран каждые 15 секунд, что не является необходимым, когда дело доходит до аналоговых часов. Таким образом, по приблизительным оценкам, виджет приложения с одним обновлением в минуту будет потреблять около 0,5% заряда аккумулятора ночью - это может быть важно для ваших пользователей.
Детали решения
Изменения в оформлении сервиса
Прежде всего вам придется создать Service
это привлечет в ImageView
представить на ваш взгляд. Код для рисования в значительной степени взят из AnalogClock
реализация и перестройка в соответствии с потребностями. Прежде всего, нет никаких проверок того, изменилось ли время - это потому, что Service
будет вызываться только тогда, когда мы решим (например, каждые 60 секунд). Изменение секунд в том, что мы создаем Drawable
s из ресурсов в методе.
Другое изменение заключается в том, что код переводит аналоговые часы в локальные Bitmap
с помощью 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);
Обновление оформлено в ImageView
с помощью RemoteViews
, Изменения агрегируются и затем помещаются в виджет приложения на главном экране - подробности см. В документации). В нашем случае есть только одно изменение, и это делается специальным методом:
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
подкласс здесь.
Также в верхней части вы найдете код, который отвечает за настройку времени, которое будет отображаться - при необходимости измените:
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;
Также не забудьте заявить об этом Service
и виджет приложения в вашем AndroidManifest.xml
файл, например:
<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"/>
Наиболее важная информация об использовании сервисов для обновления виджета приложения кратко описана в этом сообщении в блоге.
Вызывающий рисунок
Планирование обновлений выполняется в onUpdate()
метод вашего AppWidgetProvider
подкласс - то, что объявлено в вашем 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
Виджет приложения, время которого было установлено только один раз (обратите внимание на разницу между временем аналоговых часов и временем, показанным в строке состояния):
Там нет никакого способа установить время на что-то, кроме текущего времени в AnalogClock
, Этот виджет очень инкапсулирован. Вы можете легко изменить его внешний вид (в XML, используя атрибуты R.styleable, но нет простого способа изменить его поведение).
Я бы, вероятно, пошел с созданием копии AnalogClock.java (может быть хорошей идеей поместить ее в отдельный пакет, например com.example.widget
) и локальные копии drawables clock_dial, clock_hand_hour, clock_hand_minute (ПРИМЕЧАНИЕ: для получения наилучших результатов вам придется создавать локальные копии для всех плотностей: ldpi, mdpi, hdpi и xhdpi). С их помощью и небольшими изменениями в коде вы сможете получить желаемое поведение.
Использование пользовательских атрибутов Drawables WITH, устанавливаемых из макета XML
Чтобы быть в состоянии установить dial
, hand_hour
а также hand_minute
Рисунки из файлов макета XML, вам нужно объявить эти атрибуты как стилизуемые для вашего пользовательского виджета.
ПРИМЕЧАНИЕ: в следующем примере я создал TimeSettableAnalogClock
и поместил его в com.example.widget
пакет.
Создайте res/values/attrs.xml
файл со следующим содержанием:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TimeSettableAnalogClock">
<attr name="dial" format="reference"/>
<attr name="hand_hour" format="reference"/>
<attr name="hand_minute" format="reference"/>
</declare-styleable>
</resources>
format=reference
означает, что значения могут быть только ссылками на другие существующие элементы, например, drawables (@drawable/some_custom_dial
).
Теперь настраиваемые атрибуты можно использовать в файле макета XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/lib/com.example.widget"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.widget.TimeSettableAnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:dial="@drawable/some_custom_clock_dial"
custom:hand_hour="@drawable/some_custom_clock_hand_hour"
custom:hand_minute="@drawable/some_custom_clock_hand_minute"/>
</RelativeLayout>
Наконец, вам нужно обработать эти пользовательские атрибуты, когда TimeSettableAnalogClock
объект создан:
public TimeSettableAnalogClock(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Resources r = getContext().getResources();
TypedArray a =
context.obtainStyledAttributes(
attrs, R.styleable.TimeSettableAnalogClock, defStyle, 0);
mDial = a.getDrawable(R.styleable.TimeSettableAnalogClock_dial);
if (mDial == null) {
mDial = r.getDrawable(R.drawable.clock_dial);
}
mHourHand = a.getDrawable(R.styleable.TimeSettableAnalogClock_hand_hour);
if (mHourHand == null) {
mHourHand = r.getDrawable(R.drawable.clock_hand_hour);
}
mMinuteHand = a.getDrawable(R.styleable.TimeSettableAnalogClock_hand_minute);
if (mMinuteHand == null) {
mMinuteHand = r.getDrawable(R.drawable.clock_hand_minute);
}
mCalendar = new Time();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
}
Обратите внимание, я в значительной степени только удалил com.android.internal.
от ссылки на любой нарисованный или стиль. Остальное такое же, как в конструкторе AnalogClock.
Использование пользовательских элементов рисования БЕЗ атрибутов, устанавливаемых из макета XML
Если вам не нужно, чтобы ваши атрибуты настраивались через XML (например, все ваши аналоговые часы будут выглядеть одинаково в любом месте), вы можете просто решить эту проблему. Вам понадобятся только локальные копии Drawables (как упоминалось в начале), нет attrs.xml
файл нужен. Ваш конструктор должен выглядеть так:
public TimeSettableAnalogClock(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Resources r = getContext().getResources();
mDial = r.getDrawable(R.drawable.clock_dial);
mHourHand = r.getDrawable(R.drawable.clock_hand_hour);
mMinuteHand = r.getDrawable(R.drawable.clock_hand_minute);
mCalendar = new Time();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
}
Как видите, намного короче. Использование в макете XML такое же, но вам не нужно xmlns:custom
часть, и, конечно, вы не можете больше настраивать пользовательские атрибуты:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.widget.TimeSettableAnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
Я могу дополнить свой ответ еще большим количеством кода предлагаемого решения, если вам нужна дополнительная помощь по этому вопросу.