Do I need all three constructors for an Android custom view?
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.
Android Custom View Constructor
You don't need the first one, as that just won't work.
The third one will mean your custom View
will be usable from XML layout files. If you don't care about that, you don't need it.
The fourth one is just wrong, AFAIK. There is no View
constructor that take a Map
as the third parameter. There is one that takes an int
as the third parameter, used to override the default style for the widget.
I tend to use the this()
syntax to combine these:
public ColorMixer(Context context) {
this(context, null);
}
public ColorMixer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ColorMixer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// real work here
}
You can see the rest of this code in this book example.
Custom view CustomView is not using the 2- or 3-argument View constructors
if you have a custom view and customView has only one type of constructor like default constructor
when you add the custom view in xml than xml wants
CustomView(Context context, AttributeSet attrs)
,CustomView(Context context, AttributeSet attrs, int defStyle)
these type of constructor.
and if your custom view is belongs to 3rd party lib then you can only add by Java code by default constructor like new CustomView();
which constructor to be called for view?
From the Android developer site under documentation for View:
public View (Context context)
Simple constructor to use when creating a view from code.
So this constructor is what you can use to create a View in Java. It will not be called when you inflate from XML.
public View (Context context, AttributeSet attrs)
Constructor that is called when inflating a view from XML. This is called when a view is being constructed from an XML file, supplying attributes that were specified in the XML file. This version uses a default style of 0, so the only attribute values applied are those in the Context's Theme and the given AttributeSet.
The method onFinishInflate() will be called after all children have been added.
So this constructor will be called when you inflate a View from XML when you don't specify a style.
public View (Context context, AttributeSet attrs, int defStyle)
Perform inflation from XML and apply a class-specific base style. This constructor of View allows subclasses to use their own base style when they are inflating. For example, a Button class's constructor would call this version of the super class constructor and supply R.attr.buttonStyle for defStyle; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.
You should implement all of these constructors, but you can put all of the work in the third one by calling this(context, null)
and this(context, attrs, 0)
for the first two, respectively.
When you change attributes in an xml file, which constructor of custom view is called?
As described by @Zain answer the two-argument constructor is called when the layout is inflated from XML.
The
defStyleAttr
is the default style. It doesn’t directly point to a style, but lets you point to one of the attributes defined in your theme.
If you’re subclassing a widget and not specifying your own default style then be sure to use the parent classes default style in your constructors (don’t just pass0
). It can be0
only to not look for defaults.
For example in theMaterialButton
:R.attr.materialButtonStyle
The
defStyleRes
is a resource identifier of a style resource that supplies default values for the view, used only ifdefStyleAttr
is 0 or can not be found in the theme. Can be0
to not look for defaults.
For example in theMaterialButton
:R.style.Widget_MaterialComponents_Button
The AttributeSet
parameter can essentially be thought of as a map of the XML parameters you specify in your layout.
Keep in mind the styling precedence order:
When determining the final value of a particular attribute, there are
four inputs that come into play:
- Any attribute values in the given
AttributeSet
.- The style resource specified in the
AttributeSet
(named"style"
).- The default style specified by
defStyleAttr
anddefStyleRes
- The base values in this theme.
How to create constructor of custom view with Kotlin
Kotlin supports multiple constructors since M11 which was released 19.03.2015. The syntax is as follows:
class MyView : View {
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
// ...
}
constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {}
}
More info here and here.
Edit: you can also use @JvmOverloads annotation so that Kotlin auto-generates the required constructors for you:
class MyView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : View(context, attrs, defStyle)
Beware, though, as this approach may sometimes lead to the unexpected results, depending on how the class you inherit from defines its constructors. Good explanation of what might happen is given in that article.
Style in custom view not affected on super constructor call
The problem here is that you're passing a style in the place of the defStyleAttr
parameter, which expects an attribute, not a style. There's two solutions here:
Use an attribute. In
attrs.xml
declare:<attr name="rangeSelectorButtonStyle" format="reference"/>
and in your app theme:
<item name="rangeSelectorButtonStyle">@style/RangeSelectorButton</style>
and change your constructor to:
public RangeSelectorButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.rangeSelectorButtonStyle);
}If you're making a library for example, this is the best way of doing it since it allows users to customize your widget style.
If you don't want to use an attribute, you can call the fourth View constructor, which has a parameter that takes a default style and not an attribute:
View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
Add this constructor to your view and in the second constructor, call the fourth like this:
public RangeSelectorButton(Context context, AttributeSet attrs) {
this(context, attrs, 0, R.style.RangeSelectorButton);
}Note that the fourth constructor requires API 21.
Related Topics
Do X86 Android Avd's Work on Linux on Amd
Gradle - Library Duplicates in Dependencies
Detecting Whether a Headset Is Plugged into an Android Device or Not
Referencing a String in a String Array Resource with Xml
Remove Underline from Links in Textview - Android
Notification to Restore a Task Rather Than a Specific Activity
How to Compile the Android Aosp Kernel and Test It with the Android Emulator
Prevent Dialog Dismissal on Screen Rotation in Android
How to Get Access to Raw Resources That I Put in Res Folder
How to Set Versionname in APK Filename Using Gradle
Loading Existing .HTML File with Android Webview
How to Detect When Phone Is Answered or Rejected
How to Set Up Android Emulator Proxy Settings
About "_Id" Field in Android SQLite
Why Does Eclipse Automatically Add Appcompat V7 Library Support Whenever I Create a New Project