Должен ли я вызвать super() или вызвать этот() для конструкторов пользовательского вида 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.
}

Моя проблема заключается в том, что это мешает мне сделать мои переменные окончательными. Любая причина не делать следующее?

public MyView(Context context) {
  this(context, null);
  // this constructor used when programmatically creating view
}

public MyView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
  // this constructor used when creating view through XML
}

public MyView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  // this constructor used where?
  // init variables
}

Мне удалось создать представление только через XML и через код, но я не уверен, есть ли какие-то недостатки в этом подходе. Будет ли это работать во всех случаях?

Есть другая часть этого вопроса

Ответ 1

Это нормально.

Когда мы посмотрим на источник TextView.java.

Они использовали одну и ту же иерархию.

Итак, вы с этим согласны.

Ответ 2

Единственный недостаток, который я вижу (что никто, кажется, не упоминал), заключается в том, что ваш второй конструктор теряет defStyle суперкласса, потому что вы устанавливаете его на ноль. Посмотрите исходный код для любого из классов Android View, и вы заметите, что второй конструктор всегда имеет определенный defStyle.

Например, это второй конструктор ListView:

public ListView(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.listViewStyle);
}

Если вы должны расширить ListView, используя второй подход, который вы описываете, com.android.internal.R.attr.listViewStyle больше не будет defStyle, потому что вы обойдете этот второй супер-конструктор и сделаете его нулевым. Я предполагаю, что вы можете решить это, используя тот же defStyle, что и ListView, например:

public MyView(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.listViewStyle);
}

Но это не совсем "пуристский" способ, потому что вы искусственно заставляете его иметь тот же defStyle, что и ListView.

Итак, вопреки тому, что говорили другие, я действительно думаю, что вам лучше использовать первый подход doAdditionalConstructorWork(), описанный в вашем сообщении, потому что это, по крайней мере, гарантирует правильность установки defStyle.

Ответ 3

Скопировал это из моего ответа по аналогичному вопросу.

Если вы переопределите все три конструктора, не используйте 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.

Ответ 4

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

Ответ 5

Это зависит от вашего требования. Скажем, если вы хотите использовать любые методы в родительском классе, не переопределяя их функциональность в своем пользовательском представлении, вам нужно использовать super() и создать экземпляр родительского класса. Если вам не нужно ссылаться на какие-либо методы в родительском классе, все реализации переопределяются в вашем пользовательском представлении, тогда вам не нужно. В разделе прочитайте Пример пользовательского вида.