Guide to Android custom views: constructors
I’ve been designing, writing and publishing Android custom views for the past 5 years now. I guess it’s about time to sum it up and share the results.
The article series here, on Medium, is a copy of materials published on my repository, which you can find under the following link: https://github.com/ZieIony/GuideToCustomViews. There you can also find case studies, issues and my other projects including custom views.
Topics
Constructors
Each Android view has up to 4 constructors. In the simple scenario, you usually need only the first two.
View(Context context)
This is the code constructor. It can be called by a programmer directly from code to create a new instance of the view. Pretty simple. This constructor doesn’t have access to XML attributes, so you have to fill the parameters manually, using setters.
View(Context context, AttributeSet attrs)
This is the basic XML constructor. Without it, the layout inflater will crash. The AttributeSet parameter contains all attribute values provided via XML. Let’s skip accessing the attributes for now.
View(Context context, AttributeSet attrs, int defStyleAttr)
View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
The two other constructors are meant to be called by child classes to provide default style via theme attribute and direct default style resource. The defStyleAttr
parameter is the reference to a style attribute defined in the theme. The defStyleRes
parameter is the reference to the default style defined in styles.xml. Don't worry, if this explanation is unsatisfying to you. I will go deeper into details in the section about attributes and styles. Now let's take a look at our first custom view code.
This is an example of a basic, custom Button
class. Notice that I'm calling corresponding super constructors in all cases. This is the safest approach because the base class can handle its own default parameters. We could also call the 3-parameter (or 4-parameter on API≥21) constructor assuming that the default style attribute of the base class won't change. That would also reduce the call stack depth.
Now the more interesting case. The following class extends TextView
but requires its own style attribute. That's why it calls the 3-parameter constructor. My library runs on API<21, so I can't call the 4-parameter constructor, but on API≥21 that would be valid too. This time in Kotlin.
If we called the corresponding super constructors, the view would take our custom parameters via the correct attribute, but other, inherited attributes would come via the original attribute, which is incorrect. Also, it’s important to note that the view’s style extends the style of the base class so the base class can find its attributes as well.
How about using @JvmOverloads
? It works fine in both cases but adds unnecessary calls and can lead to StackOverflow on older devices. Just like using this constructor chain. On API<21 @JvmOverloads
would also incorrectly call the 4-parameter constructor, so you would have to provide 3-parameter constructor with @JvmOverloads
and a plain, 4-parameter constructor. Phew, so many details for starters.
If you target API≥21, this code is much shorter and works fine as well:
Remember that if you don’t plan to extend or publish the view, you can provide only two constructors.
What about custom constructors, to make your life easier? Why not. Here’s a simple view class with three constructors — code, custom and XML: