Здравствуйте, уважаемые читатели сайта Uspei.com. Сегодня мы успеваем на финишную прямую по изучению базового курса HTML. Сегодня мы изучим теги DIV и SPAN, которые нам понадобятся при изучении курса CSS. Это логические теги и они никак сами по себе не проявляют себя, но (!), тем не менее, это одни из самых главных тегов в CSS. Давайте рассмотрим на примере.
Если мы объединим какой-либо текст тегом
Вы будете приятно удивлены, когда узнаете, что для HTML справедливо правило 80/20, которое означает, что вам не придется изучать весь язык до последнего винтика. Но, в то же время, полученных из курса знаний вам с лихвой хватит для того, чтобы уверенно делать сайты. К стати для этого у меня есть целый раздел о том, как самому сделать сайт.
Я думаю, что вы понимаете всю силу этого тега. Вам придется использовать его чаще других. Если вы посмотрите исходный код ЛЮБОГО сайта, то вы увидите, что этого тег является практически основным. Данным тегом можно задать фоновое изображение, изменить размеры блока, а также много много других изменений элементов.
Тег span
Но это больше относится к CSS, который мы будем проходить в будущем.
Тег предназначен для определения встроенных элементов объекта. В отличие от блочных элементов, таких как
,
) можно изменить цвет и размер первой буквы, если добавляем начальный и конечный тег и определить для него стиль контента. Чтобы не описывать каждый раз стиль внутри тега, можно выделить стиль во внешнюю таблицу стилей, а для тега добавляем параметр class или id с именем селектора.
Синтаксис
Закрывающий тег
Пример 1. Использование тега
ШТМЛ 4.01 IE 5.5 IE 6 IE 7 Op 9.5 Sa 3.1 Ff 2.0 Ff 3.0
Результат данного примера показан ниже.
Рис. 1. Вид контента, оформленного с помощью тега и стилей
Разница между блочными и строчными элементами следующая.
В примере 1 показано использование тега для выделения отдельных слов.
Пример 1. Применение строчных элементов
Результат примера показан ниже (рис. 1).
Рис. 1. Текст, оформленный с помощью стилей
В данном примере тег и стили используются для выделения различными способами фрагментов текста. В частности, выделение происходит за счет фонового цвета, рамки вокруг текста и сменой его цвета.
Пример 2. Свойство display
Результат примера приведен на рис. 2.
Рис. 2. Замена блочного элемента на строчный
В данном примере блочный тег
отображается на веб-странице как строчный элемент. Это требуется для того, чтобы ширина фона и рамки равнялась ширине самого текста с учетом полей. А, как известно, ширина блочных элементов не зависит от ширины содержимого, поэтому и приходится представлять тег
в виде строчного элемента. В принципе, аналогичным решением будет использовать вместо
Строчные элементы применяются не только для управления видом текста, но также и при верстке веб-страниц, например, для изменения положения слоев. В примере 3 показано, как рисунок накладывать поверх блока с текстом.
Пример 3. Наложение слоев
Результат примера представлен на рис. 3.
Рис. 3. Положение рисунка относительно текста
В примере 3 можно вообще отказаться от добавления тега
Пример 4. Использование тега SPAN
Результат примера показан ниже (рис. 4).
Рис. 4. Положение рисунка относительно текста
За счет того, что тег не начинается с новой строки, рисунок и текст в данном примере находятся на одной линии. Поэтому вертикального отступа под текстом, как в случае предыдущего примера, здесь нет. Зато появился отступ сверху.
Резюме
Для верстки строчные элементы применяются реже, чем элементы блочные. Это связано в основном с тем, что внутрь строчных элементов не допускается вкладывать контейнеры
и подобные широко распространенные теги. Тем не менее, блочные и строчные элементы удачно дополняют друг друга, поскольку позволяют на всех уровнях менять вид составляющих веб-страниц.
Как язык, так и платформа существуют уже много лет: и все это время существовало множество средств для работы с неуправляемым кодом. Так почему же сейчас выходит очередной API для работы с неуправляемым кодом если по сути он существовал уже много-много лет? Для того чтобы ответить на этот вопрос достаточно понять чего не хватало нам раньше.
Примечание
Глава, опубликованная на Хабре не обновляется и возможно, уже несколько устарела. А потому, прошу обратиться за более свежим текстом к оригиналу:
Как результат такого отношения — все меньшее и меньшее содержание unsafe кода в проектах и все большее доверие к API самой платформы. Это легко проверяется если поискать использование конструкции stackalloc по открытым репозиториям: оно ничтожно мало. Но если взять любой код, который его использует:
Span[T], ReadOnlySpan[T]
Тип Span олицетворяет собой часть некоторого массива данных, поддиапазон его значений. При этом позволяя как и в случае массива работать с элементами этого диапазона как на запись, так и на чтение. Однако, давайте для разгона и общего понимания сравним типы данных, для которых сделана реализация типа Span и посмотрим на возможные цели его введения.
Первый тип данных, о котором хочется завести речь — это обычный массив. Для массивов работа со Span будет выглядеть следующим образом:
Как мы видим в данном примере, для начала мы создаем некий массив данных. После этого мы создаем Span (или подмножество), который ссылаясь на сам массив, разрешает его использующему коду доступ только в тот диапазон значений, который был указан при инициализации.
Тут мы видим первое свойство этого типа данных: это создание некоторого контекста. Давайте разовьем нашу идею с контекстами:
Т.е., получается, что Span — это средство унификации по работе с памятью: управляемой и неуправляемой, которое гарантирует безопасность в работе с такого рода данными во время Garbage Collection: если участки памяти с управляемыми массивами начнут двигаться, то для нас это будет безопасно.
Однако, стоит ли так сильно радоваться? Можно ли было всего этого добиться и раньше? Например, если говорить об управляемых массивах, то тут даже сомневаться не приходится: достаточно просто обернуть массив в еще один класс, предоставив аналогичный интерфейс и все готово. Мало того, аналогичную операцию можно проделать и со строками: они обладают необходимыми методами. Опять же, достаточно строку завернуть в точно такой же тип и предоставить методы по работе с ней. Другое дело что для того чтобы хранить в одном типе строку, буфер или массив, придется сильно повозиться, храня в едином экземпляре ссылки на каждый из возможных вариантов (активным, понятное дело, будет только один):
Отсюда мы можем сформулировать определение типа Span и связанного с ним readonly типа ReadOnlySpan:
Span — это тип данных, обеспечивающий единый интерфейс работы с разнородными типами массивов данных а также возможность передать в другой метод подмножество этого массива таким образом чтобы вне зависимости от глубины взятия контекста скорость доступа к исходному массиву была константной и максимально высокой.
И действительно: если мы имеем примерно такой код:
Span[T] на примерах
Человек так устроен что зачастую пока он не получит определенного опыта, то конечного понимания, для чего необходим инструмент часто не приходит. А потому, поскольку нам нужен некий опыт, давайте обратимся к примерам.
ValueStringBuilder
Каков основной минус системного типа StringBuilder? Это конечно же его суть: как он сам, так и то, на чем он основан (а это массив символов char[] ) — являются типами ссылочными. А это значит как минимум две вещи: мы все равно (хоть и немного) нагружаем кучу и второе — увеличиваем шансы промаха по кэшам процессора.
Еще один вопрос, который у меня возникал к StringBuilder — это формирование маленьких строк. Т.е. когда результирующая строка «зуб даю» будет короткой: например, менее 100 символов. Когда мы имеем достаточно короткие форматирования, к производительности возникают вопросы:
Взглянем на тип из недр mscorlib :
StringBuilder — это класс, внутри которого находится ссылка на массив символов. Т.е. когда вы создаете его то по сути создается как минимум два объекта: сам StringBuilder и массив символов в как минимум 16 символов (кстати именно поэтому так важно задавать предполагаемую длину строки: ее построение будет идти через генерацию односвязного списка 16-символьных массивов. Согласитесь, расточительство). Что это значит в контексте нашего разговора о типе ValueStringBuilder: capacity по-умолчанию отсутствует, т.к. он заимствует память извне плюс он сам является значимым типом и заставляет пользователя расположить буфер для символов на стеке. Как итог весь экземпляр типа помещается на стек вместе с его содержимым и вопрос оптимизации здесь становится решенным. Нет выделения памяти в куче? Нет проблем с проседанием производительности по куче. Но вы мне скажите: почему тогда не пользоваться ValueStringBuilder (или его самописной версией: сам он internal и нам не доступен) всегда? Ответ такой: надо смотреть на задачу, которая вами решается. Будет ли результирующая строка известного размера? Будет ли она иметь некий известный максимум по длине? Если ответ «да» и если при этом размер строки не выходит за некоторые разумные границы, то можно использовать значимую версию StringBuilder. Иначе, если мы ожидаем длинные строки, переходим на использование обычной версии.
ValueListBuilder
Правила и практика использования
Для того чтобы окончательно понять суть нового типа данных, необходимо «поиграться» с ним, написав пару-тройку, а лучше — больше методов, его использующих. Однако, основные правила можно почерпнуть уже сейчас:
В остальных случаях стоит присмотреться либо к Memory либо использовать классические типы данных.
Как работает Span
Для примера возьмем работу Span для строк:
Где string.GetRawStringData() выглядит следующим образом:
Т.е. получается, что метод лезет напрямую вовнутрь строки, а спецификация ref char позволяет отслеживать GC неуправляемую ссылку во внутрь строки, перемещая его вместе со строкой во время срабатывания GC.
Span[T] как возвращаемое значение
то все выглядит крайне логично и хорошо. Однако, стоит заменить одну инструкцию другой:
как компилятор запретит инструкцию такого вида. Но прежде чем написать, почему, я прошу вас самим догадаться, какие проблемы понесет за собой такая конструкция.
Итак, я надеюсь, что вы подумали, построили догадки и предположения, а может даже и поняли причину. Если так, главу про стек потока я по винтикам расписывал не зря. Ведь дав таким образом ссылку на локальные параменные метода, который закончил работу, вы можете вызвать другой метод, дождаться окончания его работы и через x[0.99] прочитать его локальные переменные.
Однако, к счастью, когда мы делаем попытку написать такого рода код, компилятор дает на по рукам, выдав предупреждение: CS8352 Cannot use local ‘reff’ in this context because it may expose referenced variables outside of their declaration scope и будет прав: ведь если обойти эту ошибку, то возникет возможность, например, находясь в плагине подстроить такую ситуацию что станет возможным украсть чужие пароли или повысить привилегии выполнения нашего плагина.
Если появились вопросы
Если касательно Span появились вопросы, давайте обсудим. Типы данных очень свежие и практически никем не используются, а потому разобрать use cases очень и очень сложно.
Давайте рассмотрим, как использовать span, что предоставляют span-ы “из коробки”, как проще создать свой span и наконец как их тестировать:
Стилизация текста в Android
Android предлагает несколько способов стилизации текста:
Multi style подразумевает добавление нескольких стилей к одному и тому же тексту. Например, сделать одно слово со стилем italic а другое bold. Мульти стили могут быть реализованы через использование HTML тегов, span-ов или управлением отрисовки текста на холсте Canvas.
Что касается рисования текста на canvas, то это следует использовать, если нужно сделать что-то, что не поддерживается платформой, например рисование текста по кривой.
Вы можете комбинировать простые стили и мульти стили. Вы можете рассматривать стили, которые вы применяете к TextView как “базовые” стили. Стилизация текста через span-ы применяется “поверх” базового стиля и переписывает базовый стиль. Например, когда меняет цвет текста TextView через атрибут textColor=”@color.blue” и применяем ForegroundColorSpan(Color.PINK) для первых 4 символов текста, то первые 4 символа будут розовыми (потому что span переопределил основной стиль), а цвет остальных символов будет определятся цветом, заданным через атрибут.
Применение Span-ов
Как решить какой класс нужно использовать:
Spannable (расширяет Spanned )-> неизменный текст и изменяемая разметка
Примените span через вызов setSpan(Object what, int start, int end, int flags) на Spannable объекте. Здесь what — это объект, который будет применен к тексту с позиции start и по позицию end текста. Флаг указывает, должен ли диапазон расширяться, чтобы включить текст, вставленный в их начальную или конечную точку, или нет. Независимо от того, какой флаг установлен, всякий раз, когда текст вставлен в положение больше начальной точки и меньше конечной точки, диапазон автоматически расширяется.
Например, настройка ForegroundColorSpan может быть такой:
Множество span-ов может быть составлено и присоединено к одному и тому же текстовому сегменту. Например, текст, выделенный как полужирным, так и красным, можно построить таким образом:
Framework spans
Android framework предлагает более 20 классов и интерфейсов в пакете android.text.stylepackage для работы со span-ами, производных от главных интерфейсов и абстрактных классов. Мы сгруппировали span-ы на группы:
Span-ы которые влияют на представление и метрику текста
Span-ы влияющие на метрику — изменяют размеры лайаута и требуют повторного рендеринга компонентов.
Для примера, рассмотрим Spannable объект установленный и извлеченный следующим образом:
Вот как мы можем установить изменить внешний вид, применив span:
Вариант 1: Изменение внешнего вида текста, без изменеия размеров TextView
Вариант 2: Изменение размеров текста
Character vs paragraph affecting spans
Например, CharacterStyle span BackgroundColorSpan может быть прикреплен к любому символу в тексте. Ниже мы прикрепили его начиная с 5-го до 8-го символа:
Создание кастомных span-ов
Когда вы реализовываете свой span, вы должны решить на каком уровне вы хотите стилизовать текст: на символьном уровне или на уровне параграфа, должны ли стилизация приводить к перекомпоновке view или только перерисовывать текст, без изменения размера view. Но прежде написания своего кода, проверьте существует ли требуемая функциональность в классах фреймворка.
Замечание: точно такой же эффект может быть достигнут если применить оба существующих класса RelativeSizeSpan и ForegroundColorSpan к тексту.
Тестирование кастомных span-ов
Ниже пример кода для тестирования.
Тестирование использования span-ов
Spanned интерфейс позволяет настраивать и извлекать span-ы из текста. Проверьте что span-ы корректно добавлены в нужное место через реализацию Android JUnit теста. В android-text sample мы конвертируем теги разметки точки (bullet point) в маркеры (которые будут отрисованы в TextView). Это можно сделать прицепив BulletPointSpans к тексту, в нужную позицию. Ниже код как это можно протестировать:
Посмотрите MarkdownBuilderTest для других примеров тестов.
В будущих статьях мы расскажем больше о работе span-ов “под капотом” и как их эффективно использовать. Оставайтесь с нами!