155

When creating a custom view, I have noticed that many people seem to do it like this:

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.
}

My first question is, what about the constructor MyView(Context context, AttributeSet attrs, int defStyle)? I'm not sure where it is used, but I see it in the super class. Do I need it, and where is it used?

There's another part to this question.

Community
  • 1
  • 1
Micah Hainline
  • 14,367
  • 9
  • 52
  • 85

6 Answers6

154

Long story short, No, but if you do override any constructor, then ensure to call super(...) with the exact same number of arguments (like, see Jin's answer for example why).


If you will add your custom View from xml also like :

 <com.mypack.MyView
      ...
      />

you will need the constructor public MyView(Context context, AttributeSet attrs), otherwise you will get an Exception when Android tries to inflate your View.

If you add your View from xml and also specify the android:style attribute like :

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

the 2nd constructor will also be called and default the style to MyCustomStyle before applying explicit XML attributes.

The third constructor is usually used when you want all of the Views in your application to have the same style.

Top-Master
  • 7,611
  • 5
  • 39
  • 71
Ovidiu Latcu
  • 71,607
  • 15
  • 76
  • 84
  • 3
    when to use first constructor then ? – Android Killer Jul 15 '13 at 04:45
  • @OvidiuLatcu can you please show an example of the third CTOR (with the 3 parameters) ? – android developer Sep 14 '13 at 20:10
  • can I add extra parameters to constructor and How can I use them ? – Mohammed Subhi Sheikh Quroush Dec 18 '13 at 13:09
  • 30
    Regarding the third constructor, this is actually **completely wrong**. XML **always** calls the two-argument constructor. The three-argument (and [four-argument](https://developer.android.com/reference/android/view/View.html#View(android.content.Context,%20android.util.AttributeSet,%20int,%20int))) constructors are called by **subclasses** if they want to specify an attribute containing a default style, or a default style directly (in the case of the four-argument constructor) – imgx64 Jul 15 '15 at 08:55
  • 1
    I just submitted an edit to make the answer correct. I have also proposed an alternate answer below. – mbonnin Sep 01 '16 at 20:41
127

If you override all three constructors, please DO NOT CASCADE this(...) CALLS. You should instead be doing 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
}

The reason is that the parent class might include default attributes in its own constructors that you might be accidentally overriding. For example, this is the constructor for 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);
}

If you did not call super(context), you would not have properly set R.attr.textViewStyle as the style attr.

Jin
  • 6,055
  • 2
  • 39
  • 72
  • 12
    This is essential advice when extending ListView. As a (previous) fan of the above this-cascading, I recall spending hours tracking down a subtle bug that went away when I called the correct super method for each constructor. – Groovee60 Dec 27 '15 at 06:57
  • BTW @Jin I used the code in this answer: http://stackoverflow.com/a/22780035/294884 which seems to be based on your answer - but note that the writer includes use of the Inflator ? – Fattie Nov 27 '16 at 01:44
  • 1
    I think it´s not neccessary to call init in all constructors, because when you follow the call hierarchy, you will end up in the default constructor for programmatic view creation anyway View(Context context){} – Marian Klühspies Dec 20 '16 at 20:33
  • i m doing same but failed to set values in textview which is available in my custom view i want to set value from activity – Erum Oct 19 '17 at 07:35
  • 1
    How did I never know this? – Suragch Nov 04 '17 at 09:17
  • @Suragch Exactly, me too! This answer is clear and to-the-point. Should be marked as the accepted answer. – varun May 15 '19 at 17:05
57

MyView(Context context)

Used when instanciating Views programmatically.

MyView(Context context, AttributeSet attrs)

Used by the LayoutInflater to apply xml attributes. If one of this attribute is named style, attributes will be looked up the the style before looking for explicit values in the layout xml file.

MyView(Context context, AttributeSet attrs, int defStyleAttr)

Suppose you want to apply a default style to all widgets without having to specify style in each layout file. For an example make all checkboxes pink by default. You can do this with defStyleAttr and the framework will lookup the default style in your theme.

Note that defStyleAttr was incorrectly named defStyle some time ago and there is some discussion about whether this constructor is really needed or not. See https://code.google.com/p/android/issues/detail?id=12683

MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

The 3rd constructor works well if you have control over the base theme of the applications. That is working for google because they ship their widgets along side the default Themes. But suppose you're writing a widget library and you want a default style to be set without your users needing to tweak their theme. You can now do this using defStyleRes by setting it to the default value in the 2 first constructors:

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

All in all

If you're implementing your own views, only the 2 first constructors should be needed and can be called by the framework.

If you want your Views to be extensible, you might implement the 4th constructor for children of your class to be able to use global styling.

I don't see a real use case for the 3rd constructor. Maybe a shortcut if you don't provide a default style for your widget but still want your users to be able to do so. Shouldn't happen that much.

hata
  • 11,633
  • 6
  • 46
  • 69
mbonnin
  • 6,893
  • 3
  • 39
  • 55
10

Kotlin seems to take away a lot of this pain:

class MyView
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
    : View(context, attrs, defStyle)

@JvmOverloads will generate all required constructors (see that annotation's documentation), each of which presumably calls super(). Then, simply replace your initialization method with a Kotlin init {} block. Boilerplate code gone!

jules
  • 514
  • 5
  • 8
  • With this approach your class defines the default parameter values in case the constructor is called with 1 or 2 arguments, whereas https://stackoverflow.com/a/39280344/2011622 argues that it's safer to call the super constructors with the same number of parameters directly so the super class has the responsibility to provide default values. The concise syntax is tempting but it's not the recommended approach. – Peter F Feb 19 '21 at 17:02
1

The third constructor is much more complicated.Let me hold an example.

Support-v7 SwitchCompact package supports thumbTint and trackTint attribute since 24 version while 23 version does not support them.Now you want to support them in 23 version and how will you do to achieve this?

We assume to use custom View 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();
}

It's a traditional code style.Note we pass 0 to the third param here. When you run the code, you will find getThumbDrawable() always return null how strange it is because the method getThumbDrawable() is its super class SwitchCompact's method.

If you pass R.attr.switchStyle to the third param, everything goes well.So why?

The third param is a simple attribute. The attribute points to a style resource.In above case, the system will find switchStyle attribute in current theme fortunately system finds it.

In frameworks/base/core/res/res/values/themes.xml, you will see:

<style name="Theme">
    <item name="switchStyle">@style/Widget.CompoundButton.Switch</item>
</style>
CoXier
  • 2,523
  • 8
  • 33
  • 60
-2

If you have to include three constructors like the one under discussion now, you could do this too.

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

}
arTsmarT
  • 456
  • 3
  • 11
  • 2
    @Jin That's a good idea in many cases, but this is also safe in many cases (eg: RelativeLayout, FrameLayout, RecyclerView, etc.). So, I would say this is probably a case-by-case requirement and the base class should be checked out before making the decision to cascade or not. Essentially, if the 2-param constructor in the base class is just calling this(context, attrs, 0), then it's safe to do so in the custom view class as well. – ejw Aug 16 '16 at 16:18
  • @IanWong, of course, it will be called, because first and second methods are calling third. – CoolMind Aug 04 '17 at 09:51