Modo autorevole per sovrascrivere onMeasure ()?


88

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

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

D'altra parte, come in questo post , il modo "corretto" è usare MeasureSpec, chiamare setMeasuredDimensions (), seguito da una chiamata a setLayoutParams () e terminare con una chiamata a super.onMeasure (). Per 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);
}

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

Immagino che davvero quello che sto chiedendo è qualcuno sa di un tutorial che spiega onMeasure (), layout, dimensioni delle viste figlio ecc.?


15
hai trovato la risposta utile / corretta? perché non contrassegnare come la risposta?
superjos

Risposte:


68

Le altre soluzioni non sono complete. Possono funzionare in alcuni casi e sono un buon punto di partenza, ma potrebbe non essere garantito che funzionino.

Quando viene chiamato onMeasure, potresti avere o meno il diritto di modificare la taglia. I valori che vengono passati al tuo onMeasure ( widthMeasureSpec, heightMeasureSpec) contengono informazioni su ciò che è consentito fare alla visualizzazione figlio. Attualmente ci sono tre valori:

  1. MeasureSpec.UNSPECIFIED - Puoi essere grande quanto vuoi
  2. MeasureSpec.AT_MOST- Grande quanto vuoi (fino alla dimensione specifica), questo è parentWidthnel tuo esempio.
  3. MeasureSpec.EXACTLY- Nessuna scelta. Il genitore ha scelto.

Questo viene fatto in modo che Android possa eseguire più passaggi per trovare la taglia giusta per ogni articolo, vedi qui per maggiori dettagli.

Se non segui queste regole, non è garantito che il tuo approccio funzioni.

Ad esempio, se vuoi controllare se sei autorizzato a cambiare le dimensioni, puoi fare quanto segue:

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

Usando queste informazioni saprai se puoi modificare i valori come nel tuo codice. O se ti viene richiesto di fare qualcosa di diverso. Un modo semplice e veloce per risolvere la dimensione desiderata è utilizzare uno dei seguenti metodi:

int resolSizeAndState (int size, int measureSpec, int childMeasuredState)

int resolSize (int size, int measureSpec)

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

Nota: potresti trovarlo resizeWidtho resizeHeightessere sempre falso. Ho scoperto che questo è il caso se stavo richiedendo MATCH_PARENT. Sono stato in grado di risolvere questo problema richiedendo WRAP_CONTENTal layout del mio genitore e poi durante la fase NON SPECIFICATA richiedendo una dimensione di Integer.MAX_VALUE. In questo modo ottieni la dimensione massima consentita dal tuo genitore al passaggio successivo di onMeasure.


39

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

Per riassumere: alla fine del tuo onMeasuremetodo sovrascritto dovresti chiamare setMeasuredDimension.

Non dovresti chiamare super.onMeasuredopo aver chiamato setMeasuredDimension, ciò cancellerà semplicemente ciò che hai impostato. In alcune situazioni potresti voler chiamare il super.onMeasureprimo e poi modificare i risultati chiamando setMeasuredDimension.

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


La mia esperienza è che se eseguo l'override di onMeasure senza chiamare super.onMeasure, OnLayout non viene chiamato nelle visualizzazioni figlio.
William Jockusch,

2

Penso che dipenda dal genitore che stai sovrascrivendo.

Ad esempio, se estendi un ViewGroup (come FrameLayout), dopo aver misurato la dimensione, dovresti chiamare come sotto

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

perché potresti voler ViewGroup per fare il lavoro di riposo (fai alcune cose sulla visualizzazione bambino)

Se stai estendendo una vista (come ImageView), puoi semplicemente chiamare this.setMeasuredDimension(width, height);, perché la classe genitore farà semplicemente qualcosa come hai fatto di solito.

In una parola, se vuoi alcune funzionalità che la tua classe genitore offre gratuitamente dovresti chiamare super.onMeasure()(di solito superare le specifiche di misura in modalità MeasureSpec.EXACTLY), altrimenti call this.setMeasuredDimension(width, height);è sufficiente.


1

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 il componente ViewPager


3
Questa non è una soluzione adeguata. super.onMeasurecancellerà le dimensioni impostate usando setMeasuredDimension.
tomrozb

@tomrozb non è humptydevelopers.com/2013/05/… e puoi controllare le fonti Android
Yuli Reiri

@YuliReiri: soluzione perfetta!
Shayan Tabatabaee

0

Se si modifica la dimensione delle visualizzazioni all'interno di onMeasuretutto ciò di cui hai bisogno è la setMeasuredDimensionchiamata. Se stai cambiando la taglia al di fuori di onMeasurete, devi chiamare setLayoutParams. Ad esempio, cambiare la dimensione di una visualizzazione di testo quando il testo viene modificato.


Puoi ancora chiamare setMinWidth () e setMaxWidth () e fare in modo che lo standard onMeasure lo gestisca. Ho scoperto che è meglio non chiamare setLayoutParams dall'interno di una classe View personalizzata. Viene effettivamente utilizzato per ViewGroups o Attività per sovrascrivere il comportamento della visualizzazione figlio.
Dorrin


-1

Immagino che setLayoutParams e il ricalcolo delle misurazioni siano una soluzione alternativa per ridimensionare correttamente le viste figlio, poiché questo di solito viene fatto in onMeasure della classe derivata.

Tuttavia, questo raramente funziona correttamente (per qualsiasi motivo ...), è meglio invocare measureChildren (quando si ricava un ViewGroup) o provare qualcosa di simile quando necessario.


-5

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

6
Questo codice è fondamentalmente sbagliato. Innanzitutto, non tiene conto della modalità layout (vedi la risposta di Grimmace). È un male ma non ne vedrai l'effetto perché chiami anche super.onMeasure () alla fine, che sovrascrive i valori che hai impostato (vedi la risposta di satur9nine) e sconfigge lo scopo di sovrascrivere onMeasure () del tutto.
spaaarky
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.