Upgrade to Pro — share decks privately, control downloads, hide ads and more …

詳解 ViewGroupのレイアウト内部実装

HiroYUKI Seto
February 09, 2018

詳解 ViewGroupのレイアウト内部実装

2018/2/9
DroidKaigi 2018 DAY02 room1 10:30~

付録1: RelativeLayoutの内部実装
https://speakerdeck.com/seto_hi/xiang-jie-relativelayoutfalsenei-bu-shi-zhuang
付録2: ConstraintLayoutの機能実現
https://speakerdeck.com/seto_hi/constraintlayoutfalseji-neng-falseshi-xian
付録3: ノハナ社のレイアウト戦略
https://speakerdeck.com/seto_hi/falsehanashe-falsereiautozhan-lue

HiroYUKI Seto

February 09, 2018
Tweet

More Decks by HiroYUKI Seto

Other Decks in Technology

Transcript

  1. 自己紹介 • 瀬戸優之 Seto HiroYUKI @seto_hi • Androidエンジニア & アプリデザイン

    • 株式会社ノハナ ◦ 2年連続DroidKaigiスポンサー! ◦ 一組でも多くの家族に笑顔を届ける ◦ 絶賛採用中 • Material Design大好き • 好きなAPIはCanvas#saveとViewGroup#layout
  2. おしながき • Viewってどうやってレイアウトされているの? ◦ measure, layout • 詳解FrameLayoutの内部実装 • 詳解LinearLayoutの内部実装

    • 詳解ConstraintLayoutの内部実装 ◦ ConstraintLayoutを支える技術 ◦ Optimizer ◦ 各機能の実現→付録にあります • 付録
  3. おしながき • レイアウトの流れ • measure ◦ measure, onMeasure ◦ MeasureSpec

    ◦ measuredWidth, measuredHeight • layout ◦ layout, onLayout
  4. View View Group View Gruop View Group View View request

     Layout Handler Traversal Runnable View Root Impl request  Layout request  Layout
  5. View View Group View Gruop View Group View View measure

    View Root Impl measure measure measure measure measure Handler Traversal Runnable
  6. View View Group View Gruop View Group View View layout

    View Root Impl layout layout layout layout layout
  7. measuredWidth, measuredHeight • onLayoutで使う • setMeasureDimensionsで設定した値※ • View#getWidth/getHeightはlayout後の値 ◦ View.mLeft-View.mRight

    • layout以降は使ってはならない ◦ measuredWidth/Height通りにlayoutされるとは限らない ◦ layout以降はgetWidth/Heightを使う ※ 厳密にはMeasureSpec
  8. MeasureSpec • onMesureの引数 • measureの条件(Mode)とSizeをintで表現したもの ◦ Modeが上位2byte、Sizeが下位30bit Mode 意味 UNSPECIFIED

    Viewの好きなサイズにして良い EXACTLY 指定されたサイズにすべき AT_MOST Viewの好きなサイズにして良い でも指定されたサイズは超さないように
  9. 線形計画問題 商品Xをx、商品Yをyとする x + 3y < 90 2x + y

    < 50 x ≧ 0, y ≧ 0 のとき 60x + 40yの最大値を求める
  10. 図 線形計画問題 x + 3y < 90 2x + y

    < 50 (12, 26) 40x + 60y = 2040
  11. 図 シンプレックス法 x + 3y < 90 2x + y

    < 50 (12, 26) 40x + 60y = 2040 ①初期基底解(BFS) ②
  12. Cassowary • ヒクイドリ - 世界一危険な鳥 • 線形方程式、線形不等式の 制約充足問題(constraint solving problem)の解法

    ◦ 2段階+双対シンプレックス法を使う • アプリのUI用に最適化されている ◦ 高速 ◦ 省メモリ ◦ 宣言的 ◦ 差分更新可能
  13. Cassowary • 2001年に開発された ◦ 最初はCSSのレイアウト拡張として • 多くの言語に移植されている ◦ Smalltalk, C++,

    Java, JavaScript, Dart, Python etc.. • 2011年からMac(OS X)やiOS(6~)で使われている ◦ AutoLayout • ConstraintLayoutでも採用!
  14. ConstraintLayoutを支える技術 名前だけでも覚えて帰ってね • UIの解決 ≒ 線形計画問題 • 線形計画問題の解法 ◦ シンプレックス法(2段階、双対)

    ◦ Cassowary • Advanced ConstraintLayout https://academy.realm.io/posts/360-andev-2017-nicolas-ro ard-advanced-constraintlayout/
  15. ConstraintLayoutの構成 • constraint-layout ◦ 9クラス ◦ Viewの簡単な実装がメイン • constraint-layout-solver ◦

    本体 ◦ 20クラス ◦ Androidに非依存 ◦ 線形計画問題の最適解を求める
  16. onMeasure概要 1. ConstraintWidgetで同じView構造を作る 2. ConstraintWidgetに値を反映 3. すべての子Viewをmeasure (引数はViewGroup#getChildMeasureSpec) + ConstraintWidgetにwidth/heightを反映

    4. ※solver側でレイアウトする位置を確定する 5. solverからViewに値を戻す 6. setMeasuredDimensionsで自身のサイズを決定する
  17. レイアウト位置の確定 1. ConstraintWidgetContainter#layout 2. 自身のサイズに依存する子Viewがある場合 ◦ その子Viewを再度measure ◦ その結果とlayoutの結果が合わない場合 ▪

    再度ConstraintWidgetContainter#layout ▪ ConstraintLayoutがminWidth/Heightより小さい場合 • widgetのsizeを設定して 再度ConstraintWidgetContainter#layout
  18. whileループ 3. ループが8回目未満 かつ   子Viewが制約のせいで小さくレイアウトされている かつ   ConstraintLayoutがWrapContent かつ

      子ViewのサイズがConstraintLayoutより大きい場合   ・子Viewのサイズ+WrapContentにして、もう1回ループ
  19. whileループ 5. 3.と4.に当てはまらず、WrapContent、   子Viewの変更がないのに   前回よりもwidth/heightが大きかった場合   ・measuredTooSmallフラグを立てる  

    ・自身を前回のwidth/Heightの固定値にしてもう1回ループ ※恐らく、レイアウトの微調節をしていたら  自身のサイズが予定外に変更されてしまった場合。  元のサイズで再計算する。