Do I Need All Three Constructors for an Android Custom View

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 pass 0). It can be 0 only to not look for defaults.

    For example in the MaterialButton: R.attr.materialButtonStyle

  • The defStyleRes is a resource identifier of a style resource that supplies default values for the view, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.

    For example in the MaterialButton: 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 and defStyleRes
  • 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:

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

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



Leave a reply



Submit