web-dev-qa-db-ja.com

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

カスタムビューを作成するとき、多くの人が次のようにしていることに気付きました。

_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)についてはどうですか?どこで使用されているのかわかりませんが、スーパークラスで見ます。必要ですか、どこで使用されますか?

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

134
Micah Hainline

Viewからカスタムxmlを追加する場合も同様です:

_ <com.mypack.MyView
      ...
      />
_

コンストラクターpublic MyView(Context context, AttributeSet attrs)が必要です。そうでない場合、AndroidがExceptionを膨張させようとするとViewを取得します。

Viewからxmlを追加し、次のように_Android:style_属性も指定した場合:

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

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

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

139
Ovidiu Latcu

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

_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);
}
_

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

111
Jin

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

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

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

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

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

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

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

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

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

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番目のコンストラクターの実際の使用例は見当たりません。ウィジェットにデフォルトのスタイルを提供しないが、ユーザーにそれを許可したい場合のショートカットかもしれません。そんなに起こるべきではありません。

44
mbonnin

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

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

@JvmOverloadsは必要なすべてのコンストラクターを生成し(そのアノテーションの documentation を参照)、それぞれがおそらくsuper()を呼び出します。次に、初期化メソッドをKotlin init {}ブロックに置き換えます。定型コードがなくなった!

4
jules

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

Support-v7 SwitchCompactパッケージは、24バージョン以降はthumbTintおよびtrackTint属性をサポートしますが、23バージョンはそれらをサポートしていません。これを達成するために?

カスタムビューSupportedSwitchCompact extends 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()がそのスーパークラスSwitchCompactのメソッドであるため、getThumbDrawable()は常にnullを返します。

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

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

_frameworks/base/core/res/res/values/themes.xml_には、次が表示されます。

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