Do I need all three constructors for an Android custom view?
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.
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.
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.