I have tried many solutions found in google by the keywords: multiple constructors, scala, inheritance, subclasses.
None seems to work for this occasion. ImageView
has three constructors:
ImageView(context)
ImageView(context,attribute set)
ImageView(context,attribute set, style)
In scala you can only extend one of them. And the solution of using the more complete constructor (ImageView(context,attribute set, style)
) and passing default values does not work either because the constructor ImageView(context)
does something completely different than the other two constructors.
Some solutions of using a trait or a companion object does not seem to work because the CustomView must be a class! I mean I am not the only one who uses this class (so I could write the scala code any way I wanted) there is also the android-sdk who uses this class and yes it must be a class.
target is to have a CustomView which extends ImageView and all of these work:
new CustomView(context)
new CustomView(context,attribute set)
new CustomView(context,attribute set, style)
Please let me know if you need any further clarification on this tricky matter!
According to the Android View
documentation View(Context)
is used when constructed from code and View(Context, AttributeSet)
and View(Context, AttributeSet, int)
(and since API level 21 View(Context, AttributeSet, int, int)
) are used when the View
is inflated from XML.
The XML constructor all just call the same constructor, the one with the most arguments which is the only one with any real implementation, so we can use default arguments in Scala. The "code constructor" on the other hand may have another implementation, so it is better to actually call in from Scala as well.
The following implementation may be a solution:
private trait MyViewTrait extends View {
// implementation
}
class MyView(context: Context, attrs: AttributeSet, defStyle: Int = 0)
extends View(context, attrs, defStyle) with MyViewTrait {}
object MyView {
def apply(context: Context) = new View(context) with MyViewTrait
}
The "code constructor" may then be used like:
var myView = MyView(context)
(not a real constructor).
And the other once like:
var myView2 = new MyView(context, attrs)
var myView3 = new MyView(context, attrs, defStyle)
which is the way the SDK expects them.
Analogously for API level 21 and higher the class
can be defined as:
class MyView(context: Context, attrs: AttributeSet, defStyle: Int = 0, defStyleRes: Int = 0)
extends View(context, attrs, defStyle, defStyleRes) with MyViewTrait {}
and the forth constructor can be used like:
var myView4 = new MyView(context, attrs, defStyle, defStyleRes)
Update:
It gets a bit more complicated if you try to call a protected
method in View
, like setMeasuredDimension(int, int)
from the trait
. Java protected methods cannot be called from traits. A workaround is to implement an accessor in the class
and object
implementations:
private trait MyViewTrait extends View {
protected def setMeasuredDimensionAccessor(w: Int, h: Int): Unit
def callingSetMeasuredDimensionAccessor(): Unit = {
setMeasuredDimensionAccessor(1, 2)
}
}
class MyView(context: Context, attrs: AttributeSet, defStyle: Int = 0)
extends View(context, attrs, defStyle) with MyViewTrait {
override protected def setMeasuredDimensionAccessor(w: Int, h: Int) =
setMeasuredDimension(w, h)
}
object MyView {
def apply(context: Context) = new View(context) with MyViewTrait {
override protected def setMeasuredDimensionAccessor(w: Int, h: Int) =
setMeasuredDimension(w, h)
}
}