wake-up-neo.com

GetWidth () und GetHeight () von View werden zurückgegeben

Ich erstelle alle Elemente in meinem Android-Projekt dynamisch. Ich versuche, die Breite und Höhe einer Schaltfläche zu ermitteln, damit ich sie drehen kann. Ich versuche gerade zu lernen, wie man mit der Android-Sprache arbeitet. Es wird jedoch 0 zurückgegeben. 

Ich habe etwas recherchiert und sah, dass es anders als in der onCreate()-Methode gemacht werden muss. Wenn mir jemand ein Beispiel geben kann, wie das geht, wäre das großartig. 

Hier ist mein aktueller Code:

package com.animation;

import Android.app.Activity;
import Android.os.Bundle;
import Android.view.animation.Animation;
import Android.view.animation.LinearInterpolator;
import Android.view.animation.RotateAnimation;
import Android.widget.Button;
import Android.widget.LinearLayout;

public class AnimateScreen extends Activity {


//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LinearLayout ll = new LinearLayout(this);

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(30, 20, 30, 0);

    Button bt = new Button(this);
    bt.setText(String.valueOf(bt.getWidth()));

    RotateAnimation ra = new RotateAnimation(0,360,bt.getWidth() / 2,bt.getHeight() / 2);
    ra.setDuration(3000L);
    ra.setRepeatMode(Animation.RESTART);
    ra.setRepeatCount(Animation.INFINITE);
    ra.setInterpolator(new LinearInterpolator());

    bt.startAnimation(ra);

    ll.addView(bt,layoutParams);

    setContentView(ll);
}

Jede Hilfe wird geschätzt.

430
ngreenwood6

Sie rufen getWidth() zu früh an. Die Benutzeroberfläche wurde noch nicht auf dem Bildschirm angezeigt.

Ich bezweifle, dass Sie das tun möchten, was Sie gerade tun. Die animierten Widgets ändern ihre klickbaren Bereiche nicht. Daher reagiert die Schaltfläche auf Klicks in der ursprünglichen Ausrichtung, unabhängig davon, wie sie gedreht wurde.

Sie können jedoch eine dimension-Ressource verwenden, um die Tastengröße zu definieren, und dann auf diese Dimensionsressource in Ihrer Layoutdatei und in Ihrem Quellcode verweisen, um dieses Problem zu vermeiden.

174
CommonsWare

Das Grundproblem ist, dass Sie auf die Zeichnungsphase für die eigentlichen Messungen warten müssen (insbesondere bei dynamischen Werten wie wrap_content Oder match_parent), Aber normalerweise ist diese Phase noch nicht abgeschlossen onResume(). Sie benötigen also eine Problemumgehung, um auf diese Phase zu warten. Hierfür gibt es verschiedene Lösungsmöglichkeiten:


1. Hören Sie sich Draw/Layout Events an: ViewTreeObserver

Ein ViewTreeObserver wird für verschiedene Zeichenereignisse ausgelöst. Normalerweise ist OnGlobalLayoutListener das, was Sie zum Abrufen der Messung benötigen. Daher wird der Code im Listener nach der Layout-Phase aufgerufen, sodass die Messungen fertig sind:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                view.getHeight(); //height is ready
            }
        });

Hinweis: Der Listener wird sofort entfernt, da er sonst bei jedem Layoutereignis ausgelöst wird. Wenn Sie Apps unterstützen müssen , verwenden Sie SDK Lvl <16 , um die Registrierung des Listeners aufzuheben:

public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)


2. Fügen Sie der Layout-Warteschlange eine ausführbare Datei hinzu: View.post ()

Nicht sehr bekannt und meine Lieblingslösung. Verwenden Sie einfach die Post-Methode von View mit Ihrem eigenen Runnable. Dies reiht Ihren Code nach dem Maß, dem Layout usw. der Ansicht in eine Warteschlange, wie von Romain Guy angegeben:

Die Benutzeroberflächen-Ereigniswarteschlange verarbeitet Ereignisse der Reihe nach. Nachdem setContentView () aufgerufen wurde, enthält die Ereigniswarteschlange eine Nachricht, in der Sie nach einem Relayout gefragt werden. Alles, was Sie in die Warteschlange stellen, wird also nach dem Layoutdurchlauf ausgeführt

Beispiel:

final View view=//smth;
...
view.post(new Runnable() {
            @Override
            public void run() {
                view.getHeight(); //height is ready
            }
        });

Der Vorteil gegenüber ViewTreeObserver:

  • ihr Code wird nur einmal ausgeführt und Sie müssen den Observer nach der Ausführung nicht deaktivieren, was ein Problem sein kann
  • weniger ausführliche Syntax

Verweise:


3. Überschreiben Sie die onLayout-Methode von Views

Dies ist nur in bestimmten Situationen sinnvoll, wenn die Logik in der Ansicht selbst gekapselt werden kann. Andernfalls ist dies eine recht ausführliche und umständliche Syntax.

view = new View(this) {
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        view.getHeight(); //height is ready
    }
};

Denken Sie auch daran, dass onLayout viele Male aufgerufen wird. Überlegen Sie also, was Sie in der Methode tun, oder deaktivieren Sie Ihren Code nach dem ersten Mal


4. Überprüfen Sie, ob die Layoutphase durchlaufen wurde

Wenn Sie Code haben, der beim Erstellen der Benutzeroberfläche mehrmals ausgeführt wird, können Sie die folgende Methode zur Unterstützung von v4 lib verwenden:

View viewYouNeedHeightFrom = ...
...
if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) {
   viewYouNeedHeightFrom.getHeight();
}

Gibt true zurück, wenn die Ansicht mindestens ein Layout durchlaufen hat, seit es zuletzt an ein Fenster angehängt oder von diesem getrennt wurde.


Zusätzlich: Statisch definierte Messungen abrufen

Wenn es ausreicht, nur die statisch definierte Höhe/Breite zu erhalten, können Sie dies einfach tun mit:

Beachten Sie jedoch, dass dies möglicherweise von der tatsächlichen Breite/Höhe nach dem Zeichnen abweicht. Der Javadoc beschreibt den Unterschied perfekt:

Die Größe einer Ansicht wird durch eine Breite und eine Höhe ausgedrückt. Eine Ansicht besitzt tatsächlich zwei Paare von Breiten- und Höhenwerten.

Das erste Paar ist als gemessene Breite und gemessene Höhe bekannt. Diese Dimensionen definieren, wie groß eine Ansicht in ihrem übergeordneten Element sein soll (weitere Informationen finden Sie unter Layout.) Die gemessenen Dimensionen können durch Aufrufen von getMeasuredWidth () und getMeasuredHeight () abgerufen werden.

Das zweite Paar ist einfach als Breite und Höhe oder manchmal als Zeichenbreite und Zeichenhöhe bekannt. Diese Abmessungen definieren die tatsächliche Größe der Ansicht auf dem Bildschirm zum Zeitpunkt des Zeichnens und nach dem Layout. Diese Werte können, müssen aber nicht von der gemessenen Breite und Höhe abweichen. Die Breite und Höhe können durch Aufrufen von getWidth () und getHeight () ermittelt werden.

734
patrickf

Wir können benutzen 

@Override
 public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);
  //Here you can get the size!
 }
256
Sana

Ich habe diese Lösung verwendet, die meiner Meinung nach besser ist als onWindowFocusChanged (). Wenn Sie ein DialogFragment öffnen und dann das Telefon drehen, wird onWindowFocusChanged nur aufgerufen, wenn der Benutzer das Dialogfeld schließt.

    yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once :
            yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

            // Here you can get the size :)
        }
    });

Edit: da removeGlobalOnLayoutListener veraltet ist, sollten Sie jetzt Folgendes tun:

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {

    // Ensure you call it only once :
    if(Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.JELLY_BEAN) {
        yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    else {
        yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    // Here you can get the size :)
}
103
Tim Autin

Wie Ian in diesem Android-Entwickler-Thread angibt:

Wie auch immer, der Deal ist das Layout der Der Inhalt eines Fensters geschieht after Alle Elemente werden konstruiert und ihrem übergeordneten Element hinzugefügt Ansichten. Es muss so sein, weil bis Sie wissen, welche Komponenten eine View enthält und was sie enthalten, und So weiter, es gibt keinen vernünftigen Weg. lege es aus.

Fazit, wenn Sie getWidth () .__ aufrufen. usw. in einem Konstruktor wird es zurückgegeben Null. Das Verfahren besteht darin, alle .__ zu erstellen. Ihre Ansichtselemente im Konstruktor Warten Sie dann auf Ihre View onSizeChanged () - Methode, die aufgerufen werden soll -- das ist, wenn Sie zuerst Ihr .__ herausfinden. echte Größe, so dass Sie die Größen Ihrer GUI-Elemente.

Beachten Sie auch, dass onSizeChanged () .__ ist. wird manchmal mit Parametern von .__ aufgerufen. Null - für diesen Fall prüfen und kehren Sie sofort zurück (Sie erhalten also keine Division durch Null, wenn Sie Ihr Layout berechnen). Einige Zeit später ist es wird mit den realen Werten aufgerufen.

75
kerem yokuva

Wenn Sie die Breite eines Widgets abrufen müssen, bevor es auf dem Bildschirm angezeigt wird, können Sie getMeasuredWidth () oder getMeasuredHeight () verwenden.

myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int width = myImage.getMeasuredWidth();
int height = myImage.getMeasuredHeight();
58
sulu.dev

Ich würde lieber OnPreDrawListener() anstelle von addOnGlobalLayoutListener() verwenden, da es früher aufgerufen wird. 

    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
    {
        @Override
        public boolean onPreDraw()
        {
            if (view.getViewTreeObserver().isAlive())
                view.getViewTreeObserver().removeOnPreDrawListener(this);

            // put your code here
            return true;
        }
    });
14
Sa Qada

Erweiterung um dynamisch Höhe in Kotlin zu bekommen.

Verwendungszweck:

view.height { Log.i("Info", "Here is your height:" + it) }

Implementierung:

fun <T : View> T.height(function: (Int) -> Unit) {
    if (height == 0)
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                function(height)
            }
        })
    else function(height)
}
4
Renetik

Ein Liner, wenn Sie RxJava & RxBindings verwenden. Ähnliches Vorgehen ohne Boilerplate. Dies löst auch den Hack, um Warnungen wie in der Antwort von Tim Autin zu unterdrücken.

RxView.layoutChanges(yourView).take(1)
      .subscribe(aVoid -> {
           // width and height have been calculated here
      });

Das ist es. Sie müssen sich nicht abmelden, selbst wenn Sie nie angerufen werden.

3
Odys

Vielleicht hilft das jemandem:

Erstellen Sie eine Erweiterungsfunktion für die View-Klasse

dateiname: ViewExt.kt

fun View.afterLayout(what: () -> Unit) {
    if(isLaidOut) {
        what.invoke()
    } else {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                what.invoke()
            }
        })
    }
}

Dies kann dann für jede Ansicht verwendet werden mit:

view.afterLayout {
    do something with view.height
}
1
Pepijn

Wenn Sie Kotlin verwenden

  leftPanel.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {

            override fun onGlobalLayout() {

                if(Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.JELLY_BEAN) {
                    leftPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
                }
                else {
                    leftPanel.viewTreeObserver.removeGlobalOnLayoutListener(this)
                }

                // Here you can get the size :)
                leftThreshold = leftPanel.width
            }
        })
1
Hitesh Sahu

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
Shivam Yadav

Gone Views geben 0 als Höhe zurück, wenn sich die App im Hintergrund befindet .. Dieser Code funktioniert (1oo%).

fun View.postWithTreeObserver(postJob: (View, Int, Int) -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            val widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            measure(widthSpec, heightSpec)
            postJob([email protected], measuredWidth, measuredHeight)
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                @Suppress("DEPRECATION")
                viewTreeObserver.removeGlobalOnLayoutListener(this)
            } else {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

Wir müssen warten, bis die Ansicht gezogen wird. Verwenden Sie dazu den OnPreDrawListener. Beispiel Kotlin:

val preDrawListener = object : ViewTreeObserver.OnPreDrawListener {

                override fun onPreDraw(): Boolean {
                    view.viewTreeObserver.removeOnPreDrawListener(this)

                    // code which requires view size parameters

                    return true
                }
            }

            view.viewTreeObserver.addOnPreDrawListener(preDrawListener)
0
Artem Botnev