Мне нужны все три конструктора для пользовательского представления 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)? Я не уверен, где он используется, но я вижу это в суперклассе. Нужен ли он мне и где он используется?

Там другая часть этого вопроса.

Ответ 1

Если вы добавите свой пользовательский View из xml также как:

 <com.mypack.MyView
      ...
      />

вам понадобится конструктор public MyView(Context context, AttributeSet attrs), иначе вы получите Exception, когда Android попытается раздуть ваш View.

Если вы добавите View из xml, а также укажите атрибут android:style, например:

 <com.mypack.MyView
      style="@styles/MyCustomStyle"
      ...
      />

также будет вызываться второй конструктор и по умолчанию стиль MyCustomStyle перед применением явных атрибутов XML.

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

Ответ 2

Если вы переопределите все три конструктора, не используйте CASCADE this(...) CALLS. Вы должны сделать это:

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 в качестве стиля attr.

Ответ 3

MyView (контекст контекста)

Используется при программном преобразовании Views.

MyView (контекст контекста, AttributeSet attrs)

Используется LayoutInflater для применения атрибутов xml. Если один из этих атрибутов имеет имя style, атрибуты будут искать стиль перед поиском явных значений в XML файле макета.

MyView (контекст контекста, AttributeSet attrs, int defStyleAttr)

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

Обратите внимание, что defStyleAttr было некорректно названо defStyle некоторое время назад, и есть некоторое обсуждение о том, действительно ли этот конструктор нужен или нет. См. https://code.google.com/p/android/issues/detail?id=12683

MyView (контекст контекста, AttributeSet attrs, int defStyleAttr, int defStyleRes)

3-й конструктор работает хорошо, если у вас есть контроль над базовой темой приложений. Это работает для 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();
}

Всего

Если вы реализуете свои собственные представления, нужны только два первых конструктора и могут быть вызваны каркасом.

Если вы хотите, чтобы ваши представления были расширяемыми, вы можете реализовать 4-й конструктор для детей вашего класса, чтобы иметь возможность использовать глобальный стиль.

Я не вижу реального варианта использования для третьего конструктора. Может быть, ярлык, если вы не предоставляете стиль по умолчанию для своего виджета, но по-прежнему хотите, чтобы ваши пользователи могли это сделать. Не должно быть так много.

Ответ 4

Третий конструктор намного сложнее. Позвольте мне привести пример.

Support-v7 SwitchCompact пакет поддерживает атрибут thumbTint и trackTint с 24 версии, а 23 версия не поддерживает их. Теперь вы хотите поддержать их в 23 версии и как вы это сделаете?

Мы предполагаем использовать пользовательский вид SupportedSwitchCompact extends 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() всегда возвращает null, как это странно, потому что метод 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>

Ответ 5

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

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();

}