Android özel görünümü için her üç kurucuya da ihtiyacım var mı?


143

Özel bir görünüm oluştururken, birçok insanın bunu böyle yaptığını fark ettim:

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.
}

İlk sorum, kurucu ne olacak MyView(Context context, AttributeSet attrs, int defStyle)? Nerede kullanıldığından emin değilim, ama süper sınıfta görüyorum. İhtiyacım var ve nerede kullanılıyor?

Orada bu soruya başka bir parçası .

Yanıtlar:


145

Eğer özel katacak Eğer Viewgelen xmlde şöyle:

 <com.mypack.MyView
      ...
      />

yapıcıya ihtiyacınız olacak public MyView(Context context, AttributeSet attrs), aksi takdirde ExceptionAndroid cihazınızı şişirmeye çalıştığında bir alacaksınız View.

Eğer senin eklemek Eğer Viewgelen xmlve aynı zamanda belirtmek android:stylegibi niteliğini:

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

2. kurucu da çağrılır ve MyCustomStylemüstehcen XML niteliklerini uygulamadan önce stili varsayılan olarak ayarlar.

Üçüncü kurucu genellikle uygulamanızdaki tüm Görünümlerin aynı stile sahip olmasını istediğinizde kullanılır.


3
ilk kurucu ne zaman kullanılır?
Android Katili

@OvidiuLatcu üçüncü CTOR örneğini (3 parametreli) gösterebilir misiniz?
android geliştirici

yapıcıya fazladan parametre ekleyebilir miyim ve bunları nasıl kullanabilirim?
Muhammed Subhi Şeyh Quroush

24
Üçüncü kurucu ile ilgili olarak, bu aslında tamamen yanlıştır . XML her zaman iki argümanlı yapıcıyı çağırır. Üç bağımsız değişken (ve dört bağımsız değişken ) kurucuları, varsayılan stil veya doğrudan varsayılan stil içeren bir öznitelik belirtmek istiyorlarsa alt sınıflar tarafından çağrılırlar (dört bağımsız değişken kurucusu durumunda)
imgx64

Cevabı düzeltmek için yeni bir düzenleme gönderdim. Ayrıca aşağıda alternatif bir cevap önerdim.
mbonnin

118

Her üç kurucuyu da geçersiz kılarsanız, lütfen this(...)ARAMALARI KADAR BIRAKMAYIN . Bunun yerine bunu yapmalısınız:

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
}

Bunun nedeni, üst sınıfın kendi kurucularına yanlışlıkla geçersiz kılabileceğiniz varsayılan öznitelikleri içermesidir. Örneğin, bu yapıcı 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);
}

Eğer aramadıysanız, stil attr olarak super(context)uygun şekilde ayarlamazdınız R.attr.textViewStyle.


12
ListView genişletilirken bu çok önemlidir. Yukarıdaki bu basamaklı (önceki) bir hayranı olarak, her kurucu için doğru süper yöntemi çağırdığımda ortadan kaybolan ince bir hatayı takip ederek saatler harcadığımı hatırlıyorum.
Groovee60

BTW @Jin Bu yanıtta kodu kullandım: cevabınıza dayanıyor gibi görünen stackoverflow.com/a/22780035/294884 - ancak yazarın Şişirme kullanımını içerdiğini unutmayın?
Fattie

1
Tüm kurucularda init çağırmanın gerekli olmadığını düşünüyorum, çünkü çağrı hiyerarşisini izlediğinizde, yine de programatik görünüm oluşturma için varsayılan
kurucuda görüneceksiniz

Im aynı yapıyor ama benim özel görünümde kullanılabilir metin görünümünde değerleri ayarlamak için başarısız ben etkinlik değer ayarlamak istiyorum
Erum

1
Bunu nasıl hiç bilmiyordum?
17'de Suragch

49

MyView (Bağlam içeriği)

Görünümleri programlı olarak başlatırken kullanılır.

MyView (Bağlam içeriği, AttributeSet attrs)

Tarafından LayoutInflaterxml özniteliklerini uygulamak için kullanılır . Bu özniteliklerden biri adlandırılmışsa style, mizanpaj xml dosyasında müstehcen değerler aranmadan nitelikler aranır.

MyView (Bağlam bağlamı, AttributeSet attrs, int defStyleAttr)

styleHer bir düzen dosyasında belirtmek zorunda kalmadan tüm widget'lara varsayılan bir stil uygulamak istediğinizi varsayalım . Örnek olarak, tüm onay kutularını varsayılan olarak pembe yapın. Bunu defStyleAttr ile yapabilirsiniz ve çerçeve temanızdaki varsayılan stili arayacaktır.

Bunun bir süre önce defStyleAttryanlış adlandırıldığını defStyleve bu kurucunun gerçekten gerekli olup olmadığı hakkında bazı tartışmalar olduğunu unutmayın. Bkz. Https://code.google.com/p/android/issues/detail?id=12683

MyView (Bağlam içeriği, AttributeSet attrs, int defStyleAttr, int defStyleRes)

Uygulamaların temel teması üzerinde kontrolünüz varsa 3. kurucu iyi çalışır. Bu, Google için çalışıyor çünkü widget'larını varsayılan Temalar boyunca gönderiyorlar. Ancak, bir widget kitaplığı yazdığınızı ve kullanıcılarınızın temalarını değiştirmesine gerek kalmadan varsayılan bir stilin ayarlanmasını istediğinizi varsayalım. Şimdi bunu defStyleResilk 2 yapıcıdaki varsayılan değere ayarlayarak yapabilirsiniz :

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

Neticede

Kendi görüşlerinizi uyguluyorsanız, yalnızca ilk 2 kurucuya ihtiyaç duyulmalıdır ve çerçeve tarafından çağrılabilir.

Görüşlerinizin genişletilebilir olmasını istiyorsanız, sınıfınızın çocukları için küresel stil kullanabilmek için 4. yapıcıyı uygulayabilirsiniz.

3. kurucu için gerçek bir kullanım durumu görmüyorum. Widget'ınız için varsayılan bir stil sağlamaz, ancak yine de kullanıcılarınızın bunu yapabilmesini istiyorsanız bir kısayol olabilir. O kadar da olmamalı.


7

Kotlin, bu acıdan çok şey alıyor gibi görünüyor:

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

@JvmOverloads , her biri muhtemelen super () olarak adlandırılan gerekli tüm kurucuları oluşturur (bu ek açıklamanın belgelerine bakın ). Ardından, başlatma yönteminizi bir Kotlin init {} bloğu ile değiştirin. Ortak kod gitti!


1

Üçüncü kurucu çok daha karmaşık, bir örnek vereyim.

Support-v7 SwitchCompactpaketi 24 sürümünden bu yana 23 sürümü desteklemiyor thumbTintve trackTintözniteliğini destekliyor . 23 sürümünde bunları desteklemek istiyorsunuz ve bunu başarmak için ne yapacaksınız?

Özel Görünüm SupportedSwitchCompactuzantılarını kullandığımızı varsayıyoruz 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();
}

Geleneksel bir kod stili. Burada üçüncü parametreye 0 ilettiğimizi unutmayın . Kodu çalıştırdığınızda, getThumbDrawable()yöntem getThumbDrawable()süper sınıfın yöntemi olduğundan, her zaman ne kadar garip olduğunu döndürür SwitchCompact.

Eğer başarılı olursa R.attr.switchStyleüçüncü param için, her şey well.So neden gider?

Üçüncü parametre basit bir özelliktir. Öznitelik bir stil kaynağına işaret eder. Yukarıdaki durumda sistem, switchStylemevcut temada öznitelik bulacaktır .

İçinde frameworks/base/core/res/res/values/themes.xmlgöreceksiniz:

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

-2

Şimdi tartışılan gibi üç kurucu eklemeniz gerekiyorsa, bunu da yapabilirsiniz.

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 Bu birçok durumda iyi bir fikirdir, ancak bu birçok durumda da güvenlidir (örn: RelativeLayout, FrameLayout, RecyclerView, vb.). Yani, bunun muhtemelen duruma göre bir gereklilik olduğunu söyleyebilirim ve temel sınıf, art arda karar vermeden önce kontrol edilmelidir. Temel olarak, temel sınıftaki 2-param yapıcısı bunu çağırıyorsa (bağlam, attrs, 0), özel görünüm sınıfında da bunu yapmak güvenlidir.
ejw

@IanWong, elbette, çağrılacak, çünkü birinci ve ikinci yöntemler üçüncü çağırıyor.
CoolMind
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.