Wie rufe ich die Abmessungen einer Ansicht ab?


209

Ich habe eine Ansicht bestehend aus TableLayout, TableRow and TextView. Ich möchte, dass es wie ein Gitter aussieht. Ich muss die Höhe und Breite dieses Gitters ermitteln. Die Methoden getHeight()und getWidth()Rückkehr immer 0. Dies geschieht , wenn ich das Netz dynamisch formatieren und auch wenn ich eine XML - Version verwenden.

Wie rufe ich die Dimensionen für eine Ansicht ab?


Hier ist mein Testprogramm, mit dem ich in Debug die Ergebnisse überprüft habe:

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

Verwenden Sie statt getWidth / Height getMeasuredWidth / Height, nachdem das Layout auf die Aktivität angewendet wurde.
Bhups

9
Leute, alles, was man tun muss, ist anzurufen getHeight()und getWidth()nachdem der Aktivitätslebenszyklus die Ansichten gebeten hat, sich selbst zu messen, mit anderen Worten, machen Sie solche Dinge onResume()und das war's. Sie sollten nicht erwarten, dass ein noch nicht angelegtes Objekt seine Dimensionen kennt, das ist der ganze Trick. Keine Notwendigkeit für die unten vorgeschlagene Magie.
Klassenstapler

2
Der Aufruf getHeight()/Width()in onResume()brachte nicht> 0 Wert für mich.
Darpan

@ClassStacker Was ist mit Fragment?
zdd

@zdd Bitte stellen Sie eine komplett neue Frage und posten Sie hier eine Referenz in einem Kommentar.
Klassenstapler

Antworten:


418

Ich glaube, das OP ist längst vorbei, aber falls diese Antwort zukünftigen Suchenden helfen kann, dachte ich, ich würde eine Lösung veröffentlichen, die ich gefunden habe. Ich habe diesen Code zu meiner onCreate()Methode hinzugefügt :

BEARBEITET: 07/05/11, um Code aus Kommentaren aufzunehmen:

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

});

Zuerst bekomme ich einen endgültigen Verweis auf meine TextView(um in der onGlobalLayout()Methode zuzugreifen ). Als nächstes erhalte ich das ViewTreeObservervon meinem TextViewund füge ein OnGlobalLayoutListenerÜberschreiben hinzu onGLobalLayout(es scheint hier keine Methode der Oberklasse zu geben ...) und füge meinen Code hinzu, der die Kenntnis der Maße der Ansicht in diesen Listener erfordert. Alles funktioniert wie erwartet für mich, also hoffe ich, dass dies helfen kann.


23
@ George Bailey: Eine Sache, die ich kürzlich von einem anderen gesehen habe (ich habe es nicht selbst getestet, also YMMV), um die mehrfachen Anrufe zu überwinden, war (innerhalb von onGlobalLayout ()): Auf tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);diese Weise sollte der Listener nach dem ersten Mal entfernt werden es passiert.
Kevin Coppock

3
Ich möchte auch zurück helfen. Möglicherweise müssen Sie Folgendes tun: mView.getViewTreeObserver (). RemoveGlobalOnLayoutListener (globalLayoutListener); Wenn Sie die Größe Ihrer Ansicht nur einmal ändern möchten. Hoffe diese Hilfe
Henry Sou

7
Die Frage ist sehr alt, aber die Verwendung addOnLayoutChangeListenerfunktioniert auch! :)
FX

7
Beachten Sie außerdem, dass Sie seit API 16 Folgendes benötigen: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver (). RemoveOnGlobalLayoutListener (this); } else {view.getViewTreeObserver (). removeGlobalOnLayoutListener (this); }
Edison

2
Da removeGlobalOnLayoutListener veraltet ist, werden in Eclipse weiterhin Warnungen ausgegeben. Ist das normal? Mit der Zeit würden veraltete Code-Warnungen überall überfluten ...
Jonny

82

Ich füge nur eine alternative Lösung hinzu, überschreibe die onWindowFocusChanged-Methode Ihrer Aktivität und Sie können die Werte von getHeight (), getWidth () von dort abrufen.

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

4
Um die Dokumentation zu zitieren: "Dies ist der beste Indikator dafür, ob diese Aktivität für den Benutzer sichtbar ist."
Cameron Lowell Palmer

6
Dies funktioniert nicht bei Fragmenten, die Sie verwenden müssen ViewTreeObserver.
Mihir

OP fragte speziell, wie man es für eine Ansicht
bekommt

scrollview Gesamthöhe und max Scrollview.getScrolly () sind gleich? dh in meinem Fall habe ich Höhe mit der oben genannten Methode ist 702 und ich habe den maximalen Wert von getScrolly () ist 523,
Hitesh Dhamshaniya

Diese Methode funktioniert auch nicht, wenn Ihre Ansicht aus einer anderen XML-Datei aufgenommen wird.
Jay Donga

19

Sie versuchen, die Breite und Höhe eines Elements zu ermitteln, das noch nicht gezeichnet wurde.

Wenn Sie Debug verwenden und irgendwann anhalten, werden Sie feststellen, dass der Bildschirm Ihres Geräts noch leer ist. Dies liegt daran, dass Ihre Elemente noch nicht gezeichnet wurden, sodass Sie die Breite und Höhe von etwas noch nicht ermitteln können, was noch nicht der Fall ist existieren.

Und ich könnte mich irren, werde aber setWidth()nicht immer respektiert, Layoutlegt seine Kinder fest und entscheidet, wie sie gemessen werden (Aufruf child.measure()). Wenn Sie also festlegen setWidth(), wird nicht garantiert, dass Sie diese Breite erhalten, nachdem das Element gezeichnet wurde.

Was Sie brauchen, ist, getMeasuredWidth()(das neueste Maß Ihrer Ansicht) irgendwo zu verwenden, nachdem die Ansicht tatsächlich gezeichnet wurde.

Schauen Sie in den ActivityLebenszyklus, um den besten Moment zu finden.

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

Ich glaube, eine gute Praxis ist es, Folgendes zu verwenden 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
            }
        }
    });

Sie können diesen Code direkt in platzieren onCreate()und er wird aufgerufen, wenn Ansichten angelegt werden.


Leider funktioniert es auch in OnResume nicht. Die endgültigen Abmessungen werden irgendwo nach dem OnResume-Aufruf festgelegt, zumindest im Emulator.
Jki

Das ist eine schreckliche Praxis !!! Dieser Zuhörer wird immer wieder gerufen und tötet Ihre Leistung. Anstatt Flags zu verwenden, heben Sie die Registrierung des Listeners auf, sobald Sie fertig sind.
Bluewhile

15

Verwenden Sie die Post-Methode der Ansicht wie folgt

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

1
Das funktioniert, aber wirklich keine Diskussion darüber, warum? Übrigens funktioniert dies, weil die ausführbare Datei erst ausgeführt wird, wenn das Layout erstellt wurde.
Norman H

7

Ich habe versucht, onGlobalLayout()eine benutzerdefinierte Formatierung von a TextViewvorzunehmen, aber wie @George Bailey bemerkte, onGlobalLayout()wird sie tatsächlich zweimal aufgerufen: einmal auf dem anfänglichen Layoutpfad und ein zweites Mal nach dem Ändern des Textes.

View.onSizeChanged()funktioniert bei mir besser, denn wenn ich den Text dort ändere, wird die Methode nur einmal aufgerufen (während des Layoutdurchlaufs). Dies erforderte eine Unterklassifizierung von TextView, kann jedoch auf API-Ebene 11+ View. addOnLayoutChangeListener()verwendet werden, um eine Unterklassifizierung zu vermeiden.

Eine weitere Sache, um die richtige Breite der Ansicht zu erhalten View.onSizeChanged(), layout_widthsollte auf gesetzt werden match_parent, nicht wrap_content.


2

Versuchen Sie, Größen in einem Konstruktor oder einer anderen Methode abzurufen, die ausgeführt wird, bevor Sie das tatsächliche Bild erhalten?

Sie erhalten keine Abmessungen, bevor alle Komponenten tatsächlich gemessen wurden (da Ihre XML-Datei nichts über Ihre Anzeigegröße, die Positionen der Eltern und was auch immer weiß).

Versuchen Sie, Werte nach onSizeChanged () abzurufen (obwohl dies mit Null aufgerufen werden kann), oder warten Sie einfach, bis Sie ein tatsächliches Bild erhalten.


Ich habe meine ursprüngliche Frage und meinen Code aktualisiert, um klarer zu sein. Danke dir.

2

Wie bereits erwähnt, können Sie eine OnLayoutChangeListenerAnsicht verwenden, die Sie selbst verfolgen möchten

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

Sie können den Listener im Rückruf entfernen, wenn Sie nur das ursprüngliche Layout möchten.



1

ViewTreeObserverund onWindowFocusChanged()sind überhaupt nicht so notwendig.

Wenn Sie das TextViewas-Layout aufblasen und / oder Inhalte einfügen und festlegen LayoutParams, können Sie getMeasuredHeight()und verwenden getMeasuredWidth().

ABER du musst vorsichtig sein mitLinearLayouts (vielleicht auch anderen ViewGroups). Das Problem dabei ist, dass Sie die Breite und Höhe danach onWindowFocusChanged()abrufen können. Wenn Sie jedoch versuchen, einige Ansichten hinzuzufügen, können Sie diese Informationen erst abrufen, wenn alles gezeichnet wurde. Ich habe versucht , mehr hinzufügen TextViewszu LinearLayoutsimitieren eine FlowLayout(Umbruchart) und konnte so nicht verwenden Listeners. Sobald der Prozess gestartet ist, sollte er synchron fortgesetzt werden. In diesem Fall möchten Sie möglicherweise die Breite in einer Variablen behalten, um sie später verwenden zu können, da Sie sie möglicherweise beim Hinzufügen von Ansichten zum Layout benötigen.


1

Obwohl die vorgeschlagene Lösung funktioniert, ist sie möglicherweise nicht für jeden Fall die beste Lösung, da sie auf der Dokumentation für basiert ViewTreeObserver.OnGlobalLayoutListener

Schnittstellendefinition für einen Rückruf, der aufgerufen werden soll, wenn sich der globale Layoutstatus oder die Sichtbarkeit von Ansichten im Ansichtsbaum ändert.

Das heißt, es wird oft aufgerufen und nicht immer wird die Ansicht gemessen (Höhe und Breite werden bestimmt).

Eine Alternative ist die Verwendung, ViewTreeObserver.OnPreDrawListenerdie nur aufgerufen wird, wenn die Ansicht zum Zeichnen bereit ist und alle ihre Maße hat.

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

Sie können eine Sendung verwenden, die in OnResume () aufgerufen wird.

Beispielsweise:

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

}

Sie können die Höhe einer Ansicht in OnCreate (), onStart () oder sogar in onResume () nicht abrufen, da kcoppock geantwortet hat


0

Höhe und Breite sind Null, da die Ansicht zum Zeitpunkt der Anforderung von Höhe und Breite noch nicht erstellt wurde. Eine einfachste Lösung ist

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

Diese Methode ist im Vergleich zu anderen Methoden gut, da sie kurz und knackig ist.


-1

Einfache Antwort: Dies funktionierte bei mir ohne Probleme. Es scheint, dass der Schlüssel darin besteht, sicherzustellen, dass die Ansicht den Fokus hat, bevor Sie getHeight usw. verwenden. Verwenden Sie dazu die Methode hasFocus () und dann die Methode getHeight () in dieser Reihenfolge. Nur 3 Codezeilen erforderlich.

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

int myButtonHeight = myImageButton1.getHeight();

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

Ich hoffe es hilft.


-3

Verwenden Sie getMeasuredWidth () und getMeasuredHeight () für Ihre Ansicht.

Entwicklerhandbuch: Anzeigen


4
Nach wie vor diesen Verfahren angegeben nur ein gültiges Ergebnis zurückgeben efter die Größe meassured wurde. Innerhalb einer Aktivität gilt dies, wenn die Methode onWindowFocusChanged os aufgerufen wird.
Slott

-8

KORREKTUR: Ich habe herausgefunden, dass die obige Lösung schrecklich ist. Besonders wenn Ihr Telefon langsam ist. Und hier habe ich eine andere Lösung gefunden: Berechnen Sie den px-Wert des Elements, einschließlich der Ränder und Auffüllungen: dp bis px: https://stackoverflow.com/a/6327095/1982712

oder dimension.xml bis px: https://stackoverflow.com/a/16276351/1982712

sp to px: https://stackoverflow.com/a/9219417/1982712 (Lösung umkehren)

oder dimension to px: https://stackoverflow.com/a/16276351/1982712

und das ist es.


10
Es ist normalerweise eine schlechte Idee zu hoffen, dass nach einer willkürlich gewählten Anzahl von Millisekunden alles richtig eingestellt wird. Gerät ist möglicherweise langsam, andere Prozesse / Threads werden möglicherweise ausgeführt, funktionieren möglicherweise nicht im Debugger, funktionieren möglicherweise nicht in der nächsten Version des Betriebssystems, ...
Kristopher Johnson
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.