Bir görünümün boyutları nasıl alınır?


209

Bir görüşüm var TableLayout, TableRow and TextView. Izgara gibi görünmesini istiyorum. Bu ızgaranın yüksekliğini ve genişliğini almam gerekiyor. MetodlargetHeight() ve getWidth()her zaman 0 döndürür. Bu ızgara dinamik olarak biçimlendirdiğinizde ve bir XML sürümü kullandığımda olur.

Bir görünümün boyutları nasıl alınır?


Sonuçları kontrol etmek için Debug'da kullandığım test programım:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TextView;

public class appwig extends Activity {  
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.maindemo);  //<- includes the grid called "board"
      int vh = 0;   
      int vw = 0;

      //Test-1 used the xml layout (which is displayed on the screen):
      TableLayout tl = (TableLayout) findViewById(R.id.board);  
      tl = (TableLayout) findViewById(R.id.board);
      vh = tl.getHeight();     //<- getHeight returned 0, Why?  
      vw = tl.getWidth();     //<- getWidth returned 0, Why?   

      //Test-2 used a simple dynamically generated view:        
      TextView tv = new TextView(this);
      tv.setHeight(20);
      tv.setWidth(20);
      vh = tv.getHeight();    //<- getHeight returned 0, Why?       
      vw = tv.getWidth();    //<- getWidth returned 0, Why?

    } //eof method
} //eof class

getWidth / Height kullanmak yerine düzen etkinliğe uygulandıktan sonra getMeasuredWidth / Height kullanın.
bhups

9
Çocuklar, tek yapması gereken çağrı getHeight()ve getWidth()Etkinlik yaşam döngüsü, görüşlerini kendileri ölçmek için sorduktan sonra, başka bir deyişle, bu tür şeyler yapın onResume()ve hepsi bu. Henüz ortaya konmamış bir nesnenin boyutlarını bilmesini beklememelisiniz, tüm işte bu. Aşağıda önerilen sihire gerek yok.
sınıf istifleyici

2
Arayan getHeight()/Width()içinde onResume()benim için> 0 değerini vermemiştir.
Darpan

@ClassStacker Fragment'a ne dersiniz?
zdd

@zdd Lütfen yeni bir soru sorun ve buraya bir yorumda referans gönderin.
sınıf istifleyici

Yanıtlar:


418

OP'nin çoktan gittiğine inanıyorum, ancak bu cevabın gelecekteki araştırmacılara yardımcı olması durumunda, bulduğum bir çözümü yayınlayacağımı düşündüm. Bu kodu benim onCreate()yöntemine ekledim :

DÜZENLENMİŞ: 07/05/11 yorum kodunu eklemek için:

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        LayerDrawable ld = (LayerDrawable)tv.getBackground();
        ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
        ViewTreeObserver obs = tv.getViewTreeObserver();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            obs.removeOnGlobalLayoutListener(this);
        } else {
            obs.removeGlobalOnLayoutListener(this);
        }
    }

});

İlk önce TextView( onGlobalLayout()yöntemde erişmek için ) son bir referans almak . Sonra, ben olsun ViewTreeObserverne biz gelen TextViewve bir ekleme OnGlobalLayoutListener, geçersiz kılma onGLobalLayoutve bu dinleyici içine bakış ölçümleri bilmeyi gerektirir benim kod ekleyerek (... buraya çağırmak için bir üst sınıf yöntemi olarak görünmüyor). Benim için beklendiği gibi tüm işler, umarım bu yardımcı olabilir.


23
@George Bailey: Yakın zamanda bir başkasının yayınını gördüğüm bir şey (kendim test etmedim, bu yüzden YMMV) birden fazla çağrının üstesinden gelmek için (onGlobalLayout () içinde): tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);dinleyici ilk kez sonra kaldırılmalıdır gerçekleştiği.
Kevin Coppock

3
Ben de yardım etmek istiyorum. Bunun gibi bir şey yapmanız gerekebilir: mView.getViewTreeObserver (). RemoveGlobalOnLayoutListener (globalLayoutListener); görünümünüzü yalnızca bir kez yeniden boyutlandırmak istiyorsanız. Umarım bu yardım
Henry Sou

7
Soru çok eski, ama addOnLayoutChangeListeneraynı zamanda işleri de kullanıyor ! :)
FX

7
Ayrıca, API 16'dan bu yana şunlara ihtiyacınız olduğunu unutmayın: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver (). RemoveOnGlobalLayoutListener (this); } else {view.getViewTreeObserver (). removeGlobalOnLayoutListener (bu); }
Edison

2
RemoveGlobalOnLayoutListener kullanımdan kaldırıldığı için Eclipse'de hala uyarı verir. Bu normal mi? Zamanla, kullanımdan kaldırılan kod uyarıları her yere
akacak

82

Sadece alternatif bir çözüm ekleyeceğim, etkinliğinizin onWindowFocusChanged yöntemini geçersiz kılacağım ve oradan getHeight (), getWidth () değerlerini alabileceksiniz.

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}

4
Belgeleri alıntılamak için, "Bu, bu etkinliğin kullanıcı tarafından görülebilir olup olmadığının en iyi göstergesidir."
Cameron Lowell Palmer

6
Kullanmanız gereken Parçalar durumunda bu çalışmaz ViewTreeObserver.
Mihir

OP özellikle bir görünüm için nasıl olsa sordu
JustDanyul

scrollview toplam yükseklik ve max Scrollview.getScrolly () aynı olacak mı? Yani benim durumumda yukarıdaki yöntemle Yükseklik var 702 ve getScrolly () 523 maksimum değeri var,
Hitesh Dhamshaniya

Bu yöntem, Görünümünüz başka bir xml dosyasından eklenirken de çalışmaz.
Jay Donga

19

Henüz çizilmemiş bir elemanın genişliğini ve yüksekliğini elde etmeye çalışıyorsunuz.

Hata ayıklama kullanır ve bir noktada durursanız, cihazınızın ekranının hala boş olduğunu görürsünüz, bunun nedeni öğelerinizin henüz çizilmemesidir, bu yüzden henüz bir şeyin genişliğini ve yüksekliğini elde edemezsiniz. var olmak.

Ve yanlış olabilirim, ama setWidth()her zaman saygı duyulmuyor, Layoutçocuklarını ortaya koyuyor ve nasıl ölçüleceğine karar veriyor (çağırıyor child.measure()), yanisetWidth() , , öğe çizildikten sonra bu genişliği alacağınız garanti edilmez.

İhtiyacınız olan şey kullanmak getMeasuredWidth() , görünüm gerçekten çizildikten sonra bir yerde (Görüşünüzün en son ölçüsü) kullanmaktır.

ActivityEn iyi anı bulmak için yaşam döngüsüne bakın .

http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

İyi bir uygulamanın böyle kullanmak olduğuna inanıyorum OnGlobalLayoutListener:

yourView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (!mMeasured) {
                // Here your view is already layed out and measured for the first time
                mMeasured = true; // Some optional flag to mark, that we already got the sizes
            }
        }
    });

Bu kodu doğrudan yerleştirebilirsiniz onCreate()ve görünümler ne zaman yayınlanacağı çağrılır.


Ne yazık ki OnResume'de de çalışmıyor. Son boyutlar, OnResume çağrısından sonra, en azından taklitçide bir yerde ayarlanır.
jki

Bu korkunç bir uygulamadır !!! Bu dinleyici tekrar tekrar çağrılır ve performansınızı öldürür. Bayrakları kullanmak yerine, dinleyicinin kaydını bir kez tamamlayın.
bluewhile

15

Görünümün yayın yöntemini şu şekilde kullanın

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });

1
Bu işe yarıyor, ama gerçekten, neden tartışılmıyor? Btw, bu çalışır çünkü runnable düzeni gerçekleşinceye kadar çalıştırılmaz.
Norman H

7

Bir onGlobalLayout()özel biçimlendirme yapmak için kullanmaya çalıştım TextView, ancak @George Bailey fark ettiği gibi onGlobalLayout(), gerçekten iki kez çağrılır: bir kez ilk düzen yolunda ve metni değiştirdikten sonra ikinci kez.

View.onSizeChanged()benim için daha iyi çalışır çünkü orada metni değiştirirsem, yöntem sadece bir kez çağrılır (düzen geçişi sırasında). Bu TextView, API Seviye 11+ için alt sınıflandırma gerektirdiView. addOnLayoutChangeListener() alt sınıflandırmayı önlemek için kullanılabilir.

Bir şey daha, bakış doğru genişliğini almak için View.onSizeChanged(), layout_widthayarlanmalıdır match_parentdeğil wrap_content.


2

Bir yapıcıda boyutları veya gerçek resmi almadan ÖNCE çalıştırılan başka bir yöntemi mi almaya çalışıyorsunuz?

Tüm bileşenler gerçekten ölçülmeden hiçbir boyut elde edemezsiniz (xml'niz ekran boyutunuzu, ebeveyn konumlarınızı ve her şeyi bilmiyorsa)

OnSizeChanged () öğesinden sonra değerler almayı deneyin (sıfırla çağırılabilmesine rağmen) veya yalnızca gerçek bir görüntü aldığınızda beklemeyi deneyin.


Orijinal sorum ve kodumu daha net olacak şekilde güncelledim. Teşekkür ederim.

2

FX'in belirttiği gibi, OnLayoutChangeListenerkendisini izlemek istediğiniz görünüme bir

view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        // Make changes
    }
});

Yalnızca ilk düzeni istiyorsanız dinleyiciyi geri aramadan kaldırabilirsiniz.



1

ViewTreeObserver ve onWindowFocusChanged() o kadar da gerekli değil.

Eğer şişirmek olursa TextViewdüzeni gibi ve / veya içine biraz içerik koymak ve set LayoutParamso zaman kullanabilir getMeasuredHeight()ve getMeasuredWidth().

AMA olmak zorunda dikkatliLinearLayouts (belki de diğer ViewGroups). Sorun şu ki, sonra genişliği ve yüksekliği elde edebilirsiniz, onWindowFocusChanged()ancak içine bazı görünümler eklemeye çalışırsanız, her şey çizilene kadar bu bilgileri alamazsınız. Ben çarpımı çalışıyordu TextViewsiçin LinearLayoutsmimik a kadar FlowLayout(sarma tarzı) ve böylece kullanamadı Listeners. İşlem başladıktan sonra senkronize olarak devam etmelidir. Bu durumda, genişliği daha sonra kullanmak için bir değişkente tutmak isteyebilirsiniz, çünkü mizanpaja görünüm eklerken buna ihtiyacınız olabilir.


1

Önerilen çözüm işe yarıyor olsa da, her durum için en iyi çözüm olmayabilir, çünkü ViewTreeObserver.OnGlobalLayoutListener

Genel düzen durumu veya görünüm ağacındaki görünümlerin görünürlüğü değiştiğinde çağrılacak bir geri çağırma için arabirim tanımı.

Bu, birçok kez çağrıldığı ve her zaman görünümün ölçülmediği anlamına gelir (yüksekliği ve genişliği belirlenir)

Bir alternatif, ViewTreeObserver.OnPreDrawListeneryalnızca görünüm çizilmeye hazır olduğunda ve tüm ölçümlerine sahip olduğunda çağrılan kullanımı kullanmaktır .

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {

    @Override
    public void onPreDraw() {
        tv.getViewTreeObserver().removeOnPreDrawListener(this);
        // Your view will have valid height and width at this point
        tv.getHeight();
        tv.getWidth();
    }

});


0

OnResume () içinde çağrılan bir yayın kullanabilirsiniz

Örneğin:

int vh = 0;   
int vw = 0;

public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.maindemo);  //<- includes the grid called "board"

      registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                TableLayout tl = (TableLayout) findViewById(R.id.board);  
                tl = (TableLayout) findViewById(R.id.board);
                vh = tl.getHeight();       
                vw = tl.getWidth();               
            }
      }, new IntentFilter("Test"));
}

protected void onResume() {
        super.onResume();

        Intent it = new Intent("Test");
        sendBroadcast(it);

}

Kcoppock'un yanıt vermesinin nedeni olarak OnCreate (), onStart () veya hatta onResume () öğelerinde görünümün yüksekliğini alamıyorsunuz


0

Yükseklik ve genişlik sıfırdır, çünkü görünüm yükseklik ve genişlik istediğiniz zamana göre oluşturulmamıştır. En basit çözümlerden biri

view.post(new Runnable() {
    @Override
    public void run() {
        view.getHeight(); //height is ready
        view.getWidth(); //width is ready
    }
});

Bu yöntem, kısa ve net olduğu için diğer yöntemlere kıyasla iyidir.


-1

Basit Yanıt: Bu benim için sorunsuz çalıştı. Görünüşe göre anahtar, getHeight vb. Önce Görünümün odaklanmasını sağlamaktır. Bunu hasFocus () yöntemini kullanarak, ardından bu sırayla getHeight () yöntemini kullanarak yapın. Sadece 3 satır kod gerekir.

ImageButton myImageButton1 =(ImageButton)findViewById(R.id.imageButton1); myImageButton1.hasFocus();

int myButtonHeight = myImageButton1.getHeight();

Log.d("Button Height: ", ""+myButtonHeight );//Not required

Umarım yardımcı olur.


-3

Görünümünüz için getMeasuredWidth () ve getMeasuredHeight () öğelerini kullanın.

Geliştirici kılavuzu: Görüntüle


4
Daha önce belirtildiği gibi, bu yöntemler ancak boyut ölçüldükten sonra geçerli bir sonuç verir . Bir Etkinlik içinde bu, WindowsFocusChanged os üzerindeki yöntem çağrıldığında geçerlidir.
slott

-8

DÜZELTME: Yukarıdaki çözümün korkunç olduğunu öğrendim. Özellikle telefonunuz yavaş olduğunda. Ve burada başka bir çözüm buldum: kenar boşlukları ve dolguları da dahil olmak üzere öğenin px değerini hesaplayın: dp to px: https://stackoverflow.com/a/6327095/1982712

veya dimens.xml'yi piksel boyutuna kadar: https://stackoverflow.com/a/16276351/1982712

sp'den px'ye: https://stackoverflow.com/a/9219417/1982712 (çözümü tersine çevir)

veya piksele küçülür: https://stackoverflow.com/a/16276351/1982712

ve bu kadar.


10
Rastgele seçilen milisaniye sayısından sonra her şeyin doğru şekilde ayarlanmasını ummak genellikle kötü bir fikirdir. Aygıt yavaş olabilir, diğer işlemler / iş parçacıkları çalışıyor olabilir, hata ayıklayıcıda çalışmayabilir, işletim sisteminin bir sonraki sürümünde çalışmayabilir, ...
Kristopher Johnson
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.