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

Textの構造を理解する/Understanding the Structure of Text

Textの構造を理解する/Understanding the Structure of Text

Kakeru Nakabachi

November 29, 2021
Tweet

More Decks by Kakeru Nakabachi

Other Decks in Programming

Transcript

  1. Textの構造を理解する
    Flutter Kaigi 2021 November 29-30, 2021

    View Slide

  2. Kakeru Nakabachi - batch
    CyberAgent, Inc
    株式会社WinTicketでモバイルアプリをFlutterでリ
    プレイス中
    About me
    2

    View Slide

  3. 本セッションのゴール
    Textの構造を理解して実装できるようになる💪💪
    3

    View Slide

  4. アジェンダ
    ● Text
    ● Textの構造
    ● AndroidとiOSでTextを描画したときの問題について
    ● チームで対応した問題の対処法
    ● まとめ
    4

    View Slide

  5. Text
    5

    View Slide

  6. Text
    文字を表示するためのWidget
    ● プロパティたち
    ○ maxLines
    ○ overflow
    ○ strutStyle
    ○ style
    ○ textAlign
    ○ textHeightBehavior
    ○ ...
    https://api.flutter.dev/flutter/widgets/Text-class.html 6

    View Slide

  7. Text
    文字を表示するためのWidget
    ● プロパティたち
    ○ maxLines
    ○ overflow
    ○ strutStyle
    ○ style
    ○ textAlign
    ○ textHeightBehavior
    ○ ...
    https://api.flutter.dev/flutter/widgets/Text-class.html
    debugLabel
    fontFamily
    fontFamilyFallback
    fontSize
    fontStyle
    fontWeight
    forceStrutheight
    hashCode
    height
    leading
    leadingDistribution
    7
    TextStyleのプロパティ

    View Slide

  8. Text
    8
    https://api.flutter.dev/flutter/painting/TextStyle-class.html

    View Slide

  9. Text
    9
    https://api.flutter.dev/flutter/painting/TextStyle-class.html

    View Slide

  10. Text
    文字を表示するためのWidget
    ● maxLines
    ● overflow
    ● strutStyle
    ● style
    ● textAlign
    ● textHeightBehavior
    ● ...
    https://api.flutter.dev/flutter/widgets/Text-class.html
    10

    View Slide

  11. Text
    文字を表示するためのWidget
    ● maxLines
    ● overflow
    ● strutStyle
    ● style
    ● textAlign
    ● textHeightBehavior
    ● ...
    https://api.flutter.dev/flutter/widgets/Text-class.html
    11

    View Slide

  12. Text
    文字を表示するためのWidget
    ● maxLines
    ● overflow
    ● strutStyle
    ● style
    ● textAlign
    ● textHeightBehavior
    ● ...
    https://api.flutter.dev/flutter/widgets/Text-class.html
    12

    View Slide

  13. Textの構造
    13

    View Slide

  14. Textの構造
    このようなTextを表示した場合、どのような構造でTextが構築されて表示されて
    いるかをみていく
    Flutterのvisual debuggingツールを有効化
    https://flutter.dev/docs/testing/debugging#visual-debugging
    https://docs.flutter.dev/development/tools/devtools/inspector
    14

    View Slide

  15. Textの構造
    実行結果 Android
    https://api.flutter.dev/flutter/painting/TextStyle-cl
    ass.html
    Textが確保している領域
    15

    View Slide

  16. Textの構造
    実行結果 Android
    https://api.flutter.dev/flutter/painting/TextStyle-cl
    ass.html
    Textのbaseline
    16

    View Slide

  17. それぞれの線の意味
    baseline
    ascent: baselineから上部までの高さ
    descent: baselineから下部までの高さ
    leading: ある行の下端から次の行の上端までの高さ、TextStyle.height *
    TextStyle.fontSize - TextStyle.fontSize
    これらを意識してTextをつくっていく。今回は以下の3つにフォーカスする
    height、leadingDistribution、textHeightBehavior
    Textの構造
    AaBbGgJj
    17

    View Slide

  18. Textの構造
    ● height
    行の高さをフォントサイズの倍数で調整で
    きる
    nullと1.0は別の意味
    18
    https://api.flutter.dev/flutter/painting/TextStyle-class.html

    View Slide

  19. Textの構造
    ● leadingDistribution
    leadingを上下にどのように配置するかを決める。
    asent
    descent
    Top leading
    Bottom leading
    19

    View Slide

  20. Textの構造
    ● leadingDistribution
    TextLeadingDistribution.proportionalと.evenがある
    leadingを上下にどのように配置するかを決める。nullの場合は
    TextLeadingDistribution.proportionalと同じ挙動
    20

    View Slide

  21. Textの構造
    ● leadingDistribution
    leadingを上下にどのように配置するかを決める。
    TextLeadingDistribution.evenにした場合はascentとdescentから上下に等
    しくleadingが設定される
    21

    View Slide

  22. Textの構造
    ● textHeightBehavior
    applyHeightToFirstAscentとapplyHeightToLastDescentのbool値を設定す
    ることでTextの上下のleadingを除くことができる
    デフォルトはtrueでleadingあり
    falseを設定してleadingを除いた場合、textHeightBehaviorは効かなくなる
    22

    View Slide

  23. Textの構造
    ● heightとleadingDistributionとtextHeightBehavior
    Configuration 1
    fontSizeとheight以外はすべてデフォル
    ト設定
    leadingDistributionは
    TextLeadingDistribution.proportional
    23

    View Slide

  24. Textの構造
    ● heightとleadingDistributionとtextHeightBehavior
    Configuration 2
    Configuration 1に
    TextHeightBehavior.applyHeightToFir
    stAscentをfalseに設定
    24

    View Slide

  25. Textの構造
    ● heightとleadingDistributionとtextHeightBehavior
    Configuration 3
    leadingDistributionを
    TextLeadingDistribution.evenに設定
    25

    View Slide

  26. Textの構造
    ● heightとleadingDistributionとtextHeightBehavior
    Configuration 4
    Configuration 3の設定に
    TextHeightBehavior.applyHeightToLa
    stDescentにfalseを追加で設定したとき
    26

    View Slide

  27. Textの構造
    ● 複数行の場合
    27

    View Slide

  28. Textの構造
    ● 複数行の場合
    height: 2.5を追加
    28

    View Slide

  29. Textの構造
    ● 複数行の場合
    TextHeightBehavior.applyHeightToFirstAscent: falseを追加
    29

    View Slide

  30. Textの構造
    ● 複数行の場合
    TextHeightBehavior.applyHeightToLastDescent: falseを追加
    30

    View Slide

  31. Textの構造
    ● 複数行の場合
    leadingDistributionにTextLeadingDistribution.proportionalの設定
    31

    View Slide

  32. Textの構造
    ● 複数行の場合
    leadingDistributionにTextLeadingDistribution.evenの設定
    32

    View Slide

  33. Textの構造
    ● 複数行の場合
    TextStyleのleadingDistributionとTextHeightBehaviorの
    leadingDistribution
    TextStyle側が優先されて使われる
    33

    View Slide

  34. Textの構造
    ● 複数行の場合
    TextStyleのleadingDistributionとTextHeightBehaviorの
    leadingDistribution
    TextStyle側が優先されて使われる
    34

    View Slide

  35. AndroidとiOSでTextを描画したときの
    問題について
    35

    View Slide

  36. 問題1:OSごとにTextのサイズが違う
    ● 実はAndroidとiOSでは同じフォントサイズなどstyleの設定でもTextの高さ
    が異なる
    ➢ 高さ固定のWidgetの中にTextなど配置した場合に、片方のOSでは収ま
    るけど、もう一方では収まらなかったり、OS間で異なったデザインにな

    両OSのデザインがある場合は大丈夫
    弊チームではiOSのデザインしかなくどのように対応するか考える必要があった
    36

    View Slide

  37. 問題1:OSごとにTextのサイズが違う
    実はiOSとAndroidでは同じフォントサイズなどの設定でもTextの高さが異なる
    Android
    37

    View Slide

  38. 問題1:OSごとにTextのサイズが違う
    実はiOSとAndroidでは同じフォントサイズなどの設定でもTextの高さが異なる
    iOS
    38

    View Slide

  39. 問題1:OSごとにTextのサイズが違う
    なぜ起こる?
    OSによってleadingの高さが違う、、?
    →TextHeightBehaviorを設定して上下のleadingをなくしたらどうなるか
    AaBbGgJj
    39

    View Slide

  40. 問題1:OSごとにTextのサイズが違う
    TextHeightBehaviorを設定してみる
    Android
    40

    View Slide

  41. 問題1:OSごとにTextのサイズが違う
    TextHeightBehaviorを設定してみる
    iOS
    41

    View Slide

  42. 問題1:OSごとにTextのサイズが違う
    AndroidとiOSでTextHeightBehaviorの設定の挙動が違いそう?バグ、、?
    Android
    iOS
    42

    View Slide

  43. 問題1:OSごとにTextのサイズが違う
    結論:fontFamilyを指定しなかった場合、OSごとに読み込まれるフォントが異
    なり、ascentとdescentが異なるため
    Android
    Pixel 4a
    和文フォント:Noto Sans Japanese?
    欧文フォント:Roboto
    iOS
    iPhone 12 Pro
    和文フォント:ヒラギノ角ゴシック
    欧文フォント:San Francisco Pro 43

    View Slide

  44. 問題1:OSごとにTextのサイズが違う
    TextStyle.backgroundColorを設定して、Textの中でフォントが確保している領域
    を見てみる。和文フォントで大きく確保する領域に違いがある
    Android
    iOS
    44

    View Slide

  45. 問題1:OSごとにTextのサイズが違う
    TextStyle.backgroundColorを設定して、Textの中でフォントが確保している領域
    を見てみる。和文フォントで大きく確保する領域に違いがある
    Android
    iOS
    Androidのフォントが確保している領域が
    iOSより広い
    45

    View Slide

  46. 問題2:Androidだと文字が下寄りに見える
    https://github.com/flutter/flutter/issues/79931 46

    View Slide

  47. 問題2:Androidだと文字が下寄りに見える
    Android iOS
    24.0
    32.0
    fontSize
    47

    View Slide

  48. 問題2:Androidだと文字が下寄りに見える
    Androidの方がフォントがデフォルトで確保している
    ascentとdescentが大きく、ascentの方が大きいので
    下寄りに見える
    Android iOS
    48

    View Slide

  49. 問題2:Androidだと文字が下寄りに見える
    Androidの方がフォントがデフォルトで確保しているascentと
    descentが大きく、ascentの方が大きいので下寄りに
    見える
    Android iOS
    49

    View Slide

  50. チームで対応した問題の対処法
    50

    View Slide

  51. チームで対応した問題の対処法
    ■ 一般的な対処法と思われるもの
    ● AndroidとiOSで共通のascentとdescentが小さいフォントを使う
    ● AndroidだけiOSのシステムフォントのようなascentとdescentが小さい
    フォントを使う
    51

    View Slide

  52. チームで対応した問題の対処法
    ■ 一般的な対処法と思われるもの
    ● AndroidとiOSで共通のascentとdescentが小さいフォントを使う
    ● AndroidだけiOSのシステムフォントのようなascentとdescentが小さい
    フォントを使う
    ➢ 弊チームでの対応はこれ
    52

    View Slide

  53. チームで対応した問題の対処法
    ■ AndroidでiOSのシステムフォントのようなascentとdescentが小さいフォン
    トを使うためには
    Androidで角ゴシックを使えば両プラットフォームで全く同じTextにすることが
    できるが、、、
    ➢ Androidユーザは普段と違うフォントを目にすることになって違和感か
    もしれない
    ➢ ライセンス問題(
    https://developer.apple.com/forums/thread/89094 )
    53

    View Slide

  54. チームで対応した問題の対処法
    ■ Android用にascentとdescentが小さいフォントを用意する必要がある
    ➢ NotoSans CJKを使用
    CJK = Chinese Japanese Korean
    今はJapaneseのみが入ったファイルサイズの小さいNotoSansJapaneseがあ

    https://fonts.google.com/noto/specimen/Noto+Sans+JP
    54

    View Slide

  55. チームで対応した問題の対処法
    ■ Android用にascentとdescentが小さいフォントを用意する必要がある
    NotoSans CJKのままでは、中国語や韓国語など使わないフォントも多いので、サ
    ブセット化してフォントファイルサイズを削減
    .otf → .woff → .ttf → サブセット化(JIS第1水準+JIS第2水準+その他)
    NotoSans CJK RegularとNotoSans CJK Boldを用意
    サブセット化前 サブセット化後
    34MB 3MB
    55

    View Slide

  56. チームで対応した問題の対処法
    ■ Android用にascentとdescentが小さいフォントを用意する必要がある
    コンバートする段階でascentとdescentが除かれたフォントができあがることを
    利用してiOSのフォントのような扱いをしている
    https://github.com/minoryorg/Noto-Sans-CJK-JP
    ライセンス的には商用利用可、改変可
    https://fonts.google.com/noto/use#bundle-noto-fonts-with-your-native-app
    https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
    56

    View Slide

  57. チームで対応した問題の対処法
    ■ Android用にascentとdescentが小さいフォントを用意する必要がある
    フォントの指定方法
    なにも指定がない場合、OSのデフォルトのシステムフォントが使われる
    57
    ← 最初に読み込もうとするフォントを指定
    ← fontFamilyで対応できなかった文字を描画す
    るためのフォントを指定

    View Slide

  58. チームで対応した問題の対処法
    ■ Android用にascentとdescentが小さいフォントを用意する必要がある
    フォントの指定方法
    用意したフォントは日本語表示用に、英語はシステムフォントでデフォルトで選
    択されるRobotoを使いたい
    fontFamilyに何も指定しない場合→Robotoが使われる
    日本語を表示しようとしたとき、Robotoだけでは対応できずfontFamilyFallback
    に設定されているフォントを読み込もうとする
    なので、今回は用意したフォントをfontFamilyFallbackに設定
    58

    View Slide

  59. チームで対応した問題の対処法
    ■ ボタンなどの垂直方向の中央に文字が表示されるようにleadingの調整
    ascentとdescentが小さいフォントを使うことにより、 デフォルトのleadingが効
    くようになり、文字が厳密にはまだ中心に配置されていない
    TextStyle.leadingDistributionにTextLeadingDistribution.evenを設定して、上
    下に等しくleadingを配置して文字が垂直方向に中心に配置されるようにする
    59
    フォント変更前 フォント変更後

    View Slide

  60. まとめ
    ● Textは様々な要素で構成されている
    ● AndroidとiOSではフォントを指定しない場合、読み込むシステムフォントが
    それぞれ違うため、全く同じ見た目のTextを描画しない
    ○ 高さが違う
    ○ 文字が下よりに見える
    ● 対応方法の1つにAndroidのときはデフォルトでasecntとdescentが大きいシ
    ステムフォントが使われるため、それらが小さいフォントを指定してiOSの
    挙動に近づける
    ● TextStyle.leadingDistributionにTextLeadingDistribution.evenを設定して
    上下のleadingを均等に配分して文字を中央にする 60

    View Slide

  61. 参考
    ● https://flutter.dev/docs/testing/debugging#visual-debugging
    ● https://flutter.dev/docs/development/tools/devtools/inspector
    ● https://stackoverflow.com/questions/56799068/what-is-the-strutstyl
    e-in-the-flutter-text-widget
    ● https://api.flutter.dev/flutter/painting/TextStyle-class.html
    ● https://github.com/flutter/flutter/issues/79931
    ● https://fonts.google.com/noto/use#bundle-noto-fonts-with-your-nativ
    e-app
    ● https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
    ● https://github.com/minoryorg/Noto-Sans-CJK-JP
    61

    View Slide

  62. Thank you
    62

    View Slide