onMeasure özel görünüm açıklaması


316

Özel bileşen yapmaya çalıştım. ViewSınıfı genişlettim ve onDrawgeçersiz kılınan yöntemde bazı çizimler yaptım . Neden geçersiz kılmam gerekiyor onMeasure? Eğer yapmazsam, her şeyin doğru olduğu görülüyordu. Birisi açıklayabilir mi? Yöntemimi nasıl yazmalıyım onMeasure? Ben çift öğreticiler gördüm, ama her biri diğerinden biraz farklı. Bazen super.onMeasuresonunda ararlar, bazen kullanırlar setMeasuredDimensionve çağırmazlar. Bir fark nerede?

Sonuçta ben aynı bileşenleri birkaç kullanmak istiyorum. Bu bileşenleri dosyama ekledim XML, ancak ne kadar büyük olmaları gerektiğini bilmiyorum. (Ben de set boyutuna neden ihtiyaç sonradan konumunu ve boyutunu ayarlamak istiyorsanız onMeasureeğer onDrawbunu çizerken, hem de çalışıyor) özel bileşen sınıfında. Tam olarak ne zaman yapmam gerekiyor?

Yanıtlar:


735

onMeasure()Android'e, özel görünümünüzün ebeveyn tarafından sağlanan düzen kısıtlamalarına ne kadar büyük olmasını istediğinizi söyleme fırsatınızdır; ayrıca özel görünümünüzün bu düzen kısıtlamalarının ne olduğunu öğrenme fırsatıdır (bir match_parentdurumda bir durumdan farklı davranmak istemeniz wrap_contentdurumunda). Bu kısıtlamalar MeasureSpec, yönteme iletilen değerlere paketlenir . İşte mod değerlerinin kabaca bir korelasyonu:

  • TAMAMEN , layout_widthveya layout_heightdeğerinin belirli bir değere ayarlandığı anlamına gelir . Muhtemelen görüşünüzü bu boyutta yapmalısınız. Bu match_parent, boyutu tam olarak üst görünüme ayarlamak için kullanıldığında da tetiklenebilir (bu, çerçeveye göre düzendir).
  • AT_MOST tipik olarak, layout_widthya da layout_heightdeğeri ayarlanmıştır match_parentya da wrap_contentmaksimum boyutu gerekli olduğu (bu düzen çerçevesinde bağlıdır), ve üst boyut boyutu değeridir. Bu boyuttan daha büyük olmamalısınız.
  • BELİRTİLMEDİ, tipik olarak layout_widthveya layout_heightdeğerinin wrap_contentherhangi bir kısıtlama olmadan ayarlandığı anlamına gelir . İstediğiniz boyutta olabilirsiniz. Bazı mizanpajlar da, ikinci bir ölçüm isteğinde hangi özelliklerin sizi gerçekten geçeceğini belirlemeden önce istediğiniz boyutu bulmak için bu geri aramayı kullanır.

İle var olan sözleşme onMeasure()DİR setMeasuredDimension() GEREKİR Eğer görünüm olmak istiyorum boyutuyla sonunda çağrılabilir. Bu yöntem, bulunan varsayılan uygulama da dahil olmak üzere tüm çerçeve uygulamaları tarafından çağrılır View, bu nedenle superkullanım durumunuza uyuyorsa bunun yerine aramak güvenlidir .

Çerçeve varsayılan bir uygulama uyguladığından, bu yöntemi geçersiz kılmanız gerekmeyebilir, ancak görüntüleme alanının içeriğinizden daha küçük olduğu ve kullanmıyorsanız, kırpma görebilirsiniz. wrap_contentHer iki yönde de özel görünüm ile , çerçeveniz ne kadar büyük olduğunu bilmediği için görünümünüz hiç görünmeyebilir!

Genellikle, Viewbaşka bir mevcut widget'ı geçersiz kılıyorsanız, böyle bir şey kadar basit olsa bile, bir uygulama sağlamak muhtemelen iyi bir fikirdir:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desiredWidth = 100;
    int desiredHeight = 100;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    //MUST CALL THIS
    setMeasuredDimension(width, height);
}

Umarım yardımcı olur.


1
Hey @Devunwired güzel açıklama şimdiye kadar okuduğum en iyi. Açıklamanız birçok soruya cevap verdi ve bazı şüpheleri temizledi, ancak yine de bir tanesi kaldı: Özel görünümüm diğerleriyle birlikte bir ViewGroup içinde ise ViewGroup'un tüm çocuklarını alacağı Views (hangi türlerin önemi yoktur) Her bir prob için LayoutParams kısıtlamaları için ve her çocuktan kendi kısıtlamalarına göre kendini ölçmesini ister misiniz?
firavun

47
Herhangi bir ViewGroup alt sınıfının onMeasure değerini geçersiz kılarsanız bu kodun çalışmadığını unutmayın. Alt görünümleriniz görünmez ve hepsinin boyutu 0x0 olur. Özel bir ViewGroup öğesinin onMeasure değerini geçersiz kılmanız gerekiyorsa, widthMode, widthSize, heightMode ve heightSize değerlerini değiştirin, MeasureSpec.makeMeasureSpec öğesini kullanarak bunları ölçüSpec'lerini kullanarak geri derleyin ve sonuçtaki tam sayıları super.onMeasure'a geçirin.
Alexey

1
Harika bir cevap. Google'ın dokümanlarına göre, görünümün doldurma işleminin sorumluluğunda olduğunu unutmayın.
jonstaff

4
Aşırı karmaşık c ** p Android ile çalışmak için acı verici bir düzen sistemi yapar. Onlar sadece getParent () vardı. Olsun *** () ...
Oliver Dixon

2
ViewSınıfta denilen resolveSizeAndStateve resolveSize'if' cümleciklerinin yaptığı şeyi yapması gereken yardımcı yöntemler vardır - özellikle bu IF'leri sık sık yazmak zorunda kalırsanız, bunları yararlı buldum.
stan0

5

aslında, değerler ambalaj kabına da bağlı olduğundan cevabınız tam değildir. Göreli veya doğrusal mizanpajlarda, değerler şu şekilde davranır:

  • TAM match_parent ebeveynin TAM + boyut
  • AT_MOST wrap_content değeri AT_MOST MeasureSpec ile sonuçlanır
  • BELİRTİLMEDİ asla tetiklenmedi

Yatay kaydırma görünümünde, kodunuz çalışır.


57
Burada bazı cevapların eksik olduğunu düşünüyorsanız, kısmi cevap vermek yerine lütfen cevabınızı ekleyin.
Michaël

1
Bunu düzenlerin nasıl çalıştığına bağladığınız için iyi onya, ancak benim durumumdaMeasure özel görünümüm için üç kez çağrıldı. Söz konusu görünümde bir wrap_content yüksekliği ve ağırlıklı bir genişlik (genişlik = 0, ağırlık = 1) vardı. İlk çağrı BELİRTİLMEDİ / BELİRTİLMEDİ, ikincisi AT_MOST / TAM OLARAK ve üçüncüsü TAM / TAM OLARAK vardı.
William T. Mallard

0

Ölçümde bir şeyi değiştirmeniz gerekmiyorsa - kesinlikle geçersiz kılmanıza gerek yoktur.

Devunwired kodu (burada seçilen ve en çok oylanan cevap), SDK uygulamasının sizin için zaten yaptığıyla neredeyse aynı (ve kontrol ettim - 2009'dan beri yapmıştı).

OnMeasure yöntemini buradan kontrol edebilirsiniz :

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

Aynı kodla değiştirilecek SDK kodunun geçersiz kılınması mantıklı değildir.

"Varsayılan onMeasure () öğesinin her zaman 100x100 boyutuna ayarlanacağını" iddia eden bu resmi dokümanın parçası yanlıştır.

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.