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