Сегодня мы посмотрим как можно добавлять к элементам новые
свойства на примере TextView,
покрасив его в градиент.
Практика
Чтобы добавить новые свойства к элементу, необходимо их описать как атрибуты в xml-файле values/attrs.xml.
<?xml version="1.0"
encoding="utf-8"?>
<resources>
<declare-styleable name="GradientTextView">
<attr name="colorStartGradient"
format="integer" />
<attr name="colorEndGradient"
format="integer" />
</declare-styleable>
</resources>
Опишем цвета для нашего градиента (colors.xml):
<?xml version="1.0"
encoding="utf-8"?>
<resources>
<color name="textview_start_gradient">#ffcccccc</color>
<color name="textview_end_gradient">#ff56a9c7</color>
</resources>
Теперь разработаем новый класс для нашего элемента. В качестве атрибутов этого класса выступают начальный и конечный цвет градиента. В конструкторе класса получим атрибуты из xml-файла. Градиент устанавливается с помощью метода setShader(). В качестве градиента добавим линейный градиент.
Source code (GradientTextView.java):
public class GradientTextView extends TextView { private int colorStartGradient, colorEndGradient; public GradientTextView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.GradientTextView); colorStartGradient = a.getColor(R.styleable.GradientTextView_colorStartGradient, -2); colorEndGradient = a.getColor(R.styleable.GradientTextView_colorEndGradient, -2); } @Override protected void onDraw(Canvas canvas) { if (colorStartGradient != -2 && colorEndGradient != -2) { getPaint().setShader( new LinearGradient(0, 0, 0, getHeight(), colorStartGradient, colorEndGradient, TileMode.MIRROR)); } super.onDraw(canvas); } }
Теперь добавим наш элемент в main.xml. Чтобы добавить новые свойства, необходимо прописать следующую строку: xmlns:app="http://schemas.android.com/apk/res/org.snowpard.proects.sixteen".
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/org.snowpard.proects.sixteen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
<org.snowpard.proects.sixteen.GradientTextView
android:id="@+id/gradient_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_name"
android:textSize="35sp"
app:colorEndGradient="@color/textview_end_gradient"
app:colorStartGradient="@color/textview_start_gradient"
/>
</RelativeLayout>
Если для TextView необходимо добавить тень, то обычный способ (свойства в xml) даст неправильный результат, так как setShader изменит цвет всего элемента TextView. Таким образом, установленный цвет тени в xml не отобразится. Чтобы добавить тень к нашему элементу, необходимо выполнить следующие действия:
Тень
Если для TextView необходимо добавить тень, то обычный способ (свойства в xml) даст неправильный результат, так как setShader изменит цвет всего элемента TextView. Таким образом, установленный цвет тени в xml не отобразится. Чтобы добавить тень к нашему элементу, необходимо выполнить следующие действия:
- Добавить новый цвет в color.xml:
<color name="textview_shadow">#ff674125</color>
- Описать новое свойство для GradientTextView в attrs.xml:
<attr name="colorShadowGradient" format="integer" />
- Добавить свойство тени в main.xml
app:colorShadowGradient="@color/textview_shadow"
- Изменить класс GradientTextView для работы с тенью. Идея заключается в том, чтобы отрисовать элемент частями: сначала тень, затем сам элемент.
public class GradientTextView extends TextView { private int colorStartGradient, colorEndGradient, colorShadow; public GradientTextView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.GradientTextView); colorShadow = a.getColor(R.styleable.GradientTextView_colorShadowGradient, -2); colorStartGradient = a.getColor(R.styleable.GradientTextView_colorStartGradient, -2); colorEndGradient = a.getColor(R.styleable.GradientTextView_colorEndGradient, -2); } @Override protected void onDraw(Canvas canvas) { if (colorShadow != -2) { getPaint().setShadowLayer(3, 3, 5, colorShadow); getPaint().setShader(null); super.onDraw(canvas); } if (colorStartGradient != -2 && colorEndGradient != -2) { getPaint().clearShadowLayer(); getPaint().setShader( new LinearGradient(0, 0, 0, getHeight(), colorStartGradient, colorEndGradient, TileMode.MIRROR)); } super.onDraw(canvas); } }
Ссылки
- Исходные коды данного проекта можно скачать отсюда: zip
Как раз искал, как это сделать, спасибо за детальное объяснение!
ОтветитьУдалитьВозможно ли добавить drawableGradient вместо colorStartGradient/colorEndGradient и добавлять сам градиент?
Хороший вопрос. Пытался сделать. К сожалению не получилось. Возник вопрос как из Drawable перейти к Shader. Пока ответ - никак :(
УдалитьДа, тоже над этим задумался.
УдалитьНемного поработал с Вашим классом и заметил такие вещи:
- если один из цветов градиента белый, то логика не срабатывает ( в onDraw такой цвет будет равен -1 - дефолтному значению)
- поддержка теней :) это уже как дополнительное расширение.
В любом случае очень своевременный туториал, спасибо большое!
Спасибо за замечания! Исправил и дополнил статью.
УдалитьС тенью есть косяк один =) Если нужна тень, цвет которой не такой же градиент, то через свойства TextView в xml (android:shadowColor) установить не получится. Поскольку setShader его изменить на градиент. Статью дополнил, как можно сделать тень другого цвета.