64

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)

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


更新: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
4

8 に答える 8

90

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) {}
}

詳細はこちらこちら

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

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

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

于 2015-03-20T14:11:29.623 に答える
7

TL;DRほとんどの場合、カスタム ビューを次のように定義するだけで十分です。

class MyView(context: Context, attrs: AttributeSet?) : FooView(context, attrs)

この Java コードを考えると:

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

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

その Kotlin に相当するものは、2 次コンストラクターを使用します。

class MyView : View {
    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
}

この構文は、ビューがコードで作成されているか、XML から拡張されているかに応じて、実際に異なるスーパークラス コンストラクターを呼び出したい場合に役立ちます。これが当てはまると私が知っている唯一のケースは、Viewクラスを直接拡張している場合です。

それ以外の場合は、デフォルトの引数と@JvmOverloads注釈を使用してプライマリ コンストラクターを使用できます。

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

@JvmOverloads constructorJava から呼び出す予定がない場合は必要ありません。

また、XML からビューをインフレートするだけの場合は、最も単純なものを使用できます

class MyView(context: Context, attrs: AttributeSet?) : View(context, attrs)

クラスがopen拡張用であり、親のスタイルを保持する必要がある場合は、2 次コンストラクターのみを使用する最初のバリアントに戻ります。

open class MyView : View {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
}

しかしopen、親スタイルをオーバーライドし、そのサブクラスにもオーバーライドさせるクラスが必要な場合は、次のようにして問題ありません@JvmOverloads

open class MyView @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = R.attr.customStyle,
        defStyleRes: Int = R.style.CustomStyle
) : View(context, attrs, defStyleAttr, defStyleRes)
于 2019-09-27T00:08:03.277 に答える
0

複数のコンストラクターで XML レイアウトを拡張してカスタム ビューを作成する完全な例を追加しました

class MyCustomView : FrameLayout {
    private val TAG = MyCustomView ::class.simpleName

    constructor(context: Context): super(context) {
        initView()
    }

    constructor(context: Context, attr: AttributeSet? = null): super(context, attr) {
        initView()
    }

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ):   super(context, attrs, defStyleAttr) {
        initView()
    }

    /**
     * init View Here
     */
    private fun initView() {
       val rootView = (context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)
            .inflate(R.layout.layout_custom_view, this, true)

       // Load and use rest of views here
       val awesomeBG= rootView.findViewById<ImageView>(R.id.awesomeBG)
      
}

XMLでlayout_custom_viewビューファイルを追加します

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  
    <ImageView
        android:id="@+id/awesomeBG"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="@string/bg_desc"
        android:fitsSystemWindows="true"
        android:scaleType="centerCrop" />

    <!--ADD YOUR VIEWs HERE-->
 
   </FrameLayout>
于 2020-07-24T12:03:45.463 に答える
-6

JetBrains から Kotlin 用の新しいライブラリAnkoを試すことができます ( githubに投稿することもできます)。現在はベータ版ですが、このようなコードでビューを作成できます

    button("Click me") {
         textSize = 18f
         onClick { toast("Clicked!") }
    }

このライブラリを見てください

于 2015-04-09T14:18:51.543 に答える