Android自訂視圖四:定製onMeasure強制顯示為方形

來源:互聯網
上載者:User

標籤:

這個系列是老外寫的,乾貨!翻譯出來一起學習。如有不妥,不吝賜教!

  1. Android自訂視圖一:擴充現有的視圖,添加新的XML屬性
  2. Android自訂視圖二:如何繪製內容
  3. Android自訂視圖三:給自訂視圖添加“流暢”的動畫
  4. Android自訂視圖四:定製onMeasure強制顯示為方形


上一篇開發之後的效果如。不過看著這張圖,需要注意的不是我們自訂視圖展示了什麼,而是這個視圖的大小和位置。你會看到這個折線圖有一個特定的大小(size)。這個size是怎麼定的呢?現在的代碼是使用了一個豎直方向的LinearLayout,折線圖和他下面的TextView使用layout_weight屬性平分了他們所在的LinearLayout的高度。那麼如果我們刪掉了TextView和全部的layout_weight,並把折線圖的高度設定為wrap_content會發生什麼呢?

是的,以上修改之後整個的圖就變成了這樣。雖然使用了wrap_content的高度,但是效果是填滿了整個LinearLayout。這就是View的預設布局行為,但是,如果我們要改變一下呢?

View的Layout

自訂視圖顯示在螢幕上一共分三步:measure(測量),layout(布局),draw(繪製)。基本上一個自訂視圖在測量這一步計算大小,之後可以通過getMeasureWidthgetMeasureHeight得到View的寬度和高度。在布局計算這個自訂視圖的左上和右下座標以及實際的寬度和高度,最後根據以上layout步驟獲得的資料調用onDraw方法把View繪製在螢幕上。

所以要修改size,也就是自訂視圖中修改預設的行為,就需要override onMeasure()方法。一般的通用做法是:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec)    var widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)    var widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)    var heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)    var heightSpecSize = MeasureSpec.getSize(heightMeasureSpec)    if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {        // set default width and height values you defined        setMeasuredDimension(mDefaultWidth, mDefaultHeight)    } else if (widthSpecMode == MeasureSpec.AT_MOST) {        // set default width value and calculated `heightSpecSize` as height        setMeasuredDimension(mDefaultWidth, heightSpecSize)    } else if (heightSpecMode == MeasureSpec.AT_MOST) {        // set calculated `widthSpecSize` as width and default height        setMeasuredDimension(widthSpecSize, mDefaultHeight)    }}

簡單理解MeasureSpec.AT_MOST就是你給折線圖的layout_width或者layout_height設定了wrap_content,系統不知道精確的寬度、高度是多少的時候的一個標記。如果有具體的50dp, 100dp的時候,這個標記的值為MeasureSpec.EXACTLY。一般,一個view的寬度、高度只有這兩種標記。
View的寬、高度測量分別處理三種情況:
1. 如果寬、高度都是AT_MOST的時候,寬度和高度設定為預設值。
2. 寬度為AT_MOST高度不是的時候,寬度設定為預設值,高度設定為測量的值heightSpecSize
3. 寬度為精確值,高度為AT_MOST的時候,寬度設定為widthSpecSize,高度設定為預設值。

而我們這裡則是意外的簡單。因為要設定為正方形,所以使用寬度和高度中相對較小的那個值來作為寬、高度共同的值就可以了:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec)    var size = 0    var width = getMeasuredWidth()    var height = getMeasuredHeight()    if (width > height) {        size = height    } else {        size = width    }    setMeasuredDimension(size, size)}

如前文所說,我們要把折線圖這個自訂視圖設定為正方形。所以,不管測量的Mode是如何的,只要用寬、高中的最小值就可以了。

而上面說到的layout部分,對於有子view的`View`比較有用,也就是說對於繼承自`ViewGroup`的`View`來說比較有用。我們的折線圖知識一個單純的不能更單純的`View`。

我們前面在onDraw方法使用的getWidth()getHeight()onMeasure()方法中都是停用。因為這個時候正在計算寬、高度。在這個方法裡只能取到getMeasuredWidth()getMeasuredHeight()

override onMeasure方法就一定要在最後調用setMeasuredDimension方法。調用setMeasuredDimension方法是告訴父view當前view的測量高度是多少。如果不調用這個方法的話會拋異常。

修改之後的布局,寬度match_parent,高度wrap_content不必要的屬性都略掉了

<RelativeLayout>    <LinearLayout>        <Button android:text="walking" />        <Button android:text="Running" />        <Button android:text="Cycling" />    </LinearLayout>    <demo.customview.customviewdemo.Views.SquareLineChartView        android:layout_width="match_parent"        android:layout_height="wrap_content" /></RelativeLayout>

效果:

支援任意寬高比例

一個正方形的View已經非常實用了。比如繼承ImageView之後像上面一樣overrideonMeasure()方法就可以得到一個一直都是正方形顯示的View。那麼,既然我們已經支援了寬、高1:1了,為什麼不支援任意的寬高比呢。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec)    var width = getMeasuredWidth()    var height = getMeasuredHeight()    var widthWithoutPadding = width - paddingLeft - paddingRight    var heightWithoutPadding = height - paddingTop - paddingBottom    var maxWidth = (heightWithoutPadding * RATIO).toInt()    var maxHeight = (widthWithoutPadding / RATIO).toInt()    if (widthWithoutPadding > maxWidth) {        width = maxWidth + paddingLeft + paddingRight    } else {        height = maxHeight + paddingTop + paddingBottom    }    setMeasuredDimension(width, height)}

上面的代碼就可以支援任意的寬高比了。看看效果(比例7:3):

Android自訂視圖四:定製onMeasure強制顯示為方形

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.