Стандартный ProgressBar, как известно, является индикатором выполнения длительных процессов. Однако, его часто используют для наглядного отображения численных величин. К сожалению, этот элемент не поддерживает метода setText(), чтобы поместить на него надпись, поясняющую показываемую величину, что значительно экономило бы экранное пространство. Вспомните адресную строку в стандартном браузере, где позади URI отображается зелёная шкала, отображающая ход загрузки страницы. Как же реализовать такое поведение?
Под катом — один из вариантов решения этой задачи.
Итак, постановка задачи: реализовать элемент, который бы выполнял функции ProgressBar с горизонтальным стилем (style="@android:style/Widget.ProgressBar.Horizontal"), а также имел бы возможность размещать поверх себя текст.
Краткий план действий:
Нам потребуется двухслойный Drawable, который будет отвечать за графическое наполнение элемента.
В папке res/drawable создаём файл background.xml со следующим содержанием:
Как видно из кода, фон состоит из двух слоёв: первый отвечает за контейнер создаваемой шкалы, а второй — собственно за шкалу, которая будет характеризовать отображаемую величину. Оба представляют из себя скруглённые прямоугольники, залитые серым и жёлтым градиентом. Однако второй shape заключён в тег clip, а это значит, что показываться будет лишь его часть.
Хитрость описываемого метода состоит в том, что расширять мы будем не класс ProgressBar, а класс TextView. Ключевым моментом является создание метода, который бы прорисовывал новый фон и устанавливал новую надпись.
Метод setValue() устанавливает текст надписи элемента с помощью стандартной функции setText(), а затем изменяет фоновый рисунок в соответствии с новым значением. Для этого используется метод setLevel(), которому в качестве параметра передаётся величина от 0 (шкала не видна) до 10000 (шкала закрывает 100% контейнера).
Для удобства в классе также создан метод setMaxValue(), позволяющий задать значение, соответствующее полной шкале. Таким образом, можно определить диапазон возможных значений, отображаемых элементом.
В папке res/layout модифицируем содержание файла main.xml, отвечающего за внешний вид основного Activity.
Здесь мы указываем, что фоном элемента будет созданный нами Drawable с именем background (описан в background.xml), ну и не забываем прописать id, по которому будет осуществляться его поиск внутри Activity.
Не забываем правильно указать пространство имён, в котором был создан класс AdvancedTextView. В данном примере это ru.habrahabr.widgets.
Внедрение и использование элемента в программе является простым и понятным:
Результатом будет вот такой элемент:

Итак, используя собственный Drawable для прорисовки фона и класс, расширяющий TextView, мы получили возможность использовать шкалу выполнения процесса с поясняющей надписью на переднем плане.
Под катом — один из вариантов решения этой задачи.
Итак, постановка задачи: реализовать элемент, который бы выполнял функции ProgressBar с горизонтальным стилем (style="@android:style/Widget.ProgressBar.Horizontal"), а также имел бы возможность размещать поверх себя текст.
Краткий план действий:
- Создать Drawable, отвечающий за прорисовку фона.
- Создать класс, расширяющий TextView, и прописать необходимые методы.
- Внедрить созданный элемент в Layout.
- Использовать элемент в Activity.
Создание Drawable
Нам потребуется двухслойный Drawable, который будет отвечать за графическое наполнение элемента.
В папке res/drawable создаём файл background.xml со следующим содержанием:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/bar_background"> <shape> <corners android:radius="2dp" /> <gradient android:startColor="#666666" android:centerColor="#111111" android:endColor="#555555" android:centerY="0.75" android:angle="270" /> </shape> </item> <item android:id="@+id/bar_value"> <clip> <shape> <corners android:radius="2dp" /> <gradient android:startColor="#cfb73f" android:centerColor="#635a30" android:endColor="#918030" android:centerY="0.75" android:angle="270" /> </shape> </clip> </item> </layer-list>
Как видно из кода, фон состоит из двух слоёв: первый отвечает за контейнер создаваемой шкалы, а второй — собственно за шкалу, которая будет характеризовать отображаемую величину. Оба представляют из себя скруглённые прямоугольники, залитые серым и жёлтым градиентом. Однако второй shape заключён в тег clip, а это значит, что показываться будет лишь его часть.
Создание класса
Хитрость описываемого метода состоит в том, что расширять мы будем не класс ProgressBar, а класс TextView. Ключевым моментом является создание метода, который бы прорисовывал новый фон и устанавливал новую надпись.
public class AdvancedTextView extends TextView { // Максимальное значение шкалы private int mMaxValue = 100; // Конструкторы public AdvancedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public AdvancedTextView(Context context, AttributeSet attrs) { super(context, attrs); } public AdvancedTextView(Context context) { super(context); } // Установка максимального значения public void setMaxValue(int maxValue){ mMaxValue = maxValue; } // Установка значения public synchronized void setValue(int value) { // Установка новой надписи this.setText(String.valueOf(value)); // Drawable, отвечающий за фон LayerDrawable background = (LayerDrawable) this.getBackground(); // Достаём Clip, отвечающий за шкалу, по индексу 1 ClipDrawable barValue = (ClipDrawable) background.getDrawable(1); // Устанавливаем уровень шкалы int newClipLevel = (int) (value * 10000 / mMaxValue); barValue.setLevel(newClipLevel); // Уведомляем об изменении Drawable drawableStateChanged(); } }
Метод setValue() устанавливает текст надписи элемента с помощью стандартной функции setText(), а затем изменяет фоновый рисунок в соответствии с новым значением. Для этого используется метод setLevel(), которому в качестве параметра передаётся величина от 0 (шкала не видна) до 10000 (шкала закрывает 100% контейнера).
Для удобства в классе также создан метод setMaxValue(), позволяющий задать значение, соответствующее полной шкале. Таким образом, можно определить диапазон возможных значений, отображаемых элементом.
Редактирование Layout
В папке res/layout модифицируем содержание файла main.xml, отвечающего за внешний вид основного Activity.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="wrap_content"> <ru.habrahabr.widgets.AdvancedTextView android:id="@+id/advanced_text_view" android:layout_width="160dp" android:layout_height="wrap_content" android:gravity="center" android:background="@drawable/background" android:layout_margin="4dp" /> </LinearLayout>
Здесь мы указываем, что фоном элемента будет созданный нами Drawable с именем background (описан в background.xml), ну и не забываем прописать id, по которому будет осуществляться его поиск внутри Activity.
Не забываем правильно указать пространство имён, в котором был создан класс AdvancedTextView. В данном примере это ru.habrahabr.widgets.
Использование элемента в Activity
Внедрение и использование элемента в программе является простым и понятным:
AdvancedTextView advancedTextView = (AdvancedTextView) findViewById(R.id.advanced_text_view); advancedTextView.setValue(42);
Результатом будет вот такой элемент:

Заключение
Итак, используя собственный Drawable для прорисовки фона и класс, расширяющий TextView, мы получили возможность использовать шкалу выполнения процесса с поясняющей надписью на переднем плане.