OnMeasure () 'ı geçersiz kılmanın yaratıcı yolu?


89

OnMeasure () 'ı geçersiz kılmanın doğru yolu nedir? Çeşitli yaklaşımlar gördüm. Örneğin, Profesyonel Android Geliştirme , boyutları hesaplamak için MeasureSpec'i kullanır ve ardından setMeasuredDimension () çağrısıyla sona erer. Örneğin:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
}

Öte yandan, bu gönderiye göre "doğru" yol, MeasureSpec'i kullanmak, setMeasuredDimensions () 'ı çağırmak, ardından setLayoutParams () çağrısı yapmak ve super.onMeasure () çağrısıyla bitirmektir. Örneğin:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Öyleyse doğru yol hangisi? Her iki yaklaşım da benim için% 100 işe yaramadı.

Sanırım gerçekten sorduğum şey, onMeasure (), düzen, çocuk görüşlerinin boyutları vb. Açıklayan bir öğretici biliyor mu?


16
cevabı faydalı / doğru buldunuz mu? neden olmasın olarak işaretlemek cevap?
superjos

Yanıtlar:


69

Diğer çözümler kapsamlı değil. Bazı durumlarda işe yarayabilir ve başlamak için iyi bir yerdir, ancak çalışmaları garanti edilmeyebilir.

OnMeasure arandığında, boyutu değiştirme hakkınız olabilir veya olmayabilir. OnMeasure ( widthMeasureSpec, heightMeasureSpec) öğenize aktarılan değerler, çocuğunuzun ne yapmasına izin verildiği hakkında bilgi içerir. Şu anda üç değer vardır:

  1. MeasureSpec.UNSPECIFIED - İstediğin kadar büyük olabilirsin
  2. MeasureSpec.AT_MOST- İstediğiniz kadar büyük (spesifikasyon boyutuna kadar), Bu parentWidthsizin örneğinizdedir.
  3. MeasureSpec.EXACTLY- Seçenek yok. Ebeveyn seçti.

Bu, Android'in her öğe için doğru boyutu bulmak üzere birden çok geçiş yapabilmesi için yapılır, daha fazla ayrıntı için buraya bakın.

Bu kurallara uymazsanız, yaklaşımınızın işe yarayacağı garanti edilmez.

Örneğin, boyutu değiştirmenize izin verilip verilmediğini kontrol etmek istiyorsanız, aşağıdakileri yapabilirsiniz:

final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

Bu bilgileri kullanarak, kodunuzdaki gibi değerleri değiştirip değiştiremeyeceğinizi bileceksiniz. Veya farklı bir şey yapmanız gerekiyorsa. İstediğiniz boyutu çözmenin hızlı ve kolay bir yolu, aşağıdaki yöntemlerden birini kullanmaktır:

int resolverSizeAndState (int size, int measureSpec, int childMeasuredState)

int SolutionSize (int size, int measureSpec)

İlki yalnızca Honeycomb'da mevcutken, ikincisi tüm sürümlerde mevcuttur.

Not: Bunu bulabilir resizeWidthveya resizeHeighther zaman yanlış olabilirsiniz. Bunu talep ediyor olsaydım durumun böyle olduğunu gördüm MATCH_PARENT. Bunu, WRAP_CONTENTana düzenimde istekte bulunarak ve ardından UNSPECIFIED aşamasında bir boyut isteyerek düzeltebildim Integer.MAX_VALUE. Bunu yapmak size ebeveyninizin bir sonraki onMeasure geçişinde izin verdiği maksimum boyutu verir.


39

Belgeler bu konudaki yetkidir : http://developer.android.com/guide/topics/ui/how-android-draws.html ve http://developer.android.com/guide/topics/ui/custom -components.html

Özetlemek gerekirse: geçersiz kılınan onMeasureyönteminizin sonunda aramalısınızsetMeasuredDimension .

Aradıktan super.onMeasuresonra aramamalısınız setMeasuredDimension, bu sadece ayarladığınız her şeyi silecektir. Bazı durumlarda super.onMeasureilkini aramak ve ardından arayarak sonuçları değiştirmek isteyebilirsiniz setMeasuredDimension.

Deme setLayoutParamsiçinde onMeasure. Düzen, ölçümden sonra ikinci bir geçişte gerçekleşir.


Benim deneyimim şudur: super.onMeasure'ı çağırmadan onMeasure'ı geçersiz kılarsam, OnLayout alt görüşlere çağrılmaz.
William Jockusch

2

Bence bu, geçersiz kıldığınız ebeveyne bağlı.

Örneğin, bir ViewGroup'u genişletiyorsanız (FrameLayout gibi), boyutu ölçtüğünüzde aşağıdaki gibi aramalısınız

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

çünkü ViewGroup'un dinlenme çalışması yapmak isteyebilirsiniz (çocuk görünümünde bazı şeyler yapın)

Bir Görünümü genişletiyorsanız (ImageView gibi), sadece çağırabilirsiniz this.setMeasuredDimension(width, height);, çünkü ana sınıf genellikle yaptığınız gibi bir şeyi yapacak.

Kısacası, ebeveyn sınıfınızın ücretsiz sunduğu bazı özellikleri istiyorsanız, aramanız gerekir super.onMeasure()(genellikle MeasureSpec.EXACTLY modu ölçü belirtimini geçirin), aksi takdirde çağrı this.setMeasuredDimension(width, height);yeterlidir.


1

sorunu şu şekilde çözdüm:

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

        ....

        setMeasuredDimension( measuredWidth, measuredHeight );

        widthMeasureSpec = MeasureSpec.makeMeasureSpec( measuredWidth, MeasureSpec.EXACTLY );
        heightMeasureSpec = MeasureSpec.makeMeasureSpec( measuredHeight, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

Ayrıca ViewPager bileşeni için gerekliydi


3
Bu uygun bir çözüm değil. super.onMeasurekullanılarak ayarlanan boyutları temizleyecektir setMeasuredDimension.
tomrozb

@tomrozb o değil humptydevelopers.com/2013/05/... ve android kaynaklarını kontrol edebilirsiniz
Yuli Reiri

@YuliReiri: Mükemmel çözüm!
Shayan Tabatabaee

0

onMeasureİhtiyacınız olan her şeyin içindeki görünüm boyutunu değiştirmek ise setMeasuredDimensionçağrıdır. Eğer dışında boyutunu değiştiriyorsanız onMeasurearamanız gerekir setLayoutParams. Örneğin, metin değiştirildiğinde bir metin görünümünün boyutunu değiştirmek.


Yine de setMinWidth () ve setMaxWidth () 'i çağırabilir ve standart onMeasure'ın bunu halletmesini sağlayabilirsiniz. Özel bir View sınıfının içinden setLayoutParams'ı çağırmamanın daha iyi olduğunu buldum. ViewGroups veya Activity'ler için çocuk görüntüleme davranışını geçersiz kılmak için gerçekten kullanılır.
Dorrin


-1

Sanırım, setLayoutParams ve ölçümleri yeniden hesaplamak, çocuk görünümlerini doğru şekilde yeniden boyutlandırmak için bir geçici çözümdür, çünkü bu genellikle türetilmiş sınıfın onMeasure öğesinde yapılır.

Ancak, bu nadiren doğru çalışır (herhangi bir nedenle ...), measureChildren'ı daha iyi çağırın (bir ViewGroup türeterken) veya gerektiğinde benzer bir şeyi deneyin.


-5

bu kod parçasını onMeasure () 'ın bir örneği olarak alabilirsiniz:

public class MyLayerLayout extends RelativeLayout {

    public MyLayerLayout(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        int currentChildCount = getChildCount();
        for (int i = 0; i < currentChildCount; i++) {
            View currentChild = getChildAt(i);

            //code to find information

            int widthPercent = currentChildInfo.getWidth();
            int heightPercent = currentChildInfo.getHeight();

//considering we will pass height & width as percentage

            int myWidth = (int) Math.round(parentWidth * (widthPercent / 100.0));
            int myHeight = (int) Math.round(parentHeight * (heightPercent / 100.0));

//Considering we need to set horizontal & vertical position of the view in parent

            AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP;
            AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT;
            int topPadding = 0;
            int leftPadding = 0;

            if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                topPadding = (parentHeight - myHeight) / 2;
            } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) {
                topPadding = parentHeight - myHeight;
            }

            if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                leftPadding = (parentWidth - myWidth) / 2;
            } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) {
                leftPadding = parentWidth - myWidth;
            }
            LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight);
            currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0);
            currentChild.setLayoutParams(myLayoutParams);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

6
Bu kod temelde yanlıştır. İlk olarak, düzen modunu hesaba katmaz (Grimmace'in cevabına bakın). Bu kötü, ancak bunun etkisini görmeyeceksiniz çünkü en sonunda super.onMeasure () 'ı çağırıyorsunuz, bu da ayarladığınız değerleri geçersiz kılar (bkz. Satur9nine'ın cevabı) ve onMeasure ()' ı geçersiz kılma amacını tamamen ortadan kaldırır.
spaaarky21
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.