TextView
Компонент TextView предназначен для отображения текста без возможности редактирования его пользователем, что видно из его названия (Text — текст, view — просмотр).
Находится в разделе Texts.
TextView — один из самых используемых компонентов. С его помощью пользователю удобнее ориентироваться в программе. По сути, это как таблички: Руками не трогать, По газону не ходить, Вход с собаками воспрещен, Часы работы с 9.00 до 18.00 и т.д., и служит для представления пользователю описательного текста.
Для отображения текста в TextView в файле разметки используется атрибут android:text, например:
Такой подход является нежелательным. Рекомендуется всегда использовать текстовые ресурсы. В будущем эта привычка позволит вам обеспечить многоязыковую поддержку:
Программная установка текста
Программно текст можно задать методом setText():
Атрибуты
Для всех вышеперечисленных атрибутов в классе TextView есть соответствующие методы для чтения или задания соответствующих свойств.
Программно установим размеры текста при помощи setTextSize() с различными единицами измерения.
По умолчанию у компонентов TextView отсутствует фоновый цвет. Чтобы задать цвет, укажите значение Drawable для атрибута android:background. В качестве значения Drawable может использоваться изображение или XML-представление фигуры, включающий ресурс Drawable (поместить в папку res/drawable).
Программная установка фона
В некоторых случаях программисты из-за невнимательности неправильно меняют фон элемента программным способом и удивляются, почему ничего не работает.
Предположим, у вас определён в ресурсах зелёный цвет:
Следующий код будет ошибочным:
Нужно так (два варианта):
Реагируем на событие onClick
Если вы хотите, чтобы TextView обрабатывал нажатия (атрибут android:onClick), то не забывайте также использовать в связке атрибут android:clickable=»true». Иначе работать не будет!
Многострочный текст
Если вы хотите создать многострочный текст в TextView, то используйте символы \n для переноса строк.
Например, в ресурсах:
Обратите внимание, что в тексте также применяется простое форматирование.
Также перенос на новую строку можно задать в коде:
Увеличиваем интервалы между строками
Вы можете управлять интервалом между соседними строчками текста через атрибут android:lineSpacingMultiplier, который является множителем. Установите дробное значение меньше единицы, чтобы сократить интервал или больше единицы, чтобы увеличить интервал между строками.
Бой с тенью
Чтобы оживить текст, можно дополнительно задействовать атрибуты для создания эффектов тени: shadowColor, shadowDx, shadowDy и shadowRadius. С их помощью вы можете установить цвет тени и ее смещение. Во время установки значений вы не увидите изменений, необходимо запустить пример в эмуляторе или на устройстве. В следующем примере я создал тень красного цвета со смещением в 2 пикселя по вертикали и горизонтали. Учтите, что для смещения используются единицы px (пиксели), единицы dp не поддерживаются.
Программный эквивалент — метод public void setShadowLayer (float radius, float dx, float dy, int color):
Создание ссылок автоматом
У TextView есть ещё два интересных свойства Auto link (атрибут autoLink) и Links clickable (атрибут linksClickable), которые позволяют автоматически создавать ссылки из текста.
Выглядит это следующим образом. Предположим, мы присвоим элементу TextView текст Мой сайт: developer.alexanderklimov.ru и применим к нему указанные свойства.
При этом уже на этапе разработки вы увидите, что строка адреса сайта после слов Мой адрес: стала ссылкой. Если вы запустите приложение и нажмете на ссылку, то откроется браузер с указанным адресом. Вам даже не придется писать дополнительный код. Аналогично, если указать номер телефона (параметр phone), то запустится звонилка.
У ссылки есть интересная особенность — при длительном нажатии на ссылку появляется диалоговое окно, позволяющее скопировать ссылку в буфер обмена.
Атрибут autoLink позволяет комбинировать различные виды ссылок для автоматического распознавания: веб-адрес, email, номер телефона.
Цвет ссылки можно поменять через свойство Text color link (XML-атрибут textColorLink), а программно через метод setTextLinkColor().
Программно можно установить ссылки на текст через класс Linkify:
Кроме константы ALL, можно также использовать Linkify.EMAIL_ADDRESSES, Linkify.MAP_ADDRESSES, Linkify.PHONE_NUMBERS. К сожалению, русские адреса не распознаются. В моём случае индекс был распознан как телефонный номер, а город и улица не стали ссылкой.
В таких случаях придётся самостоятельно добавить ссылки в текстах. Например, определим ссылку в ресурсе:
Присвоим созданный ресурс тексту в TextView и запустим пример. Сам текст будет выглядеть как ссылка, но реагировать не будет. Чтобы исправить данную проблему, добавим код:
Ссылки в тексте выглядят не совсем удобными. Есть отдельная библиотека, которая улучшает функциональность. Описание проблем и ссылка на библиотеку есть в статье A better way to handle links in TextView — Saket Narayan.
Совет: Используйте полупрозрачность с умом
Если вам нужно установить текст полупрозрачным, то не используйте атрибут android:alpha:
Дело в том, что такой подход затрачивает много ресурсов при перерисовке.
Атрибут textColor позволяет установить полупрозрачность без потери производительности:
Выделить текст для копирования
По умолчанию, текст в TextView нельзя выделить для копирования. Но в API 11 появилась такая возможность, которая может пригодиться. Делается либо при помощи XML-атрибута android:textIsSelectable, либо через метод setTextIsSelectable().
Добавьте в разметку два компонента TextView и одно текстовое поле EditText для вставки скопированного текста. У первой текстовой метки установим возможность выделения текста декларативно.
Для второго компонента возможность выделения создадим программно.
Сделайте долгий тап на тексте в любом TextView. Увидите стандартные ползунки для выбора длины текста. Скопируйте текст, сделайте длинный тап в EditText и вставьте текст.
Источник
Полный список
Раньше мы для View-компонентов использовали OnClickListener и ловили короткие нажатия. Теперь попробуем ловить касания и перемещения пальца по компоненту. Они состоят из трех типов событий:
— нажатие (палец прикоснулся к экрану)
— движение (палец движется по экрану)
— отпускание (палец оторвался от экрана)
Все эти события мы сможем ловить в обработчике OnTouchListener, который присвоим для View-компонента. Этот обработчик дает нам объект MotionEvent, из которого мы извлекаем тип события и координаты.
На этом уроке рассмотрим только одиночные касания. А мультитач – на следующем уроке.
Project name: P1021_Touch
Build Target: Android 2.3.3
Application name: Touch
Package name: ru.startandroid.develop.p1021touch
Create Activity: MainActivity
strings.xml и main.xml нам не понадобятся, их не трогаем.
MainActivity реализует интерфейс OnTouchListener для того, чтобы выступить обработчиком касаний.
В onCreate мы создаем новый TextView, сообщаем ему, что обработчиком касаний будет Activity, и помещаем на экран.
Интерфейс OnTouchListener предполагает, что Activity реализует его метод onTouch. На вход методу идет View для которого было событие касания и объект MotionEvent с информацией о событии.
Методы getX и getY дают нам X и Y координаты касания. Метод getAction дает тип события касания:
ACTION_DOWN – нажатие
ACTION_MOVE – движение
ACTION_UP – отпускание
ACTION_CANCEL – практически никогда не случается. Насколько я понял, возникает в случае каких-либо внутренних сбоев, и следует трактовать это как ACTION_UP.
В случае ACTION_DOWN мы пишем в sDown координаты нажатия.
В случае ACTION_MOVE пишем в sMove координаты точки текущего положения пальца. Если мы будем перемещать палец по экрану – этот текст будет постоянно меняться.
В случае ACTION_UP или ACTION_CANCEL пишем в sUp координаты точки, в которой отпустили палец.
Все это в конце события выводим в TextView. И возвращаем true – мы сами обработали событие.
Теперь мы будем водить пальцем по экрану (курсором по эмулятору) в приложении, и на экране увидим координаты начала движения, текущие координаты и координаты окончания движения.
Все сохраним и запустим приложение.
Ставим палец (курсор) на экран
Если вчерашний вечер не удался, голова не болит, рука тверда и не дрожит :), то появились координаты нажатия.
Если же рука дрогнула, то появится еще и координаты перемещения.
Продолжаем перемещать палец и видим, как меняются координаты Move.
Теперь отрываем палец от экрана и видим координаты точки, в которой это произошло
В целом все несложно. При мультитаче процесс немного усложнится, там уже будем отслеживать до 10 касаний.
Если вы уже знакомы с техникой рисования в Android, то вполне можете создать приложение выводящее на экран геометрическую фигуру, которую можно пальцем перемещать. Простейший пример реализации можно посмотреть тут: http://forum.startandroid.ru/viewtopic.php?f=28&t=535.
На следующем уроке:
— обрабатываем множественные касания
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Полный список
— обрабатываем нажатия на виджет
Продолжаем тему виджетов. Виджет, который показывает информацию – это хорошо, это мы теперь умеем. Но кроме этого виджет еще умеет реагировать на нажатия.
Т.к. прямого доступа к view-компонентам виджета мы не имеем, то использовать, как обычно, обработчики нажатий не получится. Но RemoteViews, используемый нами для работы с view, позволяет настроить реакцию view на нажатие. Для этого он использует PendingIntent. Т.е. мы можем на нажатие на виджет повесить вызов Activity, Service или BroadcastReceiver. В этом уроке сделаем непростой, но достаточно содержательный пример, отражающий различные техники реагирования на нажатия.
Создадим виджет, состоящий из двух текстов и трех зон для нажатий.
Первый текст будет отображать время последнего обновления, а второй – кол-во нажатий на третью зону нажатия.
Первая зона будет по клику открывать конфигурационное Activity. Это пригодится в том случае, когда вы хотите дать пользователю возможность донастроить виджет после установки. Конфигурировать будем формат отображаемого в первой строке времени.
Вторая зона нажатия будет просто обновлять виджет, тем самым будет меняться время в первом тексте.
Каждое нажатие на третью зону будет увеличивать на единицу счетчик нажатий и обновлять виджет. Тем самым будет меняться второй текст, отображающий текущее значение счетчика.
Для простоты, конечно, можно было разбить этот пример на три отдельных виджета. Но я решил сделать все в одном, чтобы наглядно показать, что один виджет может совершать разные действия в ответ на нажатия на разные view.
Создадим проект без Activity:
Project name: P1201_ClickWidget
Build Target: Android 2.3.3
Application name: ClickWidget
Package name: ru.startandroid.develop.p1201clickwidget
Layout-файл виджета widget.xml:
Первые два TextView – это тексты, а последние три – зоны нажатия.
Layout-файл для конфигурационного экрана config.xml:
Поле для ввода формата даты и кнопка подтверждения
Класс конфигурационного экрана ConfigActivity.java:
Тут ничего нового для нас нет.
В onCreate мы извлекаем и проверяем ID экземпляра виджета, для которого открылся конфигурационный экран. Далее формируем отрицательный ответ на случай нажатия кнопки Назад. Читаем формат времени и помещаем его в EditText. Читаем значение счетчика и, если это значения еще нет в Preferences, то пишем туда 0.
В onClick мы сохраняем в Preferences формат из EditText, обновляем виджет, формируем положительный ответ и выходим.
Код обновления виджета пока закоментен, т.к. у нас еще нет класса MyWidget. Сейчас создадим и можно будет раскоментить.
Класс виджета MyWidget.java:
А вот тут уже немного посложнее.
В onUpdate мы обновляем все требующие обновления экземпляры, в onDelete подчищаем Preferences после удаления экземпляров.
Метод updateWidget отвечает за обновления конкретного экземпляра виджета. Здесь мы настраиваем внешний вид и реакцию на нажатие.
Сначала мы читаем настройки формата времени (которые были сохранены в конфигурационном экране), берем текущее время и конвертируем в строку согласно формату. Также из настроек читаем значение счетчика. Создаем RemoteViews и помещаем время и счетчик в соответствующие TextView.
Далее идет настройка обработки нажатия. Механизм несложен. Сначала мы готовим Intent, который содержит в себе некие данные и знает куда он должен отправиться. Этот Intent мы упаковываем в PendingIntent. Далее конкретному view-компоненту мы методом setOnClickPendingIntent сопоставляем PendingIntent. И когда будет совершено нажатие на этот view, система достанет Intent из PendingIntent и отправит его по назначению.
В нашем виджете есть три зоны для нажатия. Для каждой из них мы формируем отдельный Intent и PendingIntent.
Первая зона – по нажатию должно открываться конфигурационное Activity. Создаем Intent, который будет вызывать наше Activity, помещаем данные об ID (чтобы экран знал, какой экземпляр он настраивает), упаковываем в PendingIntent и сопоставляем view-компоненту первой зоны.
Вторая зона – по нажатию должен обновляться виджет, на котором было совершено нажатие. Создаем Intent, который будет вызывать наш класс виджета, добавляем ему action = ACTION_APPWIDGET_UPDATE, помещаем данные об ID (чтобы обновился именно этот экземпляр), упаковываем в PendingIntent и сопоставляем view-компоненту второй зоны.
Третья зона – по нажатию должен увеличиваться на единицу счетчик нажатий. Создаем Intent, который будет вызывать наш класс виджета, добавляем ему наш собственный action = ACTION_CHANGE, помещаем данные об ID (чтобы работать со счетчиком именно этого экземпляра), упаковываем в PendingIntent и сопоставляем view-компоненту третьей зоны.
Теперь при нажатии на первую зону будет вызван конфигурационный экран. По нажатию на вторую будет обновлен виджет. А вот нажатие на третью ни к чему не приведет, т.к. наш класс MyWidget знает, как работать с Intent с action вида ACTION_APPWIDGET_UPDATE, ACTION_APPWIDGET_DELETED и пр. А мы ему послали свой левый action.
Значит надо научить его понимать наш Intent. Вспоминаем, что MyWidget – это расширение AppWidgetProvider, а AppWidgetProvider – это расширение BroadcastReceiver. А значит, мы можем сами реализовать метод onReceive, в котором будем ловить наш action и выполнять нужные нам действия.
В методе onReceive мы обязательно выполняем метод onReceive родительского класса, иначе просто перестанут работать обновления и прочие стандартные события виджета. Далее мы проверяем, что intent содержит наш action, читаем и проверяем ID из него, читаем из настроек значение счетчика, увеличиваем на единицу, пишем обратно в настройки и обновляем экземпляр виджета. Он прочтет новое значение счетчика из настроек и отобразит его.
Вы обратили внимание, что при создании PendingIntent мы использовали ID экземпляров виджета в качестве requestCode? Поясняю, зачем это сделано. Допустим, мы создаем два экземпляра виджета. Первый создается и создает свои PendingIntent для обновления, счетчика и конфигурирования. Эти PendingIntent содержат action и extra-данные. Теперь создается второй экземпляр. Он также пытается создать свои PendingIntent с теми же action и другими extra-данными. Тут мы вспоминаем прошлый урок, а именно дефолтное поведение системы. Если создаваемый PendingIntent похож на существующий, то создаваемый станет копией уже существующего. Т.е. все PendingIntent второго экземпляра получат extra-данные из Intent первого. В extra-данных у нас лежит ID экземпляра. Значит второй экземпляр виджета будет обновлять время/счетчик и открывать конфигурационный экран первого экземпляра. Если интересно, можете поставить нули вместо ID при создании PendingIntent и убедиться, что так все и будет. Чтобы избежать этого, используем requestCode. Надеюсь, что этот момент понятен, т.к. для этого и была написана бОльшая часть прошлого урока )
Теперь не забудьте раскаментить код обновления виджета в классе ConfigActivity в методе onClick. Иначе ничего работать не будет.
Создадим файл метаданных xml/widget_metadata.xml:
Виджет будет вертикальным. Число 0 – в updatePeriodMillis говорит о том, что виджет не будет обновляться системой. Мы его сами обновлять будем.
Осталось прописать классы в манифесте. Должен получиться примерно такой фрагмент кода:
Если для Receiver не указана иконка и текст, он возьмет их из приложения.
Т.е. наш виджет будет иметь стандартную системную иконку и имя приложения – ClickWidget.
Все сохраняем и инсталлим приложение.
Для наглядности давайте создадим пару экземпляров виджета. Настройки в конфигурационном экране пока оставляйте дефолтными.
Виджеты отображают время, когда они были последний раз обновлены и счетчик нажатий.
Теперь понажимайте Update на обоих виджетах, время будет обновляться. А, нажимая Count, вы меняете значение счетчика, и виджет это отображает. Вместе со счетчиком, кстати, актуализируется и время, т.к. оно актуализируется при каждом обновлении виджета.
Нажав на Config, мы попадаем в конфигурационный экран. Здесь можно изменить формат отображаемого времени. Настроим так, чтобы первый экземпляр отображал только часы и минуты
а второй – секунды
Предлагаю вам самостоятельно допилить виджет так, чтобы при нажатии на Count обновлялся только счетчик, а время не менялось. Также попробуйте добавить еще одну (четвертую) зону, по нажатию на которую открывался бы, например, www.google.com в браузере.
На всякий случай проговорю явно следующее. В нашем примере мы при нажатиях вызывали через Intent свои же классы. Но, думаю, всем понятно, что можно вызывать все, что позволит Intent, никаких ограничений нет.
На следующем уроке:
— создаем виджет со списком
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник