2011-09-14 13 views
84

Qual è il modo corretto di ignorare onMeasure()? Ho visto vari approcci. Ad esempio, Sviluppo Android professionale utilizza MeasureSpec per calcolare le dimensioni, quindi termina con una chiamata a setMeasuredDimension(). Ad esempio:Modo autoritativo per ignorare onMeasure()?

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

D'altro canto, come per this post, il modo "corretto" è quello di utilizzare MeasureSpec, chiamare setMeasuredDimensions(), seguita da una chiamata a setLayoutParams(), e termina con una chiamata a super.onMeasure(). Ad esempio:

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

Quindi qual è la strada giusta? Nessuno dei due approcci ha funzionato al 100% per me.

Immagino davvero che cosa sto chiedendo è qualcuno sa di un tutorial che spiega onMeasure(), il layout, le dimensioni delle visualizzazioni figlio ecc.?

+15

hai trovato la risposta utile/corretta? perché non contrassegnarlo come * la * risposta? – superjos

risposta

38

La documentazione è l'autorità in materia: http://developer.android.com/guide/topics/ui/how-android-draws.html e http://developer.android.com/guide/topics/ui/custom-components.html

In sintesi: alla fine del tuo metodo sovrascritto onMeasure si dovrebbe chiamare setMeasuredDimension.

Non chiamare il numero super.onMeasure dopo aver chiamato setMeasuredDimension, per cancellare tutto ciò che si è impostato. In alcune situazioni potresti voler chiamare prima lo super.onMeasure e poi modificare i risultati chiamando lo setMeasuredDimension.

Non chiamare setLayoutParams in onMeasure. Il layout avviene in un secondo passaggio dopo la misurazione.

+0

La mia esperienza è che se eseguo l'override su Measure senza chiamare super.onMeasure, OnLayout non viene richiamato nelle viste secondarie. –

-1

Immagino che impostareLayoutParam e ricalcolare le misurazioni sia una soluzione per ridimensionare correttamente le viste secondarie, come di solito si fa con onMeasure della classe derivata.

Tuttavia, questo funziona raramente in modo corretto (per qualsiasi motivo ...), meglio richiamare measureChildren (quando si ottiene un ViewGroup) o provare qualcosa di simile quando necessario.

63

Le altre soluzioni non sono complete. Possono funzionare in alcuni casi e sono un buon punto di partenza, ma potrebbero non essere garantiti per funzionare.

Quando viene chiamato onMeasure, è possibile o meno avere i diritti per modificare le dimensioni. I valori passati a onMeasure (widthMeasureSpec, heightMeasureSpec) contengono informazioni su ciò che la visualizzazione figlio può fare. Attualmente ci sono tre valori:

  1. MeasureSpec.UNSPECIFIED - Si può essere grande come vuoi
  2. MeasureSpec.AT_MOST - grande come si desidera (fino alle dimensioni specifiche), questo è parentWidth nel tuo esempio.
  3. MeasureSpec.EXACTLY - Nessuna scelta. Il genitore ha scelto.

Questo è fatto in modo che Android può effettuare più passaggi per trovare la giusta dimensione per ogni articolo, vedere here per maggiori dettagli.

Se non si seguono queste regole, il proprio approccio non è garantito.

Per esempio, se si desidera verificare se si è permesso di modificare la dimensione del tutto è possibile effettuare le seguenti operazioni:

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

Utilizzando queste informazioni si saprà se è possibile modificare i valori come in il tuo codice. O se ti viene richiesto di fare qualcosa di diverso. Un modo semplice e veloce per risolvere il formato desiderato è quello di utilizzare uno dei seguenti metodi:

int resolveSizeAndState (int size, int measureSpec, int childMeasuredState)

int resolveSize (int size, int measureSpec)

Mentre il primo è disponibile solo su Honeycomb, la seconda è disponibile su tutte le versioni.

Nota: è possibile che resizeWidth o resizeHeight siano sempre falsi. Ho trovato questo caso, se stavo richiedendo MATCH_PARENT. Sono stato in grado di risolvere il problema richiedendo WRAP_CONTENT sul mio layout principale e quindi durante la fase di richiesta richiedendo una dimensione di Integer.MAX_VALUE. In questo modo puoi ottenere la dimensione massima consentita dal genitore al passaggio successivo su onMeasure.

-4

si può prendere questo pezzo di codice come esempio di onMeasure() ::

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

Questo codice è fondamentalmente sbagliato. Innanzitutto, non tiene conto della modalità di layout (vedere la risposta di Grimmace). Questo è male ma non vedrai l'effetto di questo perché anche tu chiami super.onMeasure() alla fine, che sovrascrive i valori impostati (vedi la risposta di satur9nine) e vanifica lo scopo di sovrascrivere onMeasure(). – spaaarky21

0

Se la modifica della dimensione di vista interno della onMeasure tutto ciò che serve è la chiamata setMeasuredDimension. Se si modifica la dimensione al di fuori di onMeasure, è necessario chiamare setLayoutParams. Ad esempio, cambiando la dimensione di una vista testuale quando il testo è cambiato.

+0

È ancora possibile chiamare setMinWidth() e setMaxWidth() e avere lo standard onMeasure per gestirlo. Ho trovato che è meglio non chiamare setLayoutParams all'interno di una classe vista personalizzata. Viene utilizzato per ViewGroups o Attività per sovrascrivere il comportamento di visualizzazione figlio. – Dorrin

0

ecco come ho risolto il problema:

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

}

Inoltre era necessario per la componente ViewPager

+2

Questa non è la soluzione giusta. 'super.onMisura' cancellerà le dimensioni impostate usando' setMeasuredDimension'. – tomrozb

+0

@tomrozb non è http://www.humptydevelopers.com/2013/05/android-view-overriding-onmeasure.html e puoi controllare le origini Android –

0

Penso che dipende dal genitore ciò che si stanno scavalcando.

Per esempio, se si sta estendendo un ViewGroup (come FrameLayout), dopo aver misurato la dimensione, si dovrebbe chiamare come qui di seguito

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

perché si consiglia di ViewGroup per fare il lavoro di riposo (fare un po ' oggetti in vista bambino)

Se si sta estendendo una vista (come ImageView), è sufficiente chiamare lo this.setMeasuredDimension(width, height);, perché la classe genitore farà semplicemente qualcosa come si è fatto di solito.

In una parola, se si desidera che alcune funzionalità della classe genitore siano gratuite, è necessario chiamare super.onMeasure() (passare di solito MeasureSpec.EXACTLY in modo spec.), Altrimenti chiamare this.setMeasuredDimension(width, height); è sufficiente.

Problemi correlati