Статьи

Android SDK: создание пользовательских видов

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

Чтобы создать и использовать наш пользовательский вид, мы расширим класс View, определим и укажем некоторые пользовательские атрибуты, добавим View в наш XML-макет, переопределим метод onDraw, чтобы адаптировать внешний вид View и манипулировать им из основного Activity нашего приложения.


Создайте новый проект Android в Eclipse. Вы можете выбрать любые настройки, которые вам нравятся, если ваше приложение имеет основной класс Activity и файл макета для него. Нам не нужны какие-либо изменения в файле манифеста. В файле загрузки исходного кода основной вид деятельности называется «LovelyActivity», а файл макета — «activity_lovely.xml» — при необходимости измените код под свои собственные имена. Мы будем создавать и добавлять несколько дополнительных файлов по мере продвижения.


Наш пользовательский вид может расширять любой из существующих классов Android View, например Button или TextView. Однако мы создадим прямой подкласс View. Расширение существующего класса позволяет использовать существующие функциональные возможности и стили, связанные с этим классом, обеспечивая при этом обработку в соответствии с вашими дополнительными потребностями.

Создайте новый класс в своем приложении, выбрав основной пакет приложения в Eclipse и выбрав «Файл», «Новый», «Класс». Введите имя по вашему выбору и нажмите «Готово». В учебном коде используется имя класса «LovelyView» — вам нужно будет изменить его во всем приведенном ниже коде, если вы выберете другое имя. Сделайте ваш новый класс расширенным View, добавив к его открывающей строке объявления:

1
public class LovelyView extends View {

Добавьте следующие операторы импорта над этим:

1
2
3
4
5
6
7
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.View;

Чтобы использовать наш пользовательский вид, как мы бы использовали стандартный вид (то есть установить его атрибуты в XML макета и ссылаться на них в нашем коде Java), мы объявим ресурсы атрибутов. В Eclipse создайте новый файл в папке проекта «res / values», выбрав его и выбрав «Файл», «Новый», «Файл». Введите «attrs.xml» в качестве имени файла и нажмите «Готово».

В файле атрибутов нам сначала нужно указать, что мы перечисляем ресурсы, поэтому добавьте следующий родительский элемент:

1
2
3
<resources>
 
</resources>

Внутри этого элемента мы собираемся объявить три атрибута для представления, которые позволят нам стилизовать его. Давайте сделаем все относительно просто — в представлении будет отображаться круг с текстом в середине. Этими тремя атрибутами будут цвет круга, текстовая строка и цвет текста. Добавьте следующее внутри вашего элемента ресурсов :

1
2
3
4
5
<declare-styleable name=»LovelyView»>
    <attr name=»circleColor» format=»color» />
    <attr name=»circleLabel» format=»string»></attr>
    <attr name=»labelColor» format=»color»></attr>
</declare-styleable>

Элемент объявляемого стиля определяет имя представления. Каждый атрибут имеет имя и формат. Мы сможем указать эти атрибуты в XML-макете, когда добавим пользовательский вид, а также получим их в классе вида. Мы также сможем получить и установить атрибуты из нашего класса Java Activity. Значения, предоставленные для каждого атрибута, должны иметь тип, указанный здесь как формат.


Давайте добавим экземпляр пользовательского представления в основной файл макета нашего приложения. Чтобы указать пользовательский вид и его атрибуты, нам нужно добавить атрибут в родительский элемент макета. В исходной загрузке это RelativeLayout, но вы можете использовать любой тип, который вы предпочитаете. Добавьте следующий атрибут в родительский элемент вашего макета:

1
xmlns:custom=»http://schemas.android.com/apk/res/your.package.name»

Измените «your.package.name», чтобы отразить пакет, в котором находится ваше приложение. Это определяет пространство имен для нашего приложения, что позволяет нам использовать атрибуты, которые мы определили в нем. Теперь мы можем добавить экземпляр нового View. Внутри макета добавьте его следующим образом:

1
2
3
4
5
6
7
8
<your.package.name.LovelyView
    android:id=»@+id/custView»
    android:layout_width=»fill_parent»
    android:layout_height=»fill_parent»
    android:layout_margin=»5dp»
    custom:circleColor=»#ff0099″
    custom:circleLabel=»Hello»
    custom:labelColor=»#ffff66″ />

Опять же, измените имя пакета в соответствии со своим собственным и имя класса, если это необходимо. Мы будем использовать идентификатор для ссылки на представление в нашем коде активности. Обратите внимание, что элемент перечисляет стандартные атрибуты View вместе с пользовательскими атрибутами. Пользовательским атрибутам предшествует «custom:» и используются имена, которые мы указали в нашем XML-файле атрибутов. Также обратите внимание, что мы указали значения типов, которые мы указали, используя атрибуты формата в файле attrs.xml. Мы будем извлекать и интерпретировать эти значения в нашем классе View.


Теперь вернемся к классу View, который мы создали. Внутри объявления класса добавьте несколько переменных экземпляра следующим образом:

1
2
3
4
5
6
//circle and text colors
private int circleCol, labelCol;
//label text
private String circleText;
//paint for drawing custom view
private Paint circlePaint;

Мы будем использовать первые три из них, чтобы отслеживать текущие настройки цвета и текста. Объект Paint предназначен для рисования вида. После этих переменных добавьте метод конструктора для вашего класса:

1
2
3
4
public LovelyView(Context context, AttributeSet attrs){
    super(context, attrs);
 
}

Поскольку мы расширяем класс View, в первую очередь мы вызываем метод суперкласса. После супер звонка давайте расширим метод для настройки View. Сначала создайте экземпляр объекта Paint:

1
2
//paint object for drawing in onDraw
circlePaint = new Paint();

Теперь давайте восстановим значения атрибутов, которые мы установили в XML:

1
2
3
//get the attributes specified in attrs.xml using the name we included
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
    R.styleable.LovelyView, 0, 0);

Этот типизированный массив обеспечит доступ к значениям атрибута. Обратите внимание, что мы используем имя ресурса, которое мы указали в файле attrs.xml. Давайте теперь попробуем извлечь значения атрибута, используя блок try на случай, если что-то пойдет не так:

1
2
3
4
5
6
7
8
try {
    //get the text and colors specified using the names in attrs.xml
    circleText = a.getString(R.styleable.LovelyView_circleLabel);
    circleCol = a.getInteger(R.styleable.LovelyView_circleColor, 0);//0 is default
    labelCol = a.getInteger(R.styleable.LovelyView_labelColor, 0);
} finally {
    a.recycle();
}

Мы читаем атрибуты в наших переменных экземпляра. Обратите внимание, что мы снова используем имена, которые мы перечислили для каждого в «attrs.xml». Цвета извлекаются как целочисленные значения, а текстовая метка — как строка.

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


Теперь у нас есть атрибуты View в классе, так что мы можем пойти дальше и нарисовать его. Для этого нам нужно переопределить метод onDraw . Добавьте его схему после вашего метода конструктора следующим образом:

1
2
3
4
@Override
protected void onDraw(Canvas canvas) {
    //draw the View
}

Поскольку мы собираемся нарисовать круг, давайте получим некоторую информацию о доступном пространстве внутри метода onDraw :

1
2
3
//get half of the width and height as we are working with a circle
int viewWidthHalf = this.getMeasuredWidth()/2;
int viewHeightHalf = this.getMeasuredHeight()/2;

Теперь мы можем рассчитать радиус круга:

1
2
3
4
5
6
7
//get the radius as half of the width or height, whichever is smaller
//subtract ten so that it has some space around it
int radius = 0;
if(viewWidthHalf>viewHeightHalf)
    radius=viewHeightHalf-10;
else
    radius=viewWidthHalf-10;

Теперь давайте установим некоторые свойства для рисования с помощью:

1
2
circlePaint.setStyle(Style.FILL);
circlePaint.setAntiAlias(true);

Теперь мы будем использовать выбранный цвет круга, который хранится в нашей переменной экземпляра:

1
2
//set the paint color using the circle color specified
circlePaint.setColor(circleCol);

Это означает, что круг будет нарисован любым цветом, который мы перечислили в макете XML. Давайте нарисуем это сейчас, используя эти детали:

1
canvas.drawCircle(viewWidthHalf, viewHeightHalf, radius, circlePaint);

Теперь давайте добавим текст. Сначала установите цвет, используя значение, полученное из XML макета:

1
2
//set the text color using the color specified
circlePaint.setColor(labelCol);

Теперь установите еще несколько свойств:

1
2
3
//set text properties
circlePaint.setTextAlign(Paint.Align.CENTER);
circlePaint.setTextSize(50);

Наконец, мы можем нарисовать текст, используя полученную текстовую строку:

1
2
//draw the text using the string attribute and chosen properties
canvas.drawText(circleText, viewWidthHalf, viewHeightHalf, circlePaint);

На этом завершено.

Первоначальный внешний вид приложения

Когда вы создаете пользовательское представление с вашими собственными атрибутами, рекомендуется также предоставить методы get и set для них в вашем классе View. После метода onDraw сначала добавьте методы get для трех настраиваемых атрибутов:

01
02
03
04
05
06
07
08
09
10
11
public int getCircleColor(){
    return circleCol;
}
 
public int getLabelColor(){
    return labelCol;
}
 
public String getLabelText(){
    return circleText;
}

Каждый метод просто возвращает запрошенное значение. Теперь добавьте набор методов для атрибутов цвета:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public void setCircleColor(int newColor){
    //update the instance variable
    circleCol=newColor;
    //redraw the view
    invalidate();
    requestLayout();
}
public void setLabelColor(int newColor){
    //update the instance variable
    labelCol=newColor;
    //redraw the view
    invalidate();
    requestLayout();
}

Эти методы принимают параметры типа int, представляющие цвет для установки. В обоих случаях мы обновляем соответствующую переменную экземпляра, а затем предлагаем перерисовать представление. Это заставит метод onDraw выполняться снова, чтобы новые значения влияли на представление, отображаемое для пользователя. Теперь добавьте метод set для текста:

1
2
3
4
5
6
7
public void setLabelText(String newLabel){
    //update the instance variable
    circleText=newLabel;
    //redraw the view
    invalidate();
    requestLayout();
}

Это то же самое, что и два других метода set, за исключением параметра String. Мы будем вызывать эти методы в нашем классе Activity далее.


Теперь у нас есть основы нашего пользовательского представления, давайте продемонстрируем использование методов в нашем классе Activity. В основной класс Activity приложения добавьте следующие операторы импорта:

1
2
import android.graphics.Color;
import android.view.View;

Перед методом onCreate в объявлении класса добавьте переменную экземпляра, представляющую экземпляр отображаемого настраиваемого представления:

1
private LovelyView myView;

Внутри метода onCreate , после существующего кода, получите его, используя его идентификатор, включенный в файл макета XML:

1
myView = (LovelyView)findViewById(R.id.custView);

Чтобы продемонстрировать установку значений атрибута View из Activity, мы добавим простую кнопку. Откройте файл макета и добавьте его после пользовательского элемента View:

1
2
3
4
5
6
<Button
    android:layout_width=»wrap_content»
    android:layout_height=»wrap_content»
    android:layout_centerHorizontal=»true»
    android:onClick=»btnPressed»
    android:text=»@string/btn_label» />

Мы указываем метод, который будет выполняться по кликам пользователя — мы добавим его в класс Activity. Сначала добавьте строку в ваш XML-файл «res / values ​​/ strings»:

1
<string name=»btn_label»>Press Me</string>
Приложение с кнопкой

Теперь вернитесь в класс Activity и добавьте метод, указанный для кликов по кнопке:

1
2
3
public void btnPressed(View view){
//update the View
}

Давайте использовать методы set, которые мы определили для обновления внешнего вида View:

1
2
3
myView.setCircleColor(Color.GREEN);
myView.setLabelColor(Color.MAGENTA);
myView.setLabelText(«Help»);

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

Приложение после нажатия кнопки

В общем, желательно использовать существующие классы Android View, где это возможно. Однако, если вы чувствуете, что вам нужен уровень настройки, выходящий за пределы настроек по умолчанию, создание собственных пользовательских представлений обычно не вызывает затруднений. То, что мы рассмотрели в этом руководстве, на самом деле является только началом, когда речь идет о создании специализированных пользовательских интерфейсов Android. См. Официальное руководство для получения информации о добавлении интерактивности и оптимизации в ваши настройки.