web-dev-qa-db-ja.com

Kotlinでカスタムビューのコンストラクターを作成する方法

AndroidプロジェクトでKotlinを使用しようとしています。カスタムビュークラスを作成する必要があります。各カスタムビューには2つの重要なコンストラクタがあります。

_public class MyView extends View {
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}
_

MyView(Context)はコードでビューをインスタンス化するために使用され、MyView(Context, AttributeSet)はXMLからレイアウトを拡張するときにレイアウトインフレータによって呼び出されます。

この質問 への回答は、デフォルト値またはファクトリーメソッドでコンストラクターを使用することを示唆しています。しかし、ここに私たちが持っているものがあります:

工場メソッド:

_fun MyView(c: Context) = MyView(c, attrs) //attrs is nowhere to get
class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }
_

または

_fun MyView(c: Context, attrs: AttributeSet) = MyView(c) //no way to pass attrs.
                                                        //layout inflater can't use 
                                                        //factory methods
class MyView(c: Context) : View(c) { ... }
_

デフォルト値のコンストラクタ:

_class MyView(c: Context, attrs: AttributeSet? = null) : View(c, attrs) { ... }
//here compiler complains that 
//"None of the following functions can be called with the arguments supplied."
//because I specify AttributeSet as nullable, which it can't be.
//Anyway, View(Context,null) is not equivalent to View(Context,AttributeSet)
_

このパズルはどのように解決できますか?


UPDATE:View(Context, null)の代わりにView(Context)スーパークラスコンストラクターを使用できるようであるため、ファクトリメソッドアプローチが解決策です。しかし、それでも私のコードを機能させることはできません:

_fun MyView(c: Context) = MyView(c, null) //compilation error here, attrs can't be null
class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }
_

または

_fun MyView(c: Context) = MyView(c, null) 
class MyView(c: Context, attrs: AttributeSet?) : View(c, attrs) { ... }
//compilation error: "None of the following functions can be called with 
//the arguments supplied." attrs in superclass constructor is non-null
_
40

Kotlinは、2015年3月19日にリリースされたM11以降、複数のコンストラクターをサポートしています。構文は次のとおりです。

class MyView : View {
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
        // ...
    }

    constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {}
}

詳細 here および here

Edit:@JvmOverloadsアノテーションを使用して、Kotlinが必要なコンストラクターを自動生成することもできます。

class MyView @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyle: Int = 0
) : View(context, attrs, defStyle)

ただし、このアプローチでは、継承元のクラスがコンストラクタをどのように定義するかに応じて、予期しない結果が生じる場合があるため注意してください。何が起こるかについての良い説明は あの記事 にあります。

41
aga

アノテーションJvmOverloads(Kotlin 1.0のように見える)を使用する必要があります。次のようなコードを記述できます。

class CustomView @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyle: Int = 0
) : View(context, attrs, defStyle)

これにより、おそらく必要な3つのコンストラクターが生成されます。

docs からの引用:

デフォルト値を持つすべてのパラメーターについて、これにより追加のオーバーロードが1つ生成されます。これにより、このパラメーターと、パラメーターリストでその右側のすべてのパラメーターが削除されます。

49
colriot

カスタムViewとkotlinのサンプルコードを次に示します。

class TextViewLight : TextView {

constructor(context: Context) : super(context){
    val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5);
    setTypeface(typeface)
}

constructor(context: Context, attrs : AttributeSet) : super(context,attrs){
    val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5);
    setTypeface(typeface)
}

constructor(context: Context,  attrs: AttributeSet , defStyleAttr : Int) : super(context, attrs, defStyleAttr){
    val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5);
    setTypeface(typeface)
}

}
6
Sagar Jethva

これは問題のようです。私のカスタムビューはxmlのみまたはコードのみで作成されているため、これに遭遇することはありませんが、これがどこで行われるかはわかります。

私が見る限り、これを回避する方法は2つあります。

1)attrsでコンストラクターを使用します。 xmlでビューを使用すると正常に機能します。コードでは、ビューに必要なタグを使用してxmlリソースをインフレートし、属性セットに変換する必要があります。

val parser = resources.getXml(R.xml.my_view_attrs)
val attrs = Xml.asAttributeSet(parser)
val view = MyView(context, attrs)

2)attrsなしでコンストラクターを使用します。ビューをxmlに直接配置することはできませんが、xmlにFrameLayoutを配置し、コードを介してビューを追加することは簡単です。

5
ajselvig

コンストラクタをオーバーライドする方法はいくつかありますが、

デフォルトの動作が必要な場合

class MyWebView(context: Context): WebView(context) {
    // code
}

複数のバージョンが必要な場合

class MyWebView(context: Context, attr: AttributeSet? = null): WebView(context, attr) {
    // code
}

内部でparamsを使用する必要がある場合

class MyWebView(private val context: Context): WebView(context) {
    // you can access context here
}

読みやすくするために、よりクリーンなコードが必要な場合

class MyWebView: WebView {

    constructor(context: Context): super(context) {
        mContext = context
        setup()
    }

    constructor(context: Context, attr: AttributeSet? = null): super(context, attr) {
        mContext = context
        setup()
    }
}
0