Использование XML Drawables в приложениях

Автор: | 23.01.2018

Если вы хотите использовать PNG- или JPEG-изображения в своём приложении, вы должны будете предоставить несколько копий этих изображений для разных плотностей экрана. При большом количестве таких изображений это может сделать ваше приложение весьма громоздким. Решение здесь — использовать вместо картинок изображения, описанные с помощью XML.

Фактически, XML Drawable это серия команд, которая описывает, как нарисовать изображение на экране. Поэтому они могут изменяться под любую плотность экрана без какой-либо потери качества. Мы можем манипулировать ими даже когда приложение работает.

Недостатком такого подхода может являться то, что такие изображения можно использовать только там, где используются обычные Drawable. Другой проблемой может являться то, что их рисование иногда занимает больше времени, чем рисование растрового изображения. Однако эти минусы не так существенны перед возможностями модифицировать изображения и уменьшить размер APK приложения. В этой статье мы разберём несколько примеров реализации XML Drawable в приложении.

Основные элементы

При рисовании изображения в XML главным тегом является <shape> — корневой элемент для геометрической фигуры. Он имеет следующие атрибуты:

  • android:shape — тип фигуры. Может принимать значения oval, rectangle, ring, line. Если атрибут не задан, то по умолчанию берётся rectangle.
  • android:innerRadius — радиус внутренней части кольца. Используется для ring.
  • android:innerRadiusRatio — радиус внутренней части кольца, выраженный как отношение ширины кольца. Используется для ring.
  • android:thickness — толщина кольца. Используется для ring.
  • android:thicknessRatio — толщина кольца, выраженная как отношение ширины кольца. Используется для ring.
  • android:useLevel — true если используется как LevelListDrawable. Обычно должен быть false, иначе ваша фигура может не отобразиться.

Затем внутри тега <shape> используются следующие теги:

  • <corners> — создаёт закруглённые углы для фигуры. Применяется только тогда, когда в атрибуте android:shape задано значение rectangle. Атрибуты:
    • android:radius — радиус закругления углов. В данном случае все углы будут иметь одинаковый радиус, однако вы можете переопределить радиус отдельно для каждого угла с помощью атрибутов ниже.
    • android:topLeftRadius — верхний левый угол.
    • android:topRightRadius — верхний правый угол.
    • android:bottomLeftRadius — нижний левый угол.
    • android:bottomRightRadius — нижний правый угол.
  • <gradient> — задаёт параметры градиента для фигуры. Атрибуты:
    • android:angle — угол для градиента, в градусах. Должен быть кратным 45.
    • android:centerX — смещение X-координаты относительно центра градиента.
    • android:centerY — смещение Y-координаты относительно центра градиента.
    • android:startColor — начальный цвет градиента.
    • android:centerColor — дополнительный цвет, который находится между начальным и конечным цветами.
    • android:endColor — конечный цвет градиента.
    • android:gradientRadius — радиус градиента. Применяется только при типе градиента radial,
    • android:useLevel — true, если используется как LevelListDrawable.
    • android:type — тип градиента. Может иметь следующие значения: linear — линейный градиент (по умолчанию); radial — радиальный, начальный цвет находится в центре; sweep — развёрточный градиент, который идёт по кругу вокруг центра.
  • <solid> — заполняет фигуру сплошным цветом. Атрибуты:
    • android:color — цвет заливки.
  • <stroke> — рисует линию для фигуры. Можно рассматривать как границу фигуры. Атрибуты:
    • android:width — толщина линии.
    • android:color — цвет линии.
    • android:dashWidth — толщина каждого штриха. Работает только в случае, если задан атрибут android:dashGap.
    • android:dashGap — расстояние между штрихами. Работает только в случае, если задан атрибут android:dashWidth.
  • <size> — задаёт размер фигуры. Атрибуты:
    • android:width — ширина фигуры.
    • android:height — высота фигуры.
  • <padding> — отступы, применяемые к содержимому элемента View. Меняет отступы только View, не самой фигуры. Атрибуты:
    • android:left — отступ слева.
    • android:right — отступ справа.
    • android:top — отступ сверху.
    • android:bottom — отступ снизу.

Теперь, когда мы знаем, из каких элементов составляются изображения, посмотрим несколько примеров того, как можно.

Круг с градиентом

Вы можете использовать XML Drawables для создания элементов дизайна или функциональных элементов интерфейса. Например, можно нарисовать вот такой круг и залить его градиентом.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

  <gradient
      android:angle="135"
      android:centerColor="#5d8fbf"
      android:endColor="#adcfff"
      android:startColor="#065cae"
      android:type="linear"/>

  <size
      android:height="100dp"
      android:width="100dp"/>

  <stroke
      android:color="#FFFFFF"
      android:width="2.3dp"/>
</shape>

В результате получим вот такое изображение.

Фон для кнопки

Кнопкам в Android можно задавать различный фон, в том числе и собственноручно нарисованный. Это довольно просто и не требует особо большого количества элементов. Например, кнопку ниже можно использовать для навигации в приложении.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

  <corners
      android:bottomRightRadius="12dp"
      android:radius="4dp"
      android:topRightRadius="12dp"/>

  <gradient
      android:angle="90"
      android:centerX="0.5"
      android:endColor="#7bce70"
      android:startColor="#0e860e"
      android:type="linear"/>

  <size
      android:height="50dp"
      android:width="100dp"/>
</shape>

Фон для текста

Аналогично кнопка, TextView также можно задать любой фон. Например, вот так:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

  <gradient
      android:angle="90"
      android:centerColor="#FFFFFF"
      android:endColor="#550ce1f4"
      android:startColor="#550ce1f4"/>

  <padding
      android:bottom="5dp"
      android:left="5dp"
      android:right="5dp"
      android:top="5dp"/>

  <corners
      android:bottomLeftRadius="10dp"
      android:bottomRightRadius="10dp"
      android:topLeftRadius="10dp"
      android:topRightRadius="10dp"/>
</shape>

Или так:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <corners android:radius="4dp"/>

  <stroke
      android:color="#CCFFFF"
      android:width="4dp"/>

  <gradient
      android:endColor="#CCFFFF"
      android:gradientRadius="250"
      android:startColor="#0078a5"
      android:type="radial"
      />

  <padding
      android:bottom="30dp"
      android:left="30dp"
      android:right="30dp"
      android:top="30dp"/>
</shape>

Использование Layer List

Бывают ситуации, когда нужно составить изображение из разных фигур. Это возможно с помощью тега <layer-list>, который позволяет комбинировать элементы <shape>. Каждый элемент внутри <layer-list> рисуется в порядке списка — последний в списке рисуется поверх остальных слоёв. Каждая фигура <shape> помещает в тег <item>, который представляет собой элемент списка.

Например, можно создать вот такую комбинацию из двух эллипсов, которую можно использовать как метку для маркированного списка.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:gravity="center">
    <shape android:shape="oval">
      <size
          android:height="14dp"
          android:width="14dp"/>
      <stroke
          android:color="#b3b3b3"
          android:width="1dp"/>
    </shape>
  </item>
  <item
      android:bottom="3dp"
      android:left="3dp"
      android:right="3dp"
      android:top="3dp">
    <shape android:shape="oval">
      <size
          android:height="10dp"
          android:width="10dp"/>
      <solid android:color="#b3b3b3"/>
    </shape>
  </item>
</layer-list>

А с помощью кода ниже можно создать подобие почтовой марки:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape>
      <solid android:color="#ffffff"/>
      <stroke
          android:color="#ffffff"
          android:dashGap="4dp"
          android:dashWidth="4dp"
          android:width="5dip"/>
      <corners android:radius="4dip"/>
      <padding
          android:bottom="5dip"
          android:left="5dip"
          android:right="5dip"
          android:top="5dip"/>
    </shape>
  </item>
  <item>
    <shape>
      <corners android:radius="6dp"/>
      <solid android:color="#28a69a"/>
      <stroke
          android:color="#FFFFFF"
          android:width="1dip"/>
    </shape>
  </item>
  <item>
    <bitmap android:src="@drawable/ic_launcher"/>
  </item>
</layer-list>

Использовать фигуры одного типа необязательно, можно использовать разные типы. Нарисуем иконку, комбинирующую прямоугольник и линию, перечёркивающую его.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape android:shape="rectangle">
      <size
          android:height="15dp"
          android:width="30dp"/>
      <solid android:color="@color/primary"/>
      <stroke
          android:color="#ffffff"
          android:width="2dp"
          />
    </shape>
  </item>
  <item>
    <rotate
        android:fromDegrees="120">
      <shape
          android:shape="line">
        <stroke
            android:color="#ffffff"
            android:width="2dp"
            />
      </shape>
    </rotate>
  </item>
</layer-list>

StateDrawable

У некоторых элементов интерфейса существуют различные состояния, с которыми можно работать. Например, у Button можно отслеживать состояние pressed (нажат) и менять при этом фон кнопки. Для этого создадим селектор, который будет отслеживать состояния кнопки и ставить нужный фон.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      android:state_pressed="true"
      android:state_enabled="true"
      android:drawable="@drawable/button_pressed" />
  <item
      android:state_focused="true"
      android:state_enabled="true"
      android:drawable="@drawable/button_focused" />
  <item
      android:state_enabled="true"
      android:drawable="@drawable/button_enabled" />
</selector>

У селектора есть следующие атрибуты, определяющие состояние элемента:

  • android:state_pressed — когда пользователь нажимает на элемент.
  • android:state_focused — когда на элементе установлен фокус ввода.
  • android:state_enabled — когда элемент доступен.
  • android:state_checked — когда элемент в списке был отмечен.

С помощью значений true\false определяем, какое состояние нужно отследить и в атрибут android:drawable добавляем нужный фон.

button_pressed.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <corners android:radius="4dp"/>

  <solid android:color="#cc2323"/>
</shape>

button_focused.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <corners android:radius="4dp"/>

  <solid android:color="#2364cc"/>
</shape>

button_enabled.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <corners android:radius="4dp"/>

  <solid android:color="#3ccc23"/>
</shape>

В результате получим следующую кнопку, которая будет менять свой цвет в зависимости от состояния.

Кнопки это не единственные элементы, у которых можно отслеживать состояния. Например, это можно делать также для элементов RadioButton и CheckBox с помощью атрибута android:state_checked. Например, с помощью кода ниже можно добавить в приложение свою картинку, которой будет отмечаться выбранный элемент CheckBox.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/ic_action_check" android:state_checked="true"/>
  <item android:drawable="@drawable/ic_action_uncheck"/>
</selector>

Аналогичным образом отслеживается состояние у RadioButton.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/ic_radio_checked" android:state_checked="true"/>
  <item android:drawable="@drawable/ic_radio_unchecked"/>
</selector>

В качестве drawable здесь может быть любое изображение, в нашем приложении «Карточки для детей» с помощью селекторов используются белые галочки и фон.

Также в селекторе вместо изображений можно рисовать свои собственные фигуры, как это показано ниже.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true">
    <shape>
      <solid android:color="#f5b000"/>
      <stroke android:color="#000000" android:width="2dp"/>
      <corners android:radius="8dp"/>
      <padding android:bottom="8dp" android:left="88dp" android:right="88dp" android:top="8dp"/>
    </shape>
  </item>
  <item>
    <shape>
      <solid android:color="#f9b903"/>
      <stroke android:color="#000000" android:width="2dp"/>
      <corners android:radius="8dp"/>
      <padding android:bottom="8dp" android:left="88dp" android:right="88dp" android:top="8dp"/>
    </shape>
  </item>
</selector>

Здесь мы при нажатии на кнопку делаем её чуть темнее.

Как можно увидеть, у XML Drawables есть множество применений, которое ограничивается лишь фантазией разработчика. Рисование своих изображений поможет сделать ваше приложение уникальнее и заметнее среди других.

Использование XML Drawables в приложениях: 1 комментарий

  1. Ратмир

    Спасибо за познавательную статью!
    А есть ли способ добавить тень контролу в api V17 — 19 ?
    Спасибо.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *