Come recuperare le dimensioni di una vista?


209

Ho una visione composta da TableLayout, TableRow and TextView . Voglio che sembri una griglia. Ho bisogno di ottenere l'altezza e la larghezza di questa griglia. I metodi getHeight()egetWidth() restituiscono sempre 0. Questo accade quando formatto la griglia in modo dinamico e anche quando utilizzo una versione XML.

Come recuperare le dimensioni per una vista?


Ecco il mio programma di test che ho usato in Debug per verificare i risultati:

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

invece di usare getWidth / Height usare getMeasuredWidth / Height dopo che il layout è stato applicato all'attività.
bhups,

9
Ragazzi, tutto ciò che si deve fare è chiamare getHeight()e getWidth()dopo che il ciclo di vita dell'Attività ha chiesto ai punti di vista di misurarsi, in altre parole, fare questo tipo di cose onResume()e basta. Non dovresti aspettarti che un oggetto non ancora disposto conosca le sue dimensioni, questo è il trucco. Non c'è bisogno della magia suggerita di seguito.
impilatore di classe

2
Chiamata getHeight()/Width()a onResume()non cedere> 0 il valore per me.
Darpan,

@ClassStacker Che dire di Fragment?
zdd

@zdd Per favore, fai una nuova domanda completa e pubblica un riferimento qui in un commento.
impilatore di classe

Risposte:


418

Credo che l'OP sia sparito da tempo, ma nel caso in cui questa risposta possa aiutare i futuri ricercatori, ho pensato di pubblicare una soluzione che ho trovato. Ho aggiunto questo codice nel mio onCreate()metodo:

MODIFICATO: 07/05/11 per includere il codice dai commenti:

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

});

Per prima cosa ottengo un riferimento finale al mio TextView(per accedere al onGlobalLayout()metodo). Successivamente, ottengo il ViewTreeObserverdal mio TextViewe aggiungo un OnGlobalLayoutListeneroverride onGLobalLayout(non sembra esserci un metodo superclasse da invocare qui ...) e aggiungendo il mio codice che richiede di conoscere le misure della vista in questo listener. Tutto funziona come previsto per me, quindi spero che questo possa aiutare.


23
@George Bailey: Una cosa che ho visto recentemente qualcun altro pubblicare (non l'ho provato da solo, quindi YMMV) per superare le chiamate multiple è stata (all'interno di onGlobalLayout ()): in tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);questo modo l'ascoltatore dovrebbe essere rimosso dopo la prima volta capita.
Kevin Coppock,

3
Voglio aiutare anche io. Potrebbe essere necessario fare qualcosa del tipo: mView.getViewTreeObserver (). RemoveGlobalOnLayoutListener (globalLayoutListener); se desideri ridimensionare la visualizzazione una sola volta. Spero che questo aiuto
Henry Sou,

7
La domanda è molto vecchia, ma anche usare addOnLayoutChangeListenerfunziona! :)
FX

7
Inoltre, nota che dall'API 16 hai bisogno di: if (android.os.Build.VERSION.SDK_INT> = 16) {view.getViewTreeObserver (). RemoveOnGlobalLayoutListener (this); } else {view.getViewTreeObserver (). removeGlobalOnLayoutListener (this); }
Edison,

2
Poiché removeGlobalOnLayoutListener è obsoleto, fornisce comunque avvisi in Eclipse. È normale? Con il tempo, gli avvertimenti sui codici deprecati si inonderebbero dappertutto ...
Jonny il

82

Aggiungerò solo una soluzione alternativa, sovrascriverò il metodo onWindowFocusChanged della tua attività e sarai in grado di ottenere i valori di getHeight (), getWidth () da lì.

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

4
Per citare la documentazione, "Questo è il miglior indicatore del fatto che questa attività sia visibile all'utente".
Cameron Lowell Palmer l'

6
Questo non funziona in caso di frammenti che devi usare ViewTreeObserver.
Mihir,

OP ha chiesto in particolare come ottenerlo per una vista
JustDanyul il

scrollview altezza totale e max Scrollview.getScrolly () saranno uguali? cioè nel mio caso ho ottenuto Height con il metodo sopra è 702 e ho ottenuto il valore massimo da getScrolly () è 523,
Hitesh Dhamshaniya il

Questo metodo inoltre non funziona quando la tua vista viene inclusa da un altro file XML.
Jay Donga,

19

Stai cercando di ottenere la larghezza e l'altezza di un elemento, che non sono stati ancora disegnati.

Se usi il debug e ti fermi ad un certo punto, vedrai che lo schermo del tuo dispositivo è ancora vuoto, questo perché i tuoi elementi non sono stati ancora disegnati, quindi non puoi ottenere la larghezza e l'altezza di qualcosa, che non lo fanno ancora esistere.

E, potrei sbagliarmi, ma setWidth()non è sempre rispettato, Layoutespone i suoi figli e decide come misurarli (chiamata child.measure()), quindi se imposti setWidth(), non avrai la garanzia di ottenere questa larghezza dopo che l'elemento verrà disegnato.

Ciò di cui hai bisogno è usare getMeasuredWidth()(la misura più recente della tua vista) da qualche parte dopo che la vista è stata effettivamente disegnata.

Cerca nel Activityciclo di vita per trovare il momento migliore.

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

Credo che una buona pratica sia usare in OnGlobalLayoutListenerquesto modo:

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

È possibile inserire direttamente questo codice onCreate()e verrà invocato quando verranno visualizzate le viste.


Sfortunatamente non funziona anche su OnResume. Le dimensioni finali vengono impostate da qualche parte dopo la chiamata di OnResume, almeno nell'emulatore.
jki,

È una pratica orribile !!! Questo ascoltatore verrà chiamato più volte e ucciderà la tua performance. Invece di usare i flag, annulla la registrazione dell'ascoltatore una volta fatto.
Blueanto

15

Utilizzare il metodo di post della vista in questo modo

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

1
Funziona, ma davvero, nessuna discussione sul perché? A proposito, questo funziona perché il eseguibile non viene eseguito fino a quando non si è verificato il layout.
Norman H,

7

Ho provato a usare onGlobalLayout()una formattazione personalizzata di a TextView, ma come notato da @George Bailey, onGlobalLayout()viene infatti chiamato due volte: una volta sul percorso di layout iniziale e una seconda volta dopo aver modificato il testo.

View.onSizeChanged()funziona meglio per me perché se modifico il testo lì, il metodo viene chiamato solo una volta (durante il passaggio del layout). Ciò richiedeva una sottoclasse di TextView, ma a livello API 11+ View. addOnLayoutChangeListener()può essere usato per evitare la sottoclasse.

Un'altra cosa, al fine di ottenere la larghezza corretta della vista View.onSizeChanged(), layout_widthdovrebbe essere impostato su match_parentno wrap_content.


2

Stai cercando di ottenere dimensioni in un costruttore o qualsiasi altro metodo che viene eseguito PRIMA di ottenere l'immagine reale?

Non otterrai alcuna dimensione prima che tutti i componenti siano effettivamente misurati (poiché il tuo xml non è a conoscenza delle dimensioni del display, delle posizioni dei genitori e quant'altro)

Prova a ottenere valori dopo onSizeChanged () (anche se può essere chiamato con zero) o semplicemente aspettando quando otterrai un'immagine reale.


Ho aggiornato la mia domanda e il mio codice originali per essere più chiari. Grazie.

2

Come accennato FX, è possibile utilizzare un OnLayoutChangeListenerper la vista che si desidera tracciare se stesso

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

È possibile rimuovere il listener nel callback se si desidera solo il layout iniziale.



1

ViewTreeObserver e onWindowFocusChanged() non sono affatto necessari.

Se si gonfia il TextViewlayout come e / o si inserisce del contenuto e lo si imposta, LayoutParamsè possibile utilizzare getMeasuredHeight()egetMeasuredWidth() .

MA devi stare attentoLinearLayouts (forse anche altro ViewGroups). Il problema è che è possibile ottenere la larghezza e l'altezza dopo, onWindowFocusChanged()ma se si tenta di aggiungere alcune viste al suo interno, non è possibile ottenere tali informazioni fino a quando non è stato disegnato tutto. Stavo cercando di aggiungere più TextViewsa LinearLayoutsper imitare uno FlowLayout(stile di avvolgimento) e quindi non potevo usarlo Listeners. Una volta avviato, il processo dovrebbe continuare in modo sincrono. Quindi, in tal caso, potresti voler mantenere la larghezza in una variabile per usarla in un secondo momento, poiché durante l'aggiunta di viste al layout, potresti averne bisogno.


1

Anche se la soluzione proposta funziona, potrebbe non essere la soluzione migliore per ogni caso perché basata sulla documentazione per ViewTreeObserver.OnGlobalLayoutListener

Definizione dell'interfaccia per un callback da invocare quando lo stato del layout globale o la visibilità delle viste nell'albero della vista cambia.

il che significa che viene chiamato molte volte e non sempre viene misurata la vista (ha la sua altezza e larghezza determinate)

Un'alternativa è l'uso ViewTreeObserver.OnPreDrawListenerche viene chiamato solo quando la vista è pronta per essere disegnata e ha tutte le sue misure.

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

È possibile utilizzare una trasmissione chiamata in OnResume ()

Per esempio:

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

}

Non è possibile ottenere l'altezza di una vista in OnCreate (), onStart () o anche in onResume () per il motivo per cui kcoppock ha risposto


0

L'altezza e la larghezza sono zero perché la vista non è stata creata al momento della richiesta di altezza e larghezza. Una soluzione più semplice è

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

Questo metodo è buono rispetto ad altri metodi in quanto è breve e nitido.


-1

Risposta semplice: questo ha funzionato per me senza problemi. Sembra che la chiave sia assicurarsi che la vista sia attiva prima di getHeight, ecc. Fallo utilizzando il metodo hasFocus (), quindi usando il metodo getHeight () in quell'ordine. Sono necessarie solo 3 righe di codice.

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

int myButtonHeight = myImageButton1.getHeight();

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

Spero che sia d'aiuto.


-3

Utilizzare getMeasuredWidth () e getMeasuredHeight () per la visualizzazione.

Guida per gli sviluppatori: Visualizza


4
Come indicato in precedenza, questi metodi restituiscono un risultato valido solo dopo che la dimensione è stata misurata. All'interno di un'attività questo vale quando viene chiamato il metodo onWindowFocusChanged.
slott

-8

CORREZIONE: ho scoperto che la soluzione di cui sopra è terribile. Soprattutto quando il tuo telefono è lento. E qui, ho trovato un'altra soluzione: calcolare il valore px dell'elemento, inclusi i margini e le imbottiture: dp in px: https://stackoverflow.com/a/6327095/1982712

o dimens.xml in px: https://stackoverflow.com/a/16276351/1982712

sp in px: https://stackoverflow.com/a/9219417/1982712 (invertire la soluzione)

o si attenua in px: https://stackoverflow.com/a/16276351/1982712

e basta.


10
Di solito è una cattiva idea sperare che tutto sia impostato correttamente dopo un numero di millisecondi scelto arbitrariamente. Il dispositivo potrebbe essere lento, altri processi / thread potrebbero essere in esecuzione, potrebbe non funzionare nel debugger, potrebbe non funzionare nella prossima versione del sistema operativo, ...
Kristopher Johnson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.