Как я могу сделать цвет EditText стилизованным с помощью пользовательского атрибута (например, ошибка)?
Я хотел бы сделать EditText, текст которого становится красным, когда выполняется условие "ошибка". Я знаю, что TextInputLayout имеет встроенное состояние ошибки, но я не хочу, чтобы текст находился ниже области ввода, и я не хочу включать библиотеки поддержки дизайна в мой проект (который является SDK - так другие будут включать это). Тем не менее, мой EditText всегда показывает состояние ошибки, и я не знаю, почему.
Мои усилия на данный момент:
Для начала я объявляю стильную вещь. Кажется нормально.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MySpecialEditText">
<attr name="state_error" format="boolean"/>
</declare-styleable>
</resources>
Затем я создаю селектор в моем res/colors
каталог. Теперь я бы предпочел переопределить цвет, только если мой специальный атрибут установлен. Но если я должен скопировать android:state-pressed
и т. д., состояния и все их комбинации, я могу сделать это (и с теми же результатами, см. ниже). Этот файл res/color/red_on_error_dark.xml
,
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/lib/com.mypackage">
<item
custom:state_error="true"
android:color="#f00" />
<item
custom:state_error="false"
android:color="@android:color/primary_text_dark"/>
<item android:color="@android:color/primary_text_dark"/>
</selector>
И я добавляю свою новую стильную вещь в XML-включение моего пользовательского класса:
<com.mypackage.MySpecialEditText
android:id="@+id/some_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:hint="@string/a_nice_hint"
android:inputType="number"
android:textColor="@color/red_on_error_dark"
/>
Все идет нормально. Теперь я создаю свой класс, который расширяет EditText:
public class MySpecialEditText extends EditText {
private static final int[] STATE_ERROR = {R.attr.state_error};
private boolean mStateError;
//... lots of other fun functionality
// Time to set my custom status
public void setStateError(boolean stateError) {
mStateError = stateError;
invalidate();
refreshDrawableState();
}
// And now, I need to add the custom state drawable.
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (mStateError) {
mergeDrawableStates(drawableState, STATE_ERROR);
}
return drawableState;
}
}
Все это достаточно легко собрать. Однако когда я запускаю код, он всегда рисует текст красным.
Вещи, которые я проверил:
- Что я правильно устанавливаю состояния ошибки / отсутствия ошибок.
- Что код проходит через
onCreateDrawableState
метод во время без ошибок! Это пропускаетmergeDrawableStates
метод с дополнительным атрибутом. (Я также добавилelse
предложение и возвращает drawableState без добавления лишних пробелов). Несмотря на то, что атрибут является логическим, и теоретически должен иметь возможность принимать значение true или false, я добавил отдельный атрибут
STATE_VALID
и изменил мой селектор следующим образом.
Я даже зашел так далеко, что сменил селектор на исчерпывающий:
<item custom:state_error="true" android:state_selected="true" android:color="#f00" />
<item custom:state_error="true" android:state_focused="true" android:color="#f00" />
<item custom:state_error="true" android:state_pressed="true" android:color="#f00" />
<item custom:state_error="false" android:state_selected="true" android:color="@android:color/primary_text_dark" />
<item custom:state_error="false" android:state_focused="true" android:color="@android:color/primary_text_dark" />
<item custom:state_error="false" android:state_pressed="true" android:color="@android:color/primary_text_dark" />
И я пошел еще дальше и объединил ненужные дополнительные состояния с кучей android:state_blah
Таким образом, у меня есть что-то вроде 20 различных случаев в моем селекторе. (На самом деле, в этом случае он всегда остается белым, что не намного лучше).
Теперь, что я действительно, действительно хотел бы, это объявить настраиваемое значение, которое другие люди (которые используют мой sdk) могут установить значение, если они того пожелают. Но сейчас я буду рад узнать, почему текст всегда красный.
Редактировать: один из вполне законных обходных путей - просто вызвать setTextColor, когда я достигну состояния ошибки в MySpecialEditText
, Это мое временное решение, но оно не позволяет кому-либо использовать мой контроль и добавлять custom:state_error=@color/some_custom_color
или аналогичное значение в теме их выбора. Пока я вызываю setTextColor вручную, цвет ошибки всегда является моим конкретным оттенком красного.
Я мог бы пойти дальше и добавить новый конструктор или установщик для цвета ошибки, но это не очень хорошее решение для библиотеки. Если вы используете библиотеку и создаете экземпляр моего контроля, она поднимет все EditText
стили у вас уже есть, так что выглядит нативно. И если вы хотите настроить цвет ошибки, я бы хотел, чтобы вы могли сделать это в одном месте для всего вашего приложения с хорошим фрагментом XML, который вы можете запустить дизайнером.
Есть идеи? Упреждающее спасибо всем ответчикам и покушающимся ответчикам.