MeasureSpec

James Lin
8 min readNov 11, 2018

--

MeasureSpec 是 View 底下的 static class。

首先要先理解各個階段 View 所做的事情

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}

在此階段,View 會依據 Super View 所賦予的 widthMeasureSpec 與 heightMeasureSpec 去測量此 View 所要的長(measuredHeight)與寬(measureWidth)。

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
}

onLayout 是用來設定繪製 View 的邊界

大概是像這樣,也就是基於 Super View,child view 可以繪製的範圍。

onDraw 會傳入 canvas,View 就是畫在 canvas 上的!!

override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
}

自定義 View 的繪製流程:

使用 inflater 製作出一個 View

var view = layoutInflater.inflate(R.layout.child_view, null, false)

接著設定 View 的 LayoutParams

view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)

那什麼是 LayoutParams 呢?

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
>
....
</android.support.constraint.ConstraintLayout>

這是在 xml 裡面的 UI,在 ConstrintLayout 也就是 View,有兩個 tag ,分別是,這個就是 LayoutParams,Super View 會根據 LayoutParams 的不同而設定不同的 MeasureSpec Mode。

android:layout_width="match_parent"
android:layout_height="match_parent"

Super View 使用 MeasureSpec 為 child view 騰出一個空間。

設定對應的 MeasureSpec.makeMeasureSpec。

var width = MeasureSpec.makeMeasureSpec(this.measuredWidth, MeasureSpec.AT_MOST)var height = MeasureSpec.makeMeasureSpec(this.measuredHeight, MeasureSpec.AT_MOST)

設定 view 上面顯示的資料(一定要在 measure 前設定),否則 measure 出來的長與寬會不正確。

view.textView.text = "hello \n James"

接著讓 Child View 自己決定要多大的空間

view.measure(width, height)

注意:在 measure 之後,view.measuredWidth 以及 view.measuredHeight 就會有測量完的寬與高

使用 view.layout 將 view 設定在合適的位置

view.layout(0, 0, view.measuredWidth, view.measuredHeight)

最後將 View 畫在 canvas 上

view.draw(canvas)

MeasureSpec 相關參數:

UNSPECIFIED:不限制 Child View 如何渲染,Child View 會超出 Super View 的範圍(Scroll View)。

EXACTLY:Child View 必須提供一個確切的長與寬。

AT_MOST:Child View 所設定的長與寬最大只能與 Super View 一樣大。

Android 設計 View 的邏輯

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
>

<TextView
android:id="@+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>

</android.support.constraint.ConstraintLayout>

View 的大小是由 Super View 與 Child View 共同決定的,那 Root View 並沒有 Super View,以上述的例子來解釋

ConstraintLayout 的 layout_height 做了兩件事情:

  1. 與 ConstraintLayout 的 Super View 一起 measure ConstraintLayout。
  2. 扮演 TextView 的 MeasureSpec,並與 TextView 一起 measure TextView。

--

--

James Lin
James Lin

No responses yet