Нужны ли все три конструктора для пользовательского представления Android?
При создании пользовательского представления я заметил, что многие люди, кажется, делают это так:
public MyView(Context context) {
super(context);
// this constructor used when programmatically creating view
doAdditionalConstructorWork();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// this constructor used when creating view through XML
doAdditionalConstructorWork();
}
private void doAdditionalConstructorWork() {
// init variables etc.
}
Мой первый вопрос: как насчет конструктора MyView(Context context, AttributeSet attrs, int defStyle)
? Я не уверен, где это используется, но я вижу это в суперклассе. Нужно ли это, и где это используется?
Есть еще одна часть этого вопроса.
6 ответов
Если вы добавите свой кастом View
от xml
так же как:
<com.mypack.MyView
...
/>
вам понадобится конструктор public MyView(Context context, AttributeSet attrs)
в противном случае вы получите Exception
когда Android пытается надуть ваш View
,
Если вы добавите свой View
от xml
а также указать android:style
атрибут как:
<com.mypack.MyView
style="@styles/MyCustomStyle"
...
/>
2-й конструктор также будет вызван и по умолчанию стиль MyCustomStyle
перед применением явных атрибутов XML.
Третий конструктор обычно используется, когда вы хотите, чтобы все представления в вашем приложении имели одинаковый стиль.
Если вы переопределяете все три конструктора, пожалуйста, НЕ КАСКАД this(...)
ВЫЗОВ. Вместо этого вы должны делать это:
public MyView(Context context) {
super(context);
init(context, null, 0);
}
public MyView(Context context, AttributeSet attrs) {
super(context,attrs);
init(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
// do additional work
}
Причина в том, что родительский класс может включать атрибуты по умолчанию в свои собственные конструкторы, которые вы можете случайно переопределить. Например, это конструктор для TextView
:
public TextView(Context context) {
this(context, null);
}
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
Если ты не звонил super(context)
, вы бы не правильно установили R.attr.textViewStyle
как стиль
MyView(контекстный контекст)
Используется при создании экземпляров Views программно.
MyView(контекстный контекст, атрибуты AttributeSet)
Используется LayoutInflater
применять атрибуты XML. Если один из этого атрибута назван style
, атрибуты будут искать стиль, прежде чем искать явные значения в XML-файле макета.
MyView(контекстный контекст, атрибуты AttributeSet, int defStyleAttr)
Предположим, вы хотите применить стиль по умолчанию ко всем виджетам, не указывая style
в каждом файле макета. Для примера сделайте все флажки розовыми по умолчанию. Вы можете сделать это с помощью defStyleAttr, и фреймворк будет искать стиль по умолчанию в вашей теме.
Обратите внимание, что defStyleAttr
был неправильно назван defStyle
Некоторое время назад и идет дискуссия о том, действительно ли нужен этот конструктор или нет. См. https://code.google.com/p/android/issues/detail?id=12683
MyView(контекстный контекст, атрибуты AttributeSet, int defStyleAttr, int defStyleRes)
Третий конструктор работает хорошо, если у вас есть контроль над основной темой приложений. Это работает для Google, потому что они отправляют свои виджеты вместе с темами по умолчанию. Но предположим, что вы пишете библиотеку виджетов и хотите, чтобы стиль по умолчанию был установлен без того, чтобы ваши пользователи не настраивали свою тему. Теперь вы можете сделать это с помощью defStyleRes
установив его в значение по умолчанию в 2 первых конструкторах:
public MyView(Context context) {
super(context, null, 0, R.style.MyViewStyle);
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs, 0, R.style.MyViewStyle);
init();
}
В общем
Если вы реализуете свои собственные представления, вам понадобятся только 2 первых конструктора, которые могут быть вызваны средой.
Если вы хотите, чтобы ваши представления расширялись, вы можете реализовать 4-й конструктор, чтобы дети вашего класса могли использовать глобальные стили.
Я не вижу реального варианта использования 3-го конструктора. Может быть, ярлык, если вы не предоставляете стиль по умолчанию для своего виджета, но по-прежнему хотите, чтобы ваши пользователи могли это делать. Так не должно быть.
Котлин, кажется, убрал много этой боли:
class MyView
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: View(context, attrs, defStyle)
@JvmOverloads сгенерирует все необходимые конструкторы (см. Документацию к этой аннотации), каждый из которых предположительно вызывает super(). Затем просто замените ваш метод инициализации на блок Kotlin init {}. Код Boilerplate исчез!
Третий конструктор намного сложнее. Позвольте мне привести пример.
Опорно-v7 SwitchCompact
пакет поддерживает thumbTint
а также trackTint
Атрибут с версии 24, а версия 23 не поддерживает их. Теперь вы хотите поддерживать их в версии 23 и как вы будете добиваться этого?
Мы предполагаем использовать пользовательский вид SupportedSwitchCompact
продолжается SwitchCompact
,
public SupportedSwitchCompat(Context context) {
this(context, null);
}
public SupportedSwitchCompat(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SupportedSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mThumbDrawable = getThumbDrawable();
mTrackDrawable = getTrackDrawable();
applyTint();
}
Это традиционный стиль кода. Обратите внимание, что мы передаем 0 третьему параметру здесь. Когда вы запустите код, вы найдете getThumbDrawable()
всегда возвращать ноль, как это ни странно, потому что метод getThumbDrawable()
это супер класс SwitchCompact
метод.
Если вы пройдете R.attr.switchStyle
к третьему параметру все идет хорошо. Так почему?
Третий параметр - это простой атрибут. Атрибут указывает на ресурс стиля. В приведенном выше случае система найдет switchStyle
Атрибут в текущей теме, к счастью, система находит его.
В frameworks/base/core/res/res/values/themes.xml
, ты увидишь:
<style name="Theme">
<item name="switchStyle">@style/Widget.CompoundButton.Switch</item>
</style>
Если вам нужно включить три конструктора, такие как обсуждаемый сейчас, вы можете сделать это тоже.
public MyView(Context context) {
this(context,null,0);
}
public MyView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
doAdditionalConstructorWork();
}