Compose v1.3.0 で追加された LineBreak 調べてみました

こんにちは!Jetpack Compose と KMM が好きなエンジニアのスージです。

Jetpack Compose v1.3.0 から改行方法を指定できる新しいAPI @ExperimentalTextApi LineBreak が追加されています。この記事では LineBreak に渡すパラメータによって改行方法がどのように異なるのかについて調べてみました。

android-developers.googleblog.com

文章によって結果が変わるので、あくまで一例としてご覧ください。🙇‍♂️

LineBreak

v1.3.0 からTextStyleでは lineBreak のパラメータが追加されました。

/**
 * ..
 * @param lineBreak The line breaking configuration for the text.
 */
@Immutable
class TextStyle internal constructor(
    ..
    lineBreak: LineBreak? = null,
)

このLineBreak クラスに3つのパラメータを渡します。

/**
 * ..
 * @param strategy defines the algorithm that inserts line breaks
 * @param strictness defines the line breaking rules
 * @param wordBreak defines how words are broken
 */
@ExperimentalTextApi
@Immutable
actual class LineBreak(
    val strategy: Strategy,
    val strictness: Strictness,
    val wordBreak: WordBreak
) 

Strategy

LineBreak.Strategyは改行する方針を決めるパラメータです。3つのタイプがあります。

  • Simple:処理が一番早い方針です。
  • Balanced:ハイフンを含めて、文章の各行の長さをなるべくバランス良くする方針です。
  • HighQuality:言葉自体をなるべく改行しない方針です。

日本語の場合LineBreak.Strategyの影響

Strategy.Balancedの場合言葉を改行しても、ずっと各行の長さが同じで綺麗に見えますね。

同じく v1.3.0 で追加されたHyphenation APIを有効にすれば、ハイフンを含めて改行の調整を行います。英語みたいなハイフンで言葉を改行できる言語しか影響しないです

英語の場合LineBreak.Strategyの影響

Strictness

LineBreak.Strictnessは改行のアルゴリズムを決めるパラメータです。 LooseNormalStrict ではそれぞれの名前通りの厳密性で、どの文字のあと改行できるかを決定します。ドキュメンテーションでも書いてありますが、CJKフォントに影響がありそうです。

  • Loose : 例えば、「々」の前にも改行します。一番低い厳密性で改行してしまうのでタイトルみたい短いテキストに使うのがおすすめです。
  • Normal:日本語の場合「ぁ」「ィ」の前にも改行する。
  • Strict:日本語の場合「々」と「ぁ」「ィ」の前に改行しない。一番高い厳密性なので処理もより重くなります。

日本語の場合 LineBreak.Strictness の影響

英語の場合は Strictness は影響しなさそうですね。

英語の場合 LineBreak.Strictness の影響

WordBreak

LineBreak.WordBreakは単語を改行する方針を決めるパラメータです。2つのタイプがあります。 - Default:言語のデフォルトの単語の改行方法です。日本語みたいな単語の間にスペースがない言語では言葉の中でも改行を入れます。 - Phrase:なるべくフレーズを改行させないようにします。

日本語の場合 LineBreak.WordBreak の影響

途中で「色々美味しいもの食べましょう」はWordBreak.Defaultの場合「色々美味しいもの食べ」「ましょう」に改行されてますが、WordBreak.Phraseの場合は「美味しいもの」「食べましょう」に改行されてますね。

WordBreak.Default と WordBreak.Phrase の違い
WordBreak.DefaultWordBreak.Phrase の改行の違い

良い事例思いつかなかったのですが、英語の場合 Default と Phrase は同じでした。

英語の場合 LineBreak.WordBreak の影響

プリセット

LineBreak APIでアプリでよく使う3タイプのユースケースのプリセットも用意されてます。

LineBreak.Simple

高頻度で更新されるテキスト(TextField など)のための、一番早いアルゴリズムで処理させる用の LineBreak です。

/**
  * The greedy, fast line breaking algorithm. Ideal for text that updates often,
  * such as a text editor, as the text will reflow minimally.
  ..
*/ 
actual val Simple: LineBreak = LineBreak(
    strategy = Strategy.Simple,
    strictness = Strictness.Normal,
    wordBreak = WordBreak.Default
)
  • Strategy.Simple:処理スピードが一番早い
  • Strictness.Normal:可読性を守りつつ処理が一番早い
  • WordBreak.Default:言語デフォルト

LineBreak.Heading

あまり改行しないテキスト(タイトルなど)のための LineBreak です。

/**
  * Balanced line lengths, hyphenation, and phrase-based breaking.
  * Suitable for short text such as titles or narrow newspaper columns.
  ..
*/ 
actual val Heading: LineBreak = LineBreak(
    strategy = Strategy.Balanced,
    strictness = Strictness.Loose,
    wordBreak = WordBreak.Phrase
)
  • Strategy.Balanced:長さを同じようにして綺麗に見せる
  • Strictness.Loose:一番低い厳密性
  • WordBreak.Phrase:フレーズ毎改行する方針

LineBreak.Paragraph

処理時間もかかり、可読性が優先される文章(説明、長文など)のための LineBreak です。

/**
  * Slower, higher quality line breaking for improved readability.
  * Suitable for larger amounts of text.
  ..
*/   
actual val Paragraph: LineBreak = LineBreak(
    strategy = Strategy.HighQuality,
    strictness = Strictness.Strict,
    wordBreak = WordBreak.Default
)
  • Strictness.Strict:高い厳密性
  • Strategy.HighQuality:言葉自体をなるべく改行しない方針

日本語の場合は単語間にスペースがないので、WordBreak.Default より WordBreak.Phrase を使った方が良いかもしれないです。なので、日本語の場合はWordBreak.Phraseを使った方がいいと思いました。

lineBreak = LineBreak.Paragraph.copy(wordBreak = WordBreak.Phrase)

おわりに

今回調べてみた LineBreak API は元々View.java 時代から存在してるものなのですが、Compose の読みやすさと早く検討できるおかげでより良い理解ができたかなと思いました。