Размер виджета android studio
В ConstraintLayout применяются три способа установки размеров:
Установка точных размеров, например, 123dp
Значение WRAP_CONTENT , которое задает для виджета размеры, достаточные для расположения его содержимого
Значение 0dp , которое эквивалентно значению «MATCH_CONSTRAINT» в коде Java. В этом случае размеры элемента устанавливаются исходя из указанных для него ограничений. По умолчанию элемент занимает все доступное пространство.
Применим все три типа установки размеров:
Здесь создаются три элемента TextView. Все они центрируются по горизонтали, но по вертикали располагаются по верхней и нижней границе контейнера и в центре. Для всех трех TextView для высоты задано значение wrap_content , то есть все три элемента будут занимать ту высоту, которая для них предпочтительна, чтобы вместить содержимое:
Однако для каждого элемента заданы свои настройки ширины. Для верхнего TextView установлены точные размеры — 160 единиц:
Для центрального TextView установлено значение «0dp», благодаря чему элемент по умолчанию будет занимать все доступное для него пространство (в данном случае растягиваться по горизонтали):
Для нижнего TextView установлено значение «wrap_content», то есть элемент будет принимать ту ширину, которая необходима для вмещения его содержимого:
Стоит отметить, что во вложенных виджетах в ConstraintLayout не рекомендуется использовать значение match_parent , которое позволяет виджету занять все доступное пространство. Вместо этого рекомендуется использовать 0dp или «MATCH_CONSTRAINT» — вместе с другими ограничениями они дадут необходимый эффект. Так, для растяжения по ширине контейнера применяются следующие атрибуты:
А для растяжения по высоте контейнера применяются следующие атрибуты:
Например, растяжение TextView по всей длине и ширине контейнера:
Минимальные и максимальные размеры
Ряд атрибутов задают максимальные и минимальные размеры:
layout_constraintWidth_min и layout_constraintHeight_min : представляют соответственно минимальную ширину и высоту
layout_constraintWidth_max и layout_constraintHeight_max : представляют соответственно максимальную ширину и высоту
В качестве значения они принимают точное значение в dp или значение wrap (аналогично wrap_content ). Например:
Хотя в данном случае ширина TextView установлена в 260dp, поскольку максимальная ширина задана в 200dp, то реальная ширина не превысит 200dp.
Размеры в процентах
Атрибут layout_constraintWidth_percent задает ширину элемента в процентах по отношению к доступному пространству по горизонтали. Аналогично атрибут layout_constraintHeight_percent задает высоту в процентах по отношению к доступному пространству по вертикали.
Для их применения необходимо соблюсти следующие условия:
Соответствующий атрибут для установки размера ( android:layout_width — если мы устанавливаем ширину или android:layout_height — если мы устанавливаем ввысоту в процентах) должен иметь значение MATCH_CONSTRAINT или 0dp
Также необходимо установить атрибут app:layout_constraintWidth_default=»percent» при установке ширины и app:layout_constraintHeight_default=»percent» при установке высоты
В качестве значения атрибуты layout_constraintWidth_percent и layout_constraintHeight_percent принимают дробное число от 0 до 1.
Например, пусть TextView занимает по вертикали 25%, а по горизонтали 50% пространства:
Другой пример — пропорциональное разделение пространства между несколькими элементами:
В данном случае текстового поле EditText будет занимать 66%, а кнопка — 33% ширины:
Установка соотношения высоты и ширины
ConstraintLayout также позволяет устанавливать у элементов высоту относительно ширины / ширину относительно высоты. Для этого применяется атрибут layout_constraintDimensionRatio . В качестве значения он принимает отношение в виде Width:Height , например, 1:0.5 — здесь число 1 представляет ширину, а 0.5 — высоту. То есть ширина будет в два раза больше высоты. Но при этом хотя для одного измерения должно быть установлено 0dp ( MATCH_CONSTRAINT ). Например:
В данном случае ширина TextView будет такой, какая необходима для его содержимого, а высота 60% от ширины.
Если и для ширины, и для высоты установлено 0dp , то в этом случае система выберет наибольшее измерение, которое соответствует всем ограничениям, и относительно него установит значение другого измерения. Чтобы конкретизировать измерение, относительно которого будет идти расчет, можно указать символ W (ширина) или H (высота). Например:
В данном случае ширина будет в 4 раза меньше высоты.
Источник
Полный список
— рассмотрим прочие возможности виджета: превью, изменение размера, экран блокировки, ручное обновление
В прошлых уроках мы рассмотрели все основные моменты, касающиеся виджетов. Осталось поговорить о нескольких небольших, но полезных возможностях.
Не буду создавать отдельный проект под это, покажу только фрагменты кода и скрины. Знаний, полученных в предыдущих уроках, должно хватит, чтобы понять, о чем идет речь.
Превью
Когда вы хотите добавить виджет, вы открываете экран выбора и видите там список виджетов с названиями и картинками. По умолчанию, вместо картинки идет иконка приложения. А можно поставить туда свое изображение.
Я сделаю простой виджет, показывающий время при обновлении
Мой виджет называется WidgetFeatures и в списке виджетов он выглядит так:
В качестве превью-изображения сейчас используется иконка приложения и это не очень информативно.
Поставим свою картинку. Я сделаю скрин Home-экрана с моим виджетом, затем обрежу картинку до такого состояния
закину в папку res/drawable и пропишу ее в файле метаданных виджета
Теперь виджет в списке будет выглядеть так:
И пользователь имеет примерное представление, как будет выглядеть виджет после установки.
Изменение размера
Вы можете предоставить пользователю возможность менять размеры виджета.
Для этого необходимо прописать в метаданных:
В этом случае виджет будет способен менять размер и по вертикали и по горизонтали. Если вам нужно только одно направление, оставьте только его в значении атрибута.
Вы можете растянуть виджет на сколько это позволит свободное место на экране. Но не сможете сделать его меньше, чем изначально указанные размеры minHeight и minWidth.
Для этого в API Level 14 появляются еще два атрибута для метаданных: minResizeWidth и minResizeHeight. Они как раз и ставят минимальные значения для изменения размера виджета, а атрибуты minHeight и minWidth становятся значениями по умолчанию и используются для задания первоначального размера виджета при добавлении.
В API Level 16 в классе провайдера появляется метод onAppWidgetOptionsChanged. Метод срабатывает, когда вы меняете размер виджета. Он предоставляет вам Bundle с данными (тип — int, единицы измерения — dp) о новом размере. Данные можно извлечь с помощью этих ключей:
Как видно по названию переменных, система возвращает не точное значение новой ширины или высоты виджета, а диапазон с минимальным и максимальным значением. Не знаю каким образом формируются эти значения, но нам предлагается использовать их, чтобы подстраивать содержимое виджета под новые размеры.
Для примера выведу эти цифры в виджет.
Вот виджет шириной в две ячейки и высотой в одну (2х1).
А вот, что будет, если его растянуть до 3х2
Растянем до 4х3
Данные о текущем размере виджета также можно получить с помощью метода getAppWidgetOptions класса AppWidgetManager. Он вернет вам Bundle с этими же параметрами.
Экран блокировки
Атрибут widgetCategory может определять возможность размещения виджета не только в Home (home_screen), но и на экране блокировки (keyguard) .
В этом случае виджет появится в списке виджетов для экрана блокировки
И после добавления он будет виден
Чтобы программно определить где расположен виджет, используйте снова метод getAppWidgetOptions. Он вернет вам Bundle, из которого по ключу OPTION_APPWIDGET_HOST_CATEGORY можно вытащить категорию (тип int). Она может быть равна либо WIDGET_CATEGORY_HOME_SCREEN (home), либо WIDGET_CATEGORY_KEYGUARD (экран блокировки).
В метаданных вы также можете отдельно задать layout, использующийся для виджета на экране блокировки. Это атрибут android:initialKeyguardLayout. Аналогично атрибуту android:initialLayout указываете там layout-файл. Правда, у меня он почему-то не сработал и виджет с экрана блокировки использовал тот же layout, что и виджет на экране Home. Я не понял, почему.
Ручное обновление виджетов
Обычно система сама посылает broadcast-сообщения о том, что пора обновлять виджет, и минимальный порог, который она позволяет задать – это полчаса. Мы можем обойти это и назначить свое расписание. Есть несколько вариантов, как это организовать и что откуда вызывать. Я покажу один из них.
Все это делается в классе провайдера.
Далее пишем в методах onEnabled и onDisabled:
Содержимое двух этих методов почти одинаково, только первый запускает посылку broadcast сообщения каждую минуту с помощью AlarmManager, а второй отменяет это.
В методе onReceive пишем:
где updateWidget – это ваш метод, в котором прописана логика обновления виджета.
Здесь мы ловим broadcast, который каждую минуту шлет AlarmManager. Далее создаем ComponentName, соответствующий провайдеру виджета нашего приложения и, используя его, получаем список ID виджетов с помощью метода getAppWidgetIds. Далее этот список прогоняем через метод обновления.
И не забудьте поставить в метаданных атрибут updatePeriodMillis = 0, чтобы выключить для виджета рассылку broadcast-сообщений от системы.
Тем самым, мы задали свое расписание обновления. Оно включится при создании первого экземпляра виджета (или при старте системы, если есть экземпляры виджета) и выключится при удалении последнего экземпляра.
На следующем уроке:
— создаем ключи и подписываем приложение
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Полный список
— создаем простой виджет
— разбираемся в его Lifecycle
Добрались таки и до этой долгожданной темы. Вообще, интересно было бы глянуть статистику, какое количество из всех существующих приложений содержит в себе виджеты. Подозреваю, что не чаще, чем одно из десяти. Возможно, ближайшие несколько уроков эту статистику изменят )
Для понимания темы виджетов желательно знать, что такое BroadcastReceiver. Я о нем упоминал в Уроках 96 и 100. Это просто слушатель, который регистрируется в системе, ловит с помощью настроенного Intent Filter сообщения (Intent) и выполняет какой-либо код. Почему-то не сделал я отдельного урока по нему. Но, думаю, еще сделаю. Есть там свои интересные особенности, о которых можно поговорить.
Думаю, нет особой необходимости подробно объяснять, что такое виджеты. Все их видели на своих девайсах. Это что-то типа мини-приложений расположенных на рабочем столе (Home). Они позволяют просмотреть какую-либо информацию из основных приложений, либо повлиять на поведение этих приложений. В качестве примера можно привести – прогноз погоды, текущее время, баланс какого-либо счета, список сообщений в различных мессенджерах, управление состоянием WiFi/3G/GPS/Bluetooth, яркость экрана и т.д. и т.п. В этом уроке сделаем простейший виджет, который отобразит статичный текст.
Чтобы создать простейший виджет нам понадобятся три детали:
В нем мы формируем внешний вид виджета. Все аналогично layout-файлам для Activity и фрагментов, только набор доступных компонентов здесь ограничен следующим списком:
2) XML-файл c метаданными
В нем задаются различные характеристики виджета. Мы пока что укажем следующие параметры:
— layout-файл (из п.1.), чтобы виджет знал, как он будет выглядеть
— размер виджета, чтобы виджет знал, сколько места он должен занять на экране
— интервал обновления, чтобы система знала, как часто ей надо будет обновлять виджет
3) Класс, наследующий AppWidgetProvider. В этом классе нам надо будет реализовать Lifecycle методы виджета.
Давайте создадим три этих детали. Activity нам не понадобится, поэтому не забудьте убрать галку Create Activity в визарде создания нового проекта
Создадим проект без Activity:
Project name: P1171_SimpleWidget
Build Target: Android 2.3.3
Application name: SimpleWidget
Package name: ru.startandroid.develop.p1171simplewidget
Добавим строки в strings.xml:
Создаем layout-файл widget.xml:
RelativeLayout, а внутри зеленый TextView с текстом по центру. Т.е. виджет просто будет показывать текст на зеленом фоне.
Создаем файл метаданных res/xml/widget_metadata.xml:
В атрибуте initialLayout указываем layout-файл для виджета.
Атрибуты minHeight и minWidth содержат минимальные размеры виджета по высоте и ширине.
Есть определенный алгоритм расчета этих цифр. Как вы наверняка замечали, при размещении виджета, экран делится на ячейки, и виджет занимает одну или несколько из этих ячеек по ширине и высоте. Чтобы конвертнуть ячейки в dp, используется формула 70 * n – 30, где n – это количество ячеек. Т.е. если мы, например, хотим, чтобы наш виджет занимал 2 ячейки в ширину и 1 в высоту, мы высчитываем ширину = 70 * 2 – 30 = 110 и высоту = 70 * 1 – 30 = 40. Эти полученные значения и будем использовать в атрибутах minWidth и minHeight.
Атрибут updatePeriodMillis содержит количество миллисекунд. Это интервал обновления виджета. Насколько я понял хелп, указать мы тут можем хоть 5 секунд, но чаще, чем раз в 30 минут (1 800 000) виджет обновляться все равно не будет — это системное ограничение. Давайте пока что поставим интервал 40 минут (2 400 000). В следующих уроках мы разберемся, как самим обновлять виджет с необходимым интервалом.
Осталось создать класс, наследующий AppWidgetProvider.
onEnabled вызывается системой при создании первого экземпляра виджета (мы ведь можем добавить в Home несколько экземпляров одного и того же виджета).
onUpdate вызывается при обновлении виджета. На вход, кроме контекста, метод получает объект AppWidgetManager и список ID экземпляров виджетов, которые обновляются. Именно этот метод обычно содержит код, который обновляет содержимое виджета. Для этого нам нужен будет AppWidgetManager, который мы получаем на вход.
onDeleted вызывается при удалении каждого экземпляра виджета. На вход, кроме контекста, метод получает список ID экземпляров виджетов, которые удаляются.
onDisabled вызывается при удалении последнего экземпляра виджета.
Во всех методах выводим в лог одноименный текст и список ID для onUpdate и onDeleted.
Повторюсь — в onUpdate мы, по идее, должны накодить какое-то обновление виджета. Т.е. если наш виджет отображает, например, текущее время, то при очередном обновлении (вызове onUpdate) надо получать текущее время и передавать эту инфу в виджет для отображения. В этом уроке мы пока не будем с этим возиться.
Осталось немного подрисовать манифест. Добавьте туда ваш класс как Receiver
— укажите для него свои label и icon. Этот текст и эту иконку вы увидите в списке выбираемых виджетов, когда будете добавлять виджет на экран.
— настройте для него фильтр с action = android.appwidget.action.APPWIDGET_UPDATE
— добавьте метаданные с именем android.appwidget.provider и указанием файла метаданных xml/widget_metadata.xml в качестве ресурса
После этого секция receiver в манифесте должна получиться примерно такая:
Виджет готов. Все сохраняем и запускаем. Никаких Activity, разумеется, не всплывет. В консоли должен появиться текст:
\P1171_SimpleWidget\bin\P1171_SimpleWidget.apk installed on device
Done!
Открываем диалог создания виджета и видим в списке наш виджет с иконкой и текстом, которые мы указывали в манифесте для receiver.
Выбираем его и добавляем на экран.
Виджет появился, смотрим логи.
onEnabled
onUpdate [8]
Сработал onEnabled, т.к. мы добавили первый экземпляр виджета. И сразу после добавления, сработал метод onUpdate для этого экземпляра. Видим, что ему назначен (У вас, скорее всего, будет другой ID). Т.е. система добавила экземпляр виджета на экран и вызвала метод обновления с указанием ID экземпляра.
Добавим еще один экземпляр
onEnabled не сработал, т.к. добавляемый экземпляр виджета уже не первый. onUpdate же снова отработал для нового добавленного экземпляра и получил на вход >
Теперь давайте удалим с экрана два этих экземпляра виджета. Сначала второй. В логах увидим:
Сработал onDeleted и получил на вход ID удаляемого экземпляра виджета.
Удаляем первый экземпляр. В логах:
onDeleted [8]
onDisabled
Снова сработал onDeleted — нас оповестили, что экземпляр виджета с был удален. И сработал onDisabled, т.е. был удален последний экземпляр виджета, больше работающих экземпляров не осталось.
Наш виджет обновляется (получает вызов метода onUpdate) раз в 40 минут. Если кому не лень, добавьте снова пару виджетов на экран и подождите. Когда они снова обновятся, в логах это отразится.
Так создается и работает простой виджет. Пока остановимся на этом, чтобы уложились в голове схема создания виджета и его lifecycle-методы. Из интересного хотелось бы еще отметить пару вещей.
BroadcastReceiver
Класс AppWidgetProvider является расширением класса BroadcastReceiver (в манифесте мы его и прописали как Receiver). Он просто получает от системы сообщение в onReceive, определяет по значениям из Intent, какое именно событие произошло (добавление, удаление или обновление виджета), и вызывает соответствующий метод (onEnabled, onUpdate и пр.).
В манифесте мы для нашего Receiver-класса настроили фильтр с action, который ловит события update. Каким же образом этот Receiver ловит остальные события (например, удаление)? Хелп пишет об этом так:
Т.е. ACTION_APPWIDGET_UPDATE – это единственный action, который необходимо прописать явно. Остальные события AppWidgetManager каким-то образом сам доставит до нашего AppWidgetProvider-наследника.
Отступы
Если мы расположим рядом несколько экземпляров виджета, увидим следующую картину
Не очень приятное зрелище. Надо бы сделать отступ.
Добавим android:padding=»8dp» к RelativeLayout в нашем layout-файле
Виджеты на экране поменялись автоматически и теперь выглядят более пристойно.
Кстати, для них сработал onUpdate, смотрите логи. В метод был передан массив ID всех работающих экземпляров виджета.
В Android 4.0 (API Level 14) и выше этот недостаток с отступами был устранен, и вручную делать отступы больше не надо. Давайте проверим. Уберите ранее добавленный в RelativeLayout отступ. И укажите в манифесте android:targetSdkVersion версию 14 (или выше), чтобы система знала, что можно использовать стандартные возможности, а не режим совместимости.
Все сохраняем, запускаем наш виджет на эмуляторе с 4.1. Добавим три экземпляра.
Система сама делает отступы между виджетами.
Получается для версий, ниже 4 надо делать отступ в layout, а для старших версий не надо. Хелп дает подсказку, как сделать так, чтобы ваш виджет корректно работал на всех версиях. Для этого используются квалификаторы версий.
В layout для RelativeLayuot указываете:
И создаете два файла.
res/values/dimens.xml с записью:
и res/values-v14/dimens.xml с записью:
В манифесте android:targetSdkVersion должен быть 14 или выше.
Таким образом, на старых версиях (без системного отступа) отступ будет 8dp, а на новых – 0dp и останется только системный отступ.
На следующем уроке:
— настраиваем виджет при размещении
— работаем с view-компонентами виджета при обновлении
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник