Androidカスタムビューに3つすべてのコンストラクターが必要ですか?


142

カスタムビューを作成するとき、多くの人が次のように実行しているようです。

public MyView(Context context) {
  super(context);
  // this constructor used when programmatically creating view
  doAdditionalConstructorWork();
}

public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // this constructor used when creating view through XML
  doAdditionalConstructorWork();
}

private void doAdditionalConstructorWork() {

  // init variables etc.
}

私の最初の質問は、コンストラクタはMyView(Context context, AttributeSet attrs, int defStyle)どうですか?どこで使用されているかはわかりませんが、スーパークラスで見かけます。必要ですか、どこで使用されますか?

この質問には別の部分があります。

回答:


144

あなたもViewからのxmlようなカスタムを追加する場合:

 <com.mypack.MyView
      ...
      />

コンストラクタが必要ですpublic MyView(Context context, AttributeSet attrs)。それ以外の場合はException、Androidがを膨らませようとしたときにを取得しますView

Viewfrom を追加し、次のような属性をxml指定したandroid:style場合:

 <com.mypack.MyView
      style="@styles/MyCustomStyle"
      ...
      />

2番目のコンストラクターも呼び出され、MyCustomStyle明示的なXML属性を適用する前にスタイルがデフォルトになります。

3番目のコンストラクタは、通常、アプリケーションのすべてのビューに同じスタイルを設定する場合に使用されます。


3
最初のコンストラクタをいつ使用するのですか?
Android Killer

@OvidiuLatcu(3つのパラメーターを使用した)3番目のCTORの例を示していただけますか?
android開発者

コンストラクターに追加のパラメーターを追加できますか、どのように使用できますか?
Mohammed Subhi Sheikh Quroush 2013

24
3番目のコンストラクタに関しては、これは実際には完全に間違っています。XMLは常に 2つの引数のコンストラクターを呼び出します。3つの引数(および4つの引数)のコンストラクターは、デフォルトのスタイルまたはデフォルトのスタイルを直接含む属性(4つの引数のコンストラクターの場合)を指定する場合にサブクラスによって呼び出されます
imgx64

答えを正しくするために編集を送信しました。以下の代替回答も提案しました。
mbonnin

117

3つのコンストラクターをすべてオーバーライドする場合は、this(...)呼び出しをカスケードしないでください。代わりにこれを行う必要があります:

public MyView(Context context) {
    super(context);
    init(context, null, 0);
}

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

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

private void init(Context context, AttributeSet attrs, int defStyle) {
    // do additional work
}

その理由は、親クラスが独自のコンストラクターにデフォルトの属性を含み、誤ってオーバーライドしてしまう可能性があるためです。たとえば、これはのコンストラクタですTextView

public TextView(Context context) {
    this(context, null);
}

public TextView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.textViewStyle);
}

public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, 0);
}

を呼び出さなかった場合、スタイルattrとしてsuper(context)適切に設定さR.attr.textViewStyleれていません。


12
これは、ListViewを拡張する場合の重要なアドバイスです。上記のこのカスケードの(以前の)ファンとして、コンストラクタごとに適切なスーパーメソッドを呼び出したときに消えた微妙なバグを追跡するために何時間も費やしたことを思い出します。
Groovee60 2015

ところで、私はこの回答でコードを使用しました:stackoverflow.com/a/22780035/294884これはあなたの回答に基づいているようですが、ライターにはインフレータの使用が含まれていることに注意してください。
Fattie 2016年

1
すべてのコンストラクターでinitを呼び出す必要はないと思います。呼び出し階層に従うと、プログラムビューの作成用のデフォルトコンストラクターになってしまうからです
。View

私は同じことをしていますが、カスタムビューで使用可能なtextviewに値を設定できませんでした。アクティビティから値を設定したい
Erum

1
どうしてこれを知らなかったのですか?
Suragch

49

MyView(コンテキストコンテキスト)

プログラムでビューをインスタンス化するときに使用されます。

MyView(コンテキストコンテキスト、AttributeSet属性)

LayoutInflaterがxml属性を適用するために使用します。この属性の1つがという名前の場合style、レイアウトxmlファイルで明示的な値を検索する前に、属性がスタイルを検索します。

MyView(コンテキストコンテキスト、AttributeSet属性、int defStyleAttr)

style各レイアウトファイルで指定する必要なく、すべてのウィジェットにデフォルトスタイルを適用するとします。例として、すべてのチェックボックスをデフォルトでピンクにします。これはdefStyleAttrで行うことができ、フレームワークはテーマのデフォルトスタイルを検索します。

以前defStyleAttrに誤って名前が付けられたことに注意してくださいdefStyle。このコンストラクタが本当に必要かどうかについては議論があります。https://code.google.com/p/android/issues/detail?id=12683ご覧ください

MyView(コンテキストコンテキスト、AttributeSet attrs、int defStyleAttr、int defStyleRes)

3番目のコンストラクターは、アプリケーションの基本テーマを制御できる場合に適切に機能します。彼らはデフォルトのテーマと一緒にウィジェットを出荷するので、それはグーグルのために働いています。しかし、ウィジェットライブラリを作成していて、ユーザーがテーマを微調整する必要なくデフォルトのスタイルを設定したいとします。これを使用defStyleResして、2つの最初のコンストラクターでデフォルト値を設定することにより、これを行うことができます。

public MyView(Context context) {
  super(context, null, 0, R.style.MyViewStyle);
  init();
}

public MyView(Context context, AttributeSet attrs) {
  super(context, attrs, 0, R.style.MyViewStyle);
  init();
}

概して

独自のビューを実装している場合は、最初の2つのコンストラクターのみが必要であり、フレームワークから呼び出すことができます。

ビューを拡張可能にしたい場合は、クラスの子に4番目のコンストラクタを実装して、グローバルスタイルを使用できるようにすることができます。

3番目のコンストラクタの実際の使用例は見当たりません。ウィジェットにデフォルトのスタイルを提供していなくても、ユーザーがそれを行えるようにしたい場合は、おそらくショートカットになります。そんなに起こらないはずです。


7

Kotlinはこの痛みの多くを取り除くようです:

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

@JvmOverloadsは、必要なすべてのコンストラクタを生成し(その注釈のドキュメントを参照)、それぞれがおそらくsuper()を呼び出します。次に、初期化メソッドをKotlin init {}ブロックに置き換えるだけです。定型コードがなくなりました!


1

3番目のコンストラクターははるかに複雑です。例を挙げましょう。

Support-v7 SwitchCompactパッケージは24バージョン以降のサポートthumbTinttrackTint属性をサポートしていますが、23バージョンはそれらをサポートしていません。次に、23バージョンでそれらをサポートし、これを実現する方法を教えてください。

カスタムビューSupportedSwitchCompact拡張の使用を想定しています SwitchCompact

public SupportedSwitchCompat(Context context) {
    this(context, null);
}

public SupportedSwitchCompat(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public SupportedSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init(){
    mThumbDrawable = getThumbDrawable();
    mTrackDrawable = getTrackDrawable();
    applyTint();
}

これは従来のコードスタイルです。ここでは3番目のパラメータに0を渡しています。コードを実行するgetThumbDrawable()と、メソッドgetThumbDrawable()がそのスーパークラスSwitchCompactのメソッドであるため、どれほど奇妙であるかが常にnullで返されます。

R.attr.switchStyle3番目のパラメーターに渡すと、すべてがうまくいきます。

3番目のパラメーターは単純な属性です。属性はスタイルリソースを指します。上記の場合、システムはswitchStyle現在のテーマで属性を見つけますが、幸い、システムがそれを見つけます。

ではframeworks/base/core/res/res/values/themes.xml、次のようになります。

<style name="Theme">
    <item name="switchStyle">@style/Widget.CompoundButton.Switch</item>
</style>

-2

現在議論中のような3つのコンストラクターを含める必要がある場合は、これも行うことができます。

public MyView(Context context) {
  this(context,null,0);
}

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

public MyView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  doAdditionalConstructorWork();

}

2
@Jin多くの場合これは良い考えですが、これは多くの場合安全です(例:RelativeLayout、FrameLayout、RecyclerViewなど)。したがって、これはおそらくケースバイケースの要件であり、カスケードするかどうかを決定する前に基本クラスをチェックアウトする必要があります。基本的に、基本クラスの2-paramコンストラクターがthis(context、attrs、0)を呼び出すだけの場合は、カスタムビュークラスでも同じように呼び出すことができます。
ejw

もちろん、@ IanWongは呼び出されます。これは、1番目と2番目のメソッドが3番目を呼び出すためです。
CoolMind 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.