詳解 ViewGroupのレイアウト内部実装
by
HiroYUKI Seto
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
詳解 ViewGroupのレイアウト 内部実装 DroidKaigi 2018 株式会社ノハナ 瀬戸優之 @seto_hi
Slide 2
Slide 2 text
自己紹介 ● 瀬戸優之 Seto HiroYUKI @seto_hi ● Androidエンジニア & アプリデザイン ● 株式会社ノハナ ○ 2年連続DroidKaigiスポンサー! ○ 一組でも多くの家族に笑顔を届ける ○ 絶賛採用中 ● Material Design大好き ● 好きなAPIはCanvas#saveとViewGroup#layout
Slide 3
Slide 3 text
おしながき ● Viewってどうやってレイアウトされているの? ○ measure, layout ● 詳解FrameLayoutの内部実装 ● 詳解LinearLayoutの内部実装 ● 詳解ConstraintLayoutの内部実装 ○ ConstraintLayoutを支える技術 ○ Optimizer ○ 各機能の実現→付録にあります ● 付録
Slide 4
Slide 4 text
参考環境 コードを読んだ環境 ● FrameLayout, LinearLayout(, RelativeLayout) ○ Android 8.0、一部Android8.1 ● CosntraintLayout ○ constraint-layout:1.1.0-beta3, beta4, beta5 ○ 一部beta2
Slide 5
Slide 5 text
Viewってどうやって レイアウトされているの?
Slide 6
Slide 6 text
おしながき ● レイアウトの流れ ● measure ○ measure, onMeasure ○ MeasureSpec ○ measuredWidth, measuredHeight ● layout ○ layout, onLayout
Slide 7
Slide 7 text
レイアウトの流れ 1. レイアウトが要求される ○ View#requestLayout 2. Viewのサイズを計算する ○ measure, onMeasure 3. Viewのレイアウト ○ measureしたサイズを参考にする ○ layout, onLayout
Slide 8
Slide 8 text
View View Group View Gruop View Group View View request Layout Handler Traversal Runnable View Root Impl request Layout request Layout
Slide 9
Slide 9 text
View View Group View Gruop View Group View View measure View Root Impl measure measure measure measure measure Handler Traversal Runnable
Slide 10
Slide 10 text
View View Group View Gruop View Group View View layout View Root Impl layout layout layout layout layout
Slide 11
Slide 11 text
measureとlayoutの分割 ● Viewが自身だけでレイアウト位置は決められない ○ 他のViewのサイズでレイアウト位置が変わる ● 全部のViewをmeasureして 結果によってlayout位置を変える必要がある ○ 高機能なViewGroupは measureでレイアウト位置を確定させている
Slide 12
Slide 12 text
Viewのmeasure
Slide 13
Slide 13 text
measureとonMeasure ● measureがonMeasureを呼ぶ ● measureはViewクラスのfinalメソッド ● measureは呼び出す用 ● onMeasureは各Viewがoverrideする用
Slide 14
Slide 14 text
onMeasure ● Viewの幅と高さを計測 ○ ViewGroupは子Viewをmeasureする ● 自身のサイズも設定 ○ setMeasuredDimensionsで measuredWidth/Heightを設定 ■ 呼ばないと例外
Slide 15
Slide 15 text
measuredWidth, measuredHeight ● onLayoutで使う ● setMeasureDimensionsで設定した値※ ● View#getWidth/getHeightはlayout後の値 ○ View.mLeft-View.mRight ● layout以降は使ってはならない ○ measuredWidth/Height通りにlayoutされるとは限らない ○ layout以降はgetWidth/Heightを使う ※ 厳密にはMeasureSpec
Slide 16
Slide 16 text
MeasureSpec ● onMesureの引数 ● measureの条件(Mode)とSizeをintで表現したもの ○ Modeが上位2byte、Sizeが下位30bit Mode 意味 UNSPECIFIED Viewの好きなサイズにして良い EXACTLY 指定されたサイズにすべき AT_MOST Viewの好きなサイズにして良い でも指定されたサイズは超さないように
Slide 17
Slide 17 text
Viewのlayout
Slide 18
Slide 18 text
layoutとonLayout ● layoutがonLayoutを呼ぶ ● ViewGroup#layoutはfinalメソッド ● layoutが呼び出す用 ● onLayoutがoverrideする用 ● 各ViewGroupがonLayoutをoverrideする ○ ViewはonLayoutをoverrideする必要はない
Slide 19
Slide 19 text
ViewGroup#onLayout ● 子Viewのレイアウトを行う ○ ViewGroupに対する相対座標 ● measureした結果を使う ● measuredWidth/Height通りにlayoutすべき ○ 例:TextViewはmeasuredWidth/Height通りの 文字改行がされている
Slide 20
Slide 20 text
詳解 FrameLayoutの 内部実装
Slide 21
Slide 21 text
FrameLayout#onMeasure
Slide 22
Slide 22 text
FrameLayout#onMeasure 1. すべての子Viewをmeasure ○ FrameLayoutの引数と同じMeasureSpec 2. MeasureSpec#modeによって自身のサイズを変える ○ AT_MOST→子Viewの最大値とspecのsizeのmin ○ EXACTLY→specのsize ○ UNSPECIFIED →子Viewの最大値 3. match_parentな子Viewをmeasure ○ mode:EXACTLY、size:FrameLayoutの幅
Slide 23
Slide 23 text
FrameLayout#onMeasure FrameLayout View(w:match_parent, h:warp_conten) View#measuredWidth MeasureSpecのsize
Slide 24
Slide 24 text
FrameLayout#onLayout
Slide 25
Slide 25 text
FrameLayout#onLayout ● measureWidth/Heightのサイズでレイアウト ● Gravityによってレイアウト位置を変える
Slide 26
Slide 26 text
FrameLayout#onLayout ● leftの図 FrameLayout View Gravity:LEFT|BOTTOM 0 View#measuredWidth
Slide 27
Slide 27 text
FrameLayout#onLayout ● leftの図 FrameLayout View Gravity:LEFT|BOTTOM FrameLayout.bottom FrameLayout.bottom - View.mesuredHeight
Slide 28
Slide 28 text
詳解 LinearLayoutの 内部実装
Slide 29
Slide 29 text
LinearLayout#onMeasure
Slide 30
Slide 30 text
LinearLayout#onMeasure measureVertical/Horizontal 1. すべての子Viewをmeasure ○ 子Viewの高さ/幅の合計を記録する 2. weightがある子Viewがある場合 ○ weightを分配したサイズ+EXACTLYで再度measure 3. setMeasuredDimension
Slide 31
Slide 31 text
Weight Tips ● 0dp + weightがなぜ早いか ○ LinearLayoutのMesureSpecがEXACTLYの場合 0dp + weightのViewは 1回目のmeasureをskipするため
Slide 32
Slide 32 text
LinearLayout#onLayout
Slide 33
Slide 33 text
LinearLayout#onLayout ● layoutVertical/Horizontal ○ LtR対応があるのでhorizontalがやや複雑 ● 上/左から順にlayout ● orientationと逆方向のGravityが効く ○ 割愛
Slide 34
Slide 34 text
詳解 ConstraintLayoutの 内部実装
Slide 35
Slide 35 text
ところで
Slide 36
Slide 36 text
ところで
Slide 37
Slide 37 text
UIは 一次方程式と 一次不等式で 表せる
Slide 38
Slide 38 text
一次方程式と 一次不等式の 最適解を求める
Slide 39
Slide 39 text
線形計画問題
Slide 40
Slide 40 text
線形計画問題 線型計画問題とは、最適化問題において、 目的関数が線型関数で、 なおかつ線型関数の等式と不等式で 制約条件が記述できる問題である。 (wikipedia)
Slide 41
Slide 41 text
線形計画問題 例: 原料A 1gと原料B 2gで60円の商品Xを作れます 原料A 3gと原料B 1gで40円の商品Yを作れます 原料Aが90g、原料Bが50gあるとき 商品XとYをそれぞれいくつ作れば 売り上げが最大になるでしょう?
Slide 42
Slide 42 text
線形計画問題 商品Xをx、商品Yをyとする x + 3y < 90 2x + y < 50 x ≧ 0, y ≧ 0 のとき 60x + 40yの最大値を求める
Slide 43
Slide 43 text
図 線形計画問題 x + 3y < 90 2x + y < 50 40x + 60y = ?
Slide 44
Slide 44 text
図 線形計画問題 x + 3y < 90 2x + y < 50 (12, 26) 40x + 60y = 2040
Slide 45
Slide 45 text
完全に理解しましたね?
Slide 46
Slide 46 text
No content
Slide 47
Slide 47 text
シンプレックス法
Slide 48
Slide 48 text
シンプレックス法 シンプレックス法は、 実行可能解 (超多面体の頂点) の1つから出発して 目的関数の値をなるべく大きく (小さく) するようなところに移動さ せていく動作を繰り返して 最適解を見つけ出す方法である。 (wikipedia)
Slide 49
Slide 49 text
図 シンプレックス法 x + 3y < 90 2x + y < 50 (12, 26) 40x + 60y = 2040 ①初期基底解(BFS) ②
Slide 50
Slide 50 text
シンプレックス法 ● シンプレックス法 ● 二段階シンプレックス法 ○ 人工変数と人工的な目的関数を加えることで 初期基底解が簡単に求まらない場合に対応 ● 双対シンプレックス法 ○ 双対定理を利用し 双対問題を解くことで主問題も解く
Slide 51
Slide 51 text
完全に理解しましたね?
Slide 52
Slide 52 text
No content
Slide 53
Slide 53 text
Cassowary
Slide 54
Slide 54 text
Cassowary ● ヒクイドリ - 世界一危険な鳥 ● 線形方程式、線形不等式の 制約充足問題(constraint solving problem)の解法 ○ 2段階+双対シンプレックス法を使う ● アプリのUI用に最適化されている ○ 高速 ○ 省メモリ ○ 宣言的 ○ 差分更新可能
Slide 55
Slide 55 text
Cassowary ● 2001年に開発された ○ 最初はCSSのレイアウト拡張として ● 多くの言語に移植されている ○ Smalltalk, C++, Java, JavaScript, Dart, Python etc.. ● 2011年からMac(OS X)やiOS(6~)で使われている ○ AutoLayout ● ConstraintLayoutでも採用!
Slide 56
Slide 56 text
完全に理解しましたね?
Slide 57
Slide 57 text
No content
Slide 58
Slide 58 text
参考資料 @inamiyさん iOSDC Japan 2017で「Auto Layoutのアルゴリズム」について発 表しました https://qiita.com/inamiy/items/a6f73438b32896ffa81e AutoLayout Algorithm https://speakerdeck.com/inamiy/autolayout-algorithm
Slide 59
Slide 59 text
ConstraintLayoutを支える技術 名前だけでも覚えて帰ってね ● UIの解決 ≒ 線形計画問題 ● 線形計画問題の解法 ○ シンプレックス法(2段階、双対) ○ Cassowary ● Advanced ConstraintLayout https://academy.realm.io/posts/360-andev-2017-nicolas-ro ard-advanced-constraintlayout/
Slide 60
Slide 60 text
ConstraintLayoutの構成
Slide 61
Slide 61 text
ConstraintLayoutの構成 ● constraint-layout ○ 9クラス ○ Viewの簡単な実装がメイン ● constraint-layout-solver ○ 本体 ○ 20クラス ○ Androidに非依存 ○ 線形計画問題の最適解を求める
Slide 62
Slide 62 text
constraint-layout ● ConstraintLayout ● Constraints, ConstraintSet ● ConstraintHelper ○ Barrier, Group, PlaceHolder ● Guideline ● BuildConfig
Slide 63
Slide 63 text
constraint-layout-solver android.support.constraint.solver ● ArrayLinkedVariable ● ArrayRow ● GoalRow ● LinearSystem →線形計画問題の計算 ● SolverVariable
Slide 64
Slide 64 text
constraint-layout-solver android.support.constraint.solver.widgets ● ConstraintWidgetContainer (≒ ConstraintLayout) ● WidgetContainer (≒ ViewGroup) ● ConstraintWidget (≒ View) ● ConstarintAnchor (≒ Constraints + ConstraintSet)
Slide 65
Slide 65 text
constraint-layout-solver android.support.constraint.solver.widgets ● Helper = ConstrantHelper ○ Barrier, Group ● Guideline ● Chain
Slide 66
Slide 66 text
constraint-layout-solver android.support.constraint.solver.widgets ● Optimizer
Slide 67
Slide 67 text
ConstraintLayout#onMeasure
Slide 68
Slide 68 text
onMeasure概要 1. ConstraintWidgetで同じView構造を作る 2. ConstraintWidgetに値を反映 3. すべての子Viewをmeasure (引数はViewGroup#getChildMeasureSpec) + ConstraintWidgetにwidth/heightを反映 4. ※solver側でレイアウトする位置を確定する 5. solverからViewに値を戻す 6. setMeasuredDimensionsで自身のサイズを決定する
Slide 69
Slide 69 text
レイアウト位置の確定 1. ConstraintWidgetContainter#layout 2. 自身のサイズに依存する子Viewがある場合 ○ その子Viewを再度measure ○ その結果とlayoutの結果が合わない場合 ■ 再度ConstraintWidgetContainter#layout ■ ConstraintLayoutがminWidth/Heightより小さい場合 ● widgetのsizeを設定して 再度ConstraintWidgetContainter#layout
Slide 70
Slide 70 text
ConstraintWidgetContainer #layout
Slide 71
Slide 71 text
ConstraintWidgetContainer#layout 線形計画問題の最適解を求めて子ConstraintWidgetに反映 1. whileループ レイアウト位置を確定できるまで繰り返す 2. 自身の幅を設定 3. whileの中でいじった値を元に戻す ListDimensionBehaviors ≒ MeasureSpec.Mode 4. 子ConstraintWidgetのレイアウト位置を設定
Slide 72
Slide 72 text
#layout whileループ 解決できるまで以下のループを繰り返す 1. Optimizerを使えるか試す 使えない場合、線形計画問題の最適解を求める 2. 一旦解決済みとする
Slide 73
Slide 73 text
whileループ 3. ループが8回目未満 かつ 子Viewが制約のせいで小さくレイアウトされている かつ ConstraintLayoutがWrapContent かつ 子ViewのサイズがConstraintLayoutより大きい場合 ・子Viewのサイズ+WrapContentにして、もう1回ループ
Slide 74
Slide 74 text
whileループ 4. minWidth/Heightより小さい場合 ・widthとheightを minWidth/Heightの固定値にして、もう1回ループ
Slide 75
Slide 75 text
whileループ 5. 3.と4.に当てはまらず、WrapContent、 子Viewの変更がないのに 前回よりもwidth/heightが大きかった場合 ・measuredTooSmallフラグを立てる ・自身を前回のwidth/Heightの固定値にしてもう1回ループ ※恐らく、レイアウトの微調節をしていたら 自身のサイズが予定外に変更されてしまった場合。 元のサイズで再計算する。
Slide 76
Slide 76 text
ConstraintLayout#onLayout
Slide 77
Slide 77 text
onLayout概要 1. ConstraintHelper#updatePostLayout 2. すべての子ViewをConstraintWidgetの値通りにレイアウト ○ measureでConstraintWidgetに値が設定されている 3. 以上!
Slide 78
Slide 78 text
What is Optimizer?
Slide 79
Slide 79 text
Optimizer ● 前提:ConstraintLayoutは高速! ● とはいえシンプルなレイアウトには過剰な計算 ● シンプルなレイアウトはOptimizerで解決 ○ 線形問題の解決を行わない
Slide 80
Slide 80 text
Optimizerが発動する条件 ● Viewの位置が簡単に決定できること ○ 両端が親Viewと一緒+View幅が固定値 ○ 片端が親Viewと一緒+View幅が固定値 or WrapConent ● すべての辺が解決しないとOptimise失敗 ● すべてのViewが解決しないとOptimise失敗
Slide 81
Slide 81 text
ConstraintLayoutを 遅くさせないtips
Slide 82
Slide 82 text
遅くさせないtips ● ConstraintLayoutをwrap_contentにしない ● 幅が確定していないViewのpercentを使わない ○ 依存するVIewののサイズが決まらないと percentを使ったViewサイズを決めれない ● Optimizerを使えるレイアウトにする
Slide 83
Slide 83 text
以上!
Slide 84
Slide 84 text
付録 ● RelativeLayoutの内部実装 https://speakerdeck.com/seto_hi/xiang-jie-relativelayoutfalsenei-bu-shi-zhuang ● ConstraintLayoutの機能実現 https://speakerdeck.com/seto_hi/constraintlayoutfalseji-neng-falseshi-xian ● ノハナ社のレイアウト戦略 https://speakerdeck.com/seto_hi/falsehanashe-falsereiautozhan-lue