2012-09-04 14 views
263

Ich habe versucht, benutzerdefinierte Komponente zu tun. Ich erweiterte View Klasse und machen einige Zeichnung in onDraw überlagerte Methode. Warum muss ich onMeasure überschreiben? Wenn ich es nicht tat, sah alles richtig aus. Darf es jemand erklären? Wie sollte ich meine onMeasure Methode schreiben? Ich habe paar Tutorials gesehen, aber jedes ist ein bisschen anders als das andere. Manchmal rufen sie am Ende super.onMeasure, manchmal verwenden sie setMeasuredDimension und haben es nicht aufgerufen. Wo ist ein Unterschied?onMeasure benutzerdefinierte Ansicht Erklärung

Immerhin möchte ich mehrere genau die gleichen Komponenten verwenden. Ich habe diese Komponenten zu meiner XML Datei hinzugefügt, aber ich weiß nicht, wie groß sie sein sollten. Ich möchte seine Position und Größe später festlegen (warum muss ich Größe in onMeasure wenn in onDraw10 einstellen, wenn ich es zeichne, funktioniert auch) in benutzerdefinierte Komponente Klasse. Wann genau muss ich das machen?

Antwort

649

onMeasure() ist Ihre Gelegenheit, Android zu sagen, wie groß Ihre benutzerdefinierte Ansicht von den Layoutbeschränkungen abhängen soll, die vom übergeordneten Element bereitgestellt werden; Es ist auch die Gelegenheit Ihrer benutzerdefinierten Ansicht, zu erfahren, was diese Layouteinschränkungen sind (falls Sie sich in einer match_parent Situation anders als in einer wrap_content Situation verhalten möchten). Diese Einschränkungen werden in die Werte MeasureSpec verpackt, die an die Methode übergeben werden. Hier ist eine grobe Korrelation der Moduswerte:

  • GENAU bedeutet der layout_width oder layout_height Wert auf einen bestimmten Wert eingestellt wurde. Sie sollten wahrscheinlich Ihre Ansicht dieser Größe machen. Dies kann auch ausgelöst werden, wenn match_parent verwendet wird, um die Größe genau auf die übergeordnete Ansicht einzustellen (dies ist vom Layout abhängig im Framework).
  • AT_MOST bedeutet typischerweise der layout_width oder layout_height Wert match_parent oder wrap_content eingestellt war, wo eine Maximalgröße benötigt wird (dies ist das Layout in dem Rahmen abhängig), und die Größe der Dimension Elternteil ist der Wert. Sie sollten nicht größer als diese Größe sein.
  • bedeutet normalerweise layout_width oder layout_height Wert wurde auf wrap_content ohne Einschränkungen festgelegt. Sie können beliebig groß sein. Einige Layouts verwenden diesen Callback auch, um die gewünschte Größe herauszufinden, bevor Sie bestimmen, welche Spezifikationen Sie in einer zweiten Messanforderung tatsächlich erneut übergeben sollen.

Der Vertrag, der mit onMeasure() existiert, ist, dass setMeasuredDimension()MUST am Ende mit der Größe aufgerufen werden würden Sie die Ansicht gerne sein. Diese Methode wird von allen Framework-Implementierungen aufgerufen, einschließlich der Standardimplementierung, die in View zu finden ist. Aus diesem Grund ist es ratsam, stattdessen super aufzurufen, wenn dies zu Ihrem Anwendungsfall passt.

Zugegeben, da das Framework eine Standardimplementierung anwendet, ist es möglicherweise nicht notwendig, diese Methode zu überschreiben, aber Sie sehen möglicherweise Clipping in Fällen, in denen der Ansichtsbereich kleiner als Ihr Inhalt ist Sie legen Ihre benutzerdefinierte Ansicht mit wrap_content in beide Richtungen, Ihre Ansicht wird möglicherweise nicht angezeigt, da das Framework nicht weiß, wie groß es ist!

Allgemeinen, wenn Sie View und nicht ein anderes vorhandenes Widget zwingende, ist es wahrscheinlich eine gute Idee, eine Implementierung zur Verfügung zu stellen, auch wenn es so etwas wie dies so einfach ist:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 

    int desiredWidth = 100; 
    int desiredHeight = 100; 

    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
    int heightSize = MeasureSpec.getSize(heightMeasureSpec); 

    int width; 
    int height; 

    //Measure Width 
    if (widthMode == MeasureSpec.EXACTLY) { 
     //Must be this size 
     width = widthSize; 
    } else if (widthMode == MeasureSpec.AT_MOST) { 
     //Can't be bigger than... 
     width = Math.min(desiredWidth, widthSize); 
    } else { 
     //Be whatever you want 
     width = desiredWidth; 
    } 

    //Measure Height 
    if (heightMode == MeasureSpec.EXACTLY) { 
     //Must be this size 
     height = heightSize; 
    } else if (heightMode == MeasureSpec.AT_MOST) { 
     //Can't be bigger than... 
     height = Math.min(desiredHeight, heightSize); 
    } else { 
     //Be whatever you want 
     height = desiredHeight; 
    } 

    //MUST CALL THIS 
    setMeasuredDimension(width, height); 
} 

Hoffe, das hilft.

+1

Hey @Devunwired nette Erklärung die beste, die ich bisher gelesen habe. Ihre Erklärung hat viele Fragen beantwortet und einige Zweifel ausgeräumt, aber dennoch bleibt eine übrig: Meine benutzerdefinierte Ansicht befindet sich innerhalb einer ViewGroup zusammen mit einigen anderen Ansichten (egal welche Typen), dass ViewGroup alle seine Kinder annimmt für jede einzelne Probe nach ihrer LayoutParams-Einschränkung und bitte jedes Kind, es entsprechend seinen Beschränkungen zu messen? – pharaoh

+0

Ja, das ist die 'measureChildren()' Methode von 'ViewGroup' während des Measure/Layout Prozesses. – Devunwired

+34

Beachten Sie, dass dieser Code nicht funktioniert, wenn Sie onMeasure einer ViewGroup-Unterklasse überschreiben. Ihre Unteransichten werden nicht angezeigt und haben alle eine Größe von 0x0. Wenn Sie OnMeasure einer benutzerdefinierten ViewGroup überschreiben müssen, ändern Sie widthMode, widthSize, heightMode und heightSize, kompilieren Sie sie mit MeasureSpec.makeMeasureSpec zurück zu measureSpecs und übergeben Sie die resultierenden Ganzzahlen an super.onMeasure. – Alexey

4

tatsächlich ist Ihre Antwort nicht vollständig, da die Werte auch vom Verpackungsbehälter abhängen. Im Falle einer relativen oder linearen Layouts, verhalten sich die Werte wie folgt aus:

  • GENAU match_parent ist GENAU + Größe des Mutter
  • AT_MOST wrap_content führt zu einer AT_MOST Measurespec
  • UNSPECIFIED nie ausgelöst

Im Falle einer horizontalen Bildlaufansicht funktioniert Ihr Code.

+49

Wenn Sie der Meinung sind, dass eine Antwort hier unvollständig ist, fügen Sie sie bitte hinzu, anstatt eine unvollständige Antwort zu geben. –

+0

Gute onya für die Verknüpfung dieser, wie Layouts funktionieren, aber in meinem Fall OnMeasure wird dreimal für meine benutzerdefinierte Ansicht aufgerufen. Die fragliche Ansicht hatte eine Wrap_Content-Höhe und eine gewichtete Breite (Breite = 0, Gewicht = 1).Der erste Anruf hatte SOFORT, der zweite hatte AT_MOST/EXAKT und der dritte hatte EXAKT/GENAU. –

Verwandte Themen