onMeasure自定義視圖說明

本文翻譯自:onMeasure custom view explanation

I tried to do custom component. 我試着做自定義組件。 I extended View class and do some drawing in onDraw overrided method. 我擴展了View類並在onDraw覆蓋方法中進行了一些繪製。 Why I need to override onMeasure ? 爲什麼我需要覆蓋onMeasure If I didn't, everything seen to be right. 如果我沒有,一切都被證明是正確的。 May someone explain it? 有人可以解釋一下嗎? How should I write my onMeasure method? 我該如何編寫onMeasure方法? I've seen couple tutorials, but each one is a little bit different than the other. 我見過幾個教程,但每個教程都有點不同。 Sometimes they call super.onMeasure at the end, sometimes they use setMeasuredDimension and didn't call it. 有時他們最後會調用super.onMeasure ,有時他們使用setMeasuredDimension並且沒有調用它。 Where is a difference? 差異在哪裏?

After all I want to use several exactly the same components. 畢竟我想要使用幾個完全相同的組件。 I added those components to my XML file, but I don't know how big they should be. 我將這些組件添加到我的XML文件中,但我不知道它們應該有多大。 I want to set its position and size later (why I need to set size in onMeasure if in onDraw when I draw it, is working as well) in custom component class. 我想稍後設置它的位置和大小(爲什麼我需要在onMeasure設置大小,如果我在onDraw繪製它,也可以在自定義組件類中設置)。 When exactly I need to do that? 什麼時候我需要這樣做?


#1樓

參考:https://stackoom.com/question/pTBD/onMeasure自定義視圖說明


#2樓

onMeasure() is your opportunity to tell Android how big you want your custom view to be dependent the layout constraints provided by the parent; onMeasure()是告訴Android您希望自定義視圖依賴於父級提供的佈局約束的大小的機會; it is also your custom view's opportunity to learn what those layout constraints are (in case you want to behave differently in a match_parent situation than a wrap_content situation). 您也可以通過自定義視圖瞭解這些佈局約束是什麼(如果您希望在match_parent情況下的行爲與wrap_content情況不同)。 These constraints are packaged up into the MeasureSpec values that are passed into the method. 這些約束被打包到傳遞給方法的MeasureSpec值中。 Here is a rough correlation of the mode values: 以下是模式值的粗略關聯:

  • EXACTLY means the layout_width or layout_height value was set to a specific value. 確切意味着layout_widthlayout_height值設置爲特定值。 You should probably make your view this size. 您可能應該將視圖設爲這麼大。 This can also get triggered when match_parent is used, to set the size exactly to the parent view (this is layout dependent in the framework). 這也可以在使用match_parent時觸發,以將大小精確地設置爲父視圖(這取決於框架中的佈局)。
  • AT_MOST typically means the layout_width or layout_height value was set to match_parent or wrap_content where a maximum size is needed (this is layout dependent in the framework), and the size of the parent dimension is the value. AT_MOST通常表示layout_widthlayout_height值設置爲match_parentwrap_content ,其中需要最大大小(這取決於框架中的佈局),並且父維度的大小是值。 You should not be any larger than this size. 你不應該大於這個尺寸。
  • UNSPECIFIED typically means the layout_width or layout_height value was set to wrap_content with no restrictions. UNSPECIFIED通常表示layout_widthlayout_height值設定爲wrap_content沒有任何限制。 You can be whatever size you would like. 你可以任意大小。 Some layouts also use this callback to figure out your desired size before determine what specs to actually pass you again in a second measure request. 某些佈局還會使用此回調來確定所需的大小,然後再確定在第二個度量請求中實際傳遞給您的規範。

The contract that exists with onMeasure() is that setMeasuredDimension() MUST be called at the end with the size you would like the view to be. onMeasure()存在的契約是setMeasuredDimension() 必須在末尾調用您希望視圖的大小。 This method is called by all the framework implementations, including the default implementation found in View , which is why it is safe to call super instead if that fits your use case. 所有框架實現都會調用此方法,包括View的默認實現,這就是爲什麼在適合您的用例時調用super是安全的。

Granted, because the framework does apply a default implementation, it may not be necessary for you to override this method, but you may see clipping in cases where the view space is smaller than your content if you do not, and if you lay out your custom view with wrap_content in both directions, your view may not show up at all because the framework doesn't know how large it is! 當然,因爲框架確實應用了默認實現,所以您可能沒有必要覆蓋此方法,但如果您不這樣做,您可能會在視圖空間小於您的內容的情況下看到裁剪,如果您佈局了自定義視圖在兩個方向都有wrap_content ,您的視圖可能根本不顯示,因爲框架不知道它有多大!

Generally, if you are overriding View and not another existing widget, it is probably a good idea to provide an implementation, even if it is as simple as something like this: 通常,如果您要覆蓋View而不是其他現有窗口小部件,那麼提供實現可能是一個好主意,即使它像這樣簡單:

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

Hope that Helps. 希望有助於。


#3樓

actually, your answer is not complete as the values also depend on the wrapping container. 實際上,您的答案並不完整,因爲值也取決於包裝容器。 In case of relative or linear layouts, the values behave like this: 在相對或線性佈局的情況下,值的行爲如下:

  • EXACTLY match_parent is EXACTLY + size of the parent 完全匹配_parent是完全+父級的大小
  • AT_MOST wrap_content results in an AT_MOST MeasureSpec AT_MOST wrap_content導致AT_MOST MeasureSpec
  • UNSPECIFIED never triggered UNSPECIFIED從未觸發

In case of an horizontal scroll view, your code will work. 如果是水平滾動視圖,您的代碼將起作用。


#4樓

If you don't need to change something onMeasure - there's absolutely no need for you to override it. 如果您不需要更改onMeasure上的內容 - 您絕對不需要覆蓋它。

Devunwired code (the selected and most voted answer here) is almost identical to what the SDK implementation already does for you (and I checked - it had done that since 2009). Devunwired代碼(這裏選擇和最多的投票答案)幾乎與SDK實現已經爲您做的(我檢查過 - 自2009年以來已經完成)。

You can check the onMeasure method here : 你可以在這裏查看onMeasure方法:

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

Overriding SDK code to be replaced with the exact same code makes no sense. 覆蓋用完全相同的代碼替換的SDK代碼是沒有意義的。

This official doc's piece that claims "the default onMeasure() will always set a size of 100x100" - is wrong. 這篇官方文檔聲稱“默認onMeasure()將始終設置爲100x100”的大小 - 是錯誤的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章