it-swarm.com.de

onMeasure benutzerdefinierte Ansicht Erklärung

Ich habe versucht, eine benutzerdefinierte Komponente zu erstellen. Ich habe die Klasse View erweitert und mit der überschriebenen Methode onDraw gezeichnet. Warum muss ich onMeasure überschreiben? Wenn ich es nicht tat, sah alles so aus, als ob es richtig wäre. Darf es jemand erklären? Wie soll ich meine onMeasure Methode schreiben? Ich habe ein paar Tutorials gesehen, aber jedes ist ein bisschen anders als das andere. Manchmal rufen sie super.onMeasure am Ende benutzen sie manchmal setMeasuredDimension und haben es nicht aufgerufen. Wo ist ein Unterschied?

Immerhin möchte ich mehrere genau die gleichen Komponenten verwenden. Ich habe diese Komponenten zu meiner XML -Datei hinzugefügt, weiß aber nicht, wie groß sie sein sollen. Ich möchte seine Position und Größe später in der benutzerdefinierten Komponentenklasse festlegen (warum muss ich die Größe in onMeasure festlegen, wenn in onDraw beim Zeichnen dies ebenfalls funktioniert). Wann genau muss ich das tun?

304
sennin

onMeasure() ist Ihre Gelegenheit, Android mitzuteilen, wie groß Ihre benutzerdefinierte Ansicht sein soll, abhängig von den vom übergeordneten Element bereitgestellten Layout-Einschränkungen. Außerdem können Sie in Ihrer benutzerdefinierten Ansicht erfahren, wie diese Layouteinschränkungen aussehen (falls Sie sich in einer match_parent - Situation anders verhalten möchten als in einer wrap_content - Situation). Diese Einschränkungen werden in die MeasureSpec Werte gepackt, die an die Methode übergeben werden. Hier ist eine grobe Korrelation der Moduswerte:

  • GENAU bedeutet, dass der Wert layout_width oder layout_height auf einen bestimmten Wert gesetzt wurde. Sie sollten Ihre Ansicht wahrscheinlich auf diese Größe bringen. Dies kann auch ausgelöst werden, wenn match_parent Verwendet wird, um die Größe genau auf die übergeordnete Ansicht einzustellen (dies ist layoutabhängig im Framework).
  • AT_MOST bedeutet normalerweise, dass der Wert layout_width Oder layout_height Auf match_parent Oder wrap_content Gesetzt wurde, wenn eine maximale Größe erforderlich ist (dies ist Layout im Framework abhängig) und die Größe der übergeordneten Dimension ist der Wert. Sie sollten nicht größer als diese Größe sein.
  • NICHT ANGEGEBEN bedeutet normalerweise, dass der Wert layout_width oder layout_height ohne Einschränkungen auf wrap_content gesetzt wurde. Sie können jede Größe haben, die Sie möchten. Einige Layouts verwenden diesen Rückruf auch, um die gewünschte Größe zu ermitteln, bevor Sie festlegen, welche Spezifikationen Sie in einer zweiten Messanforderung erneut übergeben sollen.

Der Vertrag, der mit onMeasure() besteht, besteht darin, dass setMeasuredDimension() MUSS am Ende mit der Größe aufgerufen werden, die Sie möchten die Aussicht zu sein. Diese Methode wird von allen Framework-Implementierungen aufgerufen, einschließlich der Standardimplementierung in View, weshalb es sicher ist, super aufzurufen, wenn dies für Ihren Anwendungsfall geeignet ist.

Zugegeben, da das Framework eine Standardimplementierung anwendet, ist es möglicherweise nicht erforderlich, diese Methode zu überschreiben. In Fällen, in denen der Anzeigebereich kleiner ist als Ihr Inhalt, und in denen Sie den Inhalt anpassen, kann es jedoch zu Ausschnitten kommen Benutzerdefinierte Ansicht mit wrap_content in beide Richtungen, Ihre Ansicht wird möglicherweise überhaupt nicht angezeigt, da das Framework nicht weiß, wie groß es ist!

Wenn Sie View überschreiben und kein anderes vorhandenes Widget, ist es wahrscheinlich eine gute Idee, eine Implementierung bereitzustellen, auch wenn diese so einfach wie die folgende 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);
}

Ich hoffe, das hilft.

707
Devunwired

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

  • GENAU match_parent ist GENAU + Größe des übergeordneten Elements
  • AT_MOST wrap_content ergibt eine AT_MOST MeasureSpec
  • NICHT ANGEGEBEN nie ausgelöst

Im Falle einer horizontalen Bildlaufansicht funktioniert Ihr Code.

4
Florian

Wenn Sie bei Measure nichts ändern müssen, müssen Sie es auf keinen Fall überschreiben.

Devunwired-Code (die ausgewählte und am häufigsten gewählte Antwort hier) ist fast identisch mit dem, was die SDK-Implementierung bereits für Sie erledigt (und ich habe überprüft, dass dies seit 2009 der Fall ist).

Sie können die onMeasure-Methode überprüfen hier :

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

Das Überschreiben des SDK-Codes, der durch denselben Code ersetzt werden soll, macht keinen Sinn.

Dieses offizielles Dokument , das besagt, dass "die Standardeinstellung von onMeasure () immer eine Größe von 100x100 festlegt", ist falsch.

0
David Refaeli