Как вы можете создать свой собственный стиль по умолчанию для виджета подкласса, который добавляет/переопределяет стиль по умолчанию базового класса, а не заменяет его?

версия TLDR

Я создаю подклассы встроенных виджетов Android, таких как TextView, CheckBox и RadioButton, и добавляю пару дополнительных свойств/атрибутов. Я также хочу указать стиль по умолчанию для каждого из моих подклассов, чтобы я мог предварительно настроить их при использовании.

Мой вопрос заключается в том, как я могу определить свой собственный стиль по умолчанию, но объединить этот стиль со стилем по умолчанию базового класса, а не полностью заменить его?

Полная версия

Я следовал передовым методам оформления пользовательских виджетов нашего приложения, используя следующую рекомендуемую технику.

Рассмотрим следующее для подкласса View с именем MyWidget:

MyWidget.java

public class MyWidget extends View
{
    public MyWidget(Context context)
    {
        this(context, null);
    }

    public MyWidget(Context context, AttributeSet attrs)
    {
        super(context, attrs, R.attr.myWidgetStyle);

        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MyWidget, R.attr.myWidgetStyle, 0);

        // Only need to read custom attributes here
        // Others are handled in the base class

        styledAttrs.recycle();
    }
}

В attrs.xml

<!-- Attribute to hold the default style -->
<attr name="myWidgetStyle" format="reference" />

<!-- Assigning attributes to controls -->
<declare-styleable name="MyWidget">
    <attr name="isRealTime" format="boolean" />
</declare-styleable>

В стилях.xml

<style name="MyTheme">

    <!-- Store default style in the style-reference attributes -->
    <item name="myWidgetStyle">@style/MyWidget</item>

</style>

<style name="MyWidget">
    <item name="background">@color/someColorResource</item>
    <item name="isRealTime">true</item>
</style>

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

Проблема возникает, когда вместо подкласса View я подклассирую что-то вроде RadioButton. Используя это в качестве примера, поскольку я вручную указываю defStyleAttr в вызове «super» в конструкторе, мой стиль полностью заменяет стиль по умолчанию, и я теряю внешний вид RadioButton по умолчанию, и точка не появляется, просто текст.

Если вместо этого я не передам свой defStyleAttr в «super» и буду использовать его только при вызове getStyledAttributes (для обработки моих пользовательских атрибутов), то любые непользовательские атрибуты, установленные в этом стиле (например, поля, фон, buttonTint и т. ) не применяются, поскольку базовый класс, который обычно их обрабатывает, больше не знает о моем стиле по умолчанию, поэтому он возвращается к внешнему виду базового класса по умолчанию.

Самый простой способ решить эту проблему, конечно же, основывать мой стиль по умолчанию на существующем стиле RadioButton по умолчанию через свойство 'parent' моего стиля, а затем, когда мой стиль становится новым значением по умолчанию, два стиля, смешанные со значениями в моем, имеют приоритет.

Проблема в том, что я не знаю, каков стиль по умолчанию для базового элемента управления (RadioButton в этом примере), потому что я понятия не имею, какой атрибут они используют для его хранения (предполагалось, чтобы быть radioButtonStyle, но это не сработало), и даже если бы я знал атрибут, это не то, что мне нужно. Мне нужен стиль, на который он указывает. (Я также пробовал ?someAttr и ?attr/someAttr', но ни один из них не работал.)

Надеюсь, вы смогли это проследить. А еще лучше, надеюсь, у вас есть ответ для меня!


person Mark A. Donohoe    schedule 04.08.2017    source источник
comment
Стили могут иметь родителей. Пример: ‹style name=Widget.MyTextView parent=@android:style/Theme.Material.TextView› Примечание. Тема Material доступна только на Android 5+. Возможно, вы захотите вместо этого расширить виджеты и стили AppCompat.   -  person Eugen Pechanec    schedule 04.08.2017
comment
Да, я уже расширяю версии AppCompat. Я также знаю (и упоминал в своем вопросе) о том, что стили основываются на других через родительский атрибут стиля. Но чего я не знаю, так это того, как использовать эти базовые стили! Другими словами, что на самом деле поместить в поле «не родитель». Если вы это сделаете и сможете поместить это в ответ, я отмечу его как принятый.   -  person Mark A. Donohoe    schedule 04.08.2017


Ответы (1)


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

Виджеты и их стили по умолчанию, предоставляемые AppCompat, следуют соглашению об именах. Пример: стиль по умолчанию AppCompatEditText управляется атрибутом темы ?editTextStyle, который по умолчанию указывает на @style/Widget.AppCompat.EditText.

В большинстве случаев вы можете угадать правильное имя (и IDE поможет вам), но если вы ищете "правильный" способ определения стиля, выполните следующие действия:

  1. Открытый исходный код любого класса, который вы расширяете, скажем, AppCompatEditText.
  2. Посмотрите на его двухпараметрический конструктор. Он вызывает конструктор с тремя параметрами, причем третий параметр является атрибутом темы стиля по умолчанию. В данном случае R.attr.editTextStyle.
  3. Откройте values.xml из библиотеки appcompat-v7 и найдите определение Theme.AppCompat. Он будет содержать все стили по умолчанию. Найдите, на какой конкретный стиль ссылается атрибут темы.

Это выглядит так:

<style name="Theme.AppCompat" parent="@style/Base.Theme.AppCompat">
    <item name="editTextStyle">@style/Widget.AppCompat.EditText</item>
    <!-- Other styles... -->
</style>
  1. Определите свой стиль...

...как продолжение стиля, который вы нашли.

<style name="Widget.VeryCustomEditText" parent="@style/Widget.AppCompat.EditText">

Если бы у вас были расширенные виджеты платформы, такие как EditText, все было бы немного иначе. Я расширю ответ только в случае необходимости.

person Eugen Pechanec    schedule 04.08.2017
comment
Отличный ответ и именно то, что я хотел. Я бы все же предложил вам расширить его для версий, отличных от AppCompat, для полноты картины. Сам вопрос не диктует так или иначе, поэтому ваш ответ определенно будет полезен при показе всех случаев. Но опять же, это здорово! Спасибо! - person Mark A. Donohoe; 04.08.2017