Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
複数行のTextで中間省略(…)を実現する
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
kobaken
February 19, 2026
Programming
0
30
複数行のTextで中間省略(…)を実現する
2026年2月12日(木)開催のpixiv App Talkで発表した資料です。
kobaken
February 19, 2026
Tweet
Share
More Decks by kobaken
See All by kobaken
Jetpack Compose Preview実践ガイド
kobaken0029
0
96
Serializable / Parcelableとの上手な付き合い方
kobaken0029
0
90
Kotlinの好きなところ
kobaken0029
0
1.2k
Compose駆動開発のためのマルチモジュール化
kobaken0029
0
240
DataStoreを導入してみた
kobaken0029
1
350
Epoxyを用いたレイアウト構築術
kobaken0029
1
240
Androidエンジニアが1週間でiOSアプリ開発を学び、1ヶ月で大規模アプリ開発にJOINした話
kobaken0029
0
3.5k
Modern REST Communicate for Android
kobaken0029
0
1.6k
AndroidでモダンREST通信してみたった
kobaken0029
0
260
Other Decks in Programming
See All in Programming
Agent Skills Workshop - AIへの頼み方を仕組み化する
gotalab555
13
7.3k
AI巻き込み型コードレビューのススメ
nealle
2
2.4k
「ブロックテーマでは再現できない」は本当か?
inc2734
0
1.1k
RubyとGoでゼロから作る証券システム: 高信頼性が求められるシステムのコードの外側にある設計と運用のリアル
free_world21
0
120
15年目のiOSアプリを1から作り直す技術
teakun
0
570
PostgreSQL を使った快適な go test 環境を求めて
otakakot
0
370
Claude Codeセッション現状確認 2026福岡 / fukuoka-aicoding-00-beacon
monochromegane
3
350
あなたはユーザーではない #PdENight
kajitack
4
290
受け入れテスト駆動開発(ATDD)×AI駆動開発 AI時代のATDDの取り組み方を考える
kztakasaki
2
490
「やめとこ」がなくなった — 1月にZennを始めて22本書いた AI共創開発のリアル
atani14
0
330
Rails Girls Tokyo 18th GMO Pepabo Sponsor Talk
yutokyokutyo
0
180
Amazon Bedrockを活用したRAGの品質管理パイプライン構築
tosuri13
5
900
Featured
See All Featured
Making Projects Easy
brettharned
120
6.6k
Marketing to machines
jonoalderson
1
5k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
jQuery: Nuts, Bolts and Bling
dougneiner
65
8.4k
Why Our Code Smells
bkeepers
PRO
340
58k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3k
A designer walks into a library…
pauljervisheath
210
24k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.8k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.3k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.1k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
1
1.3k
Transcript
複数行のTextで中間省略(...)を実現する @kobaken
自己紹介 • kobaken / こばけん / @koba_dog_ ◦ 入社9年目 ◦
Comic Division / pixivコミックSection / プロダクト開発Unit / 開発Team • Android / Kotlin / マンガ / ゲーム / YouTube 2
本日のお品書き • なぜ中間省略(...)が必要なのか? • 実装アプローチ • 考慮すべきポイント • アルゴリズムと実装 •
まとめ 3
なぜ中間省略(...)が必要なのか? 4
なぜ中間省略(...)が必要なのか? • 「最初」と「最後」に重要な情報がある場合 ◦ 例1:長いファイルパス(/data/user/0/com.example.../files/config.json) ◦ 例2:マンガのタイトル( 悪役令嬢の中の人~断罪された転生...します~:3【イラスト特典付】 ) •
これを末尾省略(...)にすると一番知りたい情報が見えなくなる ◦ 例1:長いファイルパス(/data/user/0/com.example.android/files/con...) ▪ 🤦ファイル名がわからない ◦ 例2:マンガのタイトル( 悪役令嬢の中の人~断罪された転生者のため嘘つきヒロイ...) ▪ 🤦巻数がわからない 5
要件を定義する ~今回やりたいこと~ • Text の中間付近を省略する • 複数行表示に対応 • 省略記号の挿入位置は最終行の1行前の末尾 6
「赤毛の役立たず」とクビになっ た魔力なしの魔女ですが、「薬草 の知識がハンパない!」と王立研 究所に即採用されました。【電子 限定おまけ付き】5巻 長いテキスト 中間省略の表示(最大2行) 「赤毛の役立たず」とクビにな 限定おまけ付き】5巻 中間省略を適用 … 最終行の手前で省略 末尾にある巻数情 報
しかし、標準機能にはとある制限が... 7
標準機能では「複数行+中間省略」はできない • TextView の android:ellipsize="middle" は maxLines="1" が必須 • Compose
の TextOverflow.MiddleEllipsis も同様 • 複数行(maxLines > 1)に設定した瞬間、中間省略は無視されてしまう 8 🚨今回の要件を満たすための実装が必要🚨
実装アプローチ 9
TextLayoutResult の紹介 • テキストの計測や配置計算が完了した後のレイアウト情報を保持するクラス ◦ 全体のサイズ、行数、各行の座標やベースラインなど • 文字のオフセットや座標、行の境界といった詳細な情報にもアクセスできる ◦ getLineStart,
getLineEnd, getBoundingBox など 利用用途 • カスタム描画やテキスト選択、クリック位置の判定などの制御に利用される ◦ 今回は主にカスタム描画と中間省略のロジックで使います 取得方法 • Textコンポーザブルの onTextLayout コールバックや TextMeasurer.measure の戻り値 10
どう実現する?実現アプローチを検討してみる • TextMeasurer から TextLayoutResult を取得してテキスト情報を得る ◦ 描画される前にCanvasへの描画をシミュレートする ◦ lineCount
や getLineStart / getLineEnd を駆使して中間省略ロジックを実装する ◦ 中間処理後の TextLayoutResult を使って Canvas へテキストを描画する • 省略ロジックでは TextUtils.ellipsize を使わない ◦ TextUtils.ellipsize は TextView 時代から活用されているUtil関数 ◦ これまでpixivコミックでは TextView による複数行の中間省略では TextUtils.ellipsize が採用されていた ▪ (最大行数) * (1行あたりの横幅) を合計幅とした擬似的な1行のテキストと見立てていた ◦ 改行位置が考慮されないため稀に意図しない挙動になる場合がある ◦ TextLayoutResult は行ごとに情報が取得できるため、今回は自前で省略ロジックを組み立ていく 11
考慮すべきポイント 12
注意点とハマりどころ(パフォーマンス) • フェーズを理解して無駄な計算や描画が発生しないようにする 13
Jetpack Compose のフェーズ • Compose がデータをUIに変換するまで、3つのフェーズが存在する 1. Compositionフェーズ 2. Layoutフェーズ
3. Drawingフェーズ • 今回は Layoutフェーズと Drawingフェーズに注目していく ◦ どのくらいのサイズでどこに配置するのかを決定する → Layoutフェーズ ◦ 前フェーズで決定したUIを画面に描画 → Drawingフェーズ 14
注意点とハマりどころ(パフォーマンス) • フェーズを理解して無駄な計算や描画が発生しないようにする ◦ 2つのフェーズをまたいで必要なものは再計算させないように意識する 15
注意点とハマりどころ(パフォーマンス) • フェーズを理解して無駄な計算や描画が発生しないようにする ◦ 2つのフェーズをまたいで必要なものは再計算させないように意識する • SubcomposeLayout (BoxWithConstraints) は利用しない ◦
Layoutフェーズで Composition する特殊なレイアウト ▪ 普通のレイアウトと比べるとオーバーヘッドあり ◦ 子要素をもとに他の要素を動的に配置したり変更したりする場合に便利 ◦ Text の描画領域を決定するためのサイズ情報 (Constraints) が欲しい ▪ BoxWithConstraints を使うと Constraints 生成に必要な maxWidth などが簡単に取得できるが... ◦ Layout コンポーザブルで代替可能 16
注意点とハマりどころ(アクセシビリティ) • スクリーンリーダーが省略(...)された文字列を読んでしまう ◦ pixivコミックでは文字+表紙でどの作品の何巻なのかを判別できる ◦ 仮に省略されたタイトルに巻数が表記されていないパターンがあっても、表紙から情報を得られる ◦ 視覚情報なしで判別できるように、省略前の全文を読ませるようにするべき 17
アルゴリズムと実装 18
実装アルゴリズム(1/2) 19 1. テキストを TextMeasurer で計測 して TextLayoutResult を取得 2.
省略が必要かどうか判定する 3. 中間省略されたテキストを生成 (※2/2を参照) 4. 省略後のテキストで再計測
実装アルゴリズム(2/2) 20 • 省略する行のindexを算出 • 省略行より前の文字列①を取得する • 省略行を加工して文字列②を取得する ◦ 任意の文字を(...)に置換する
◦ 今回は末尾の1文字を対象にする • 最終行の文字列③を取得する • ①+②+③
パフォーマンスのその先へ • Layoutコンポーザブルを使用する • Modifier で Canvas へ描画する • MeasurePolicy
で Layoutフェーズに実施される処理を実装 ◦ 中間省略のための TextLayoutResult の計算はここで実行する 21
コンポーネント定義と準備 22 • rememberTextMeasurer から TextMeasurer のインスタンスを取得 • 計測時にズレが生じないように文字間 隔を0にしておく
• TextLayoutResult をキャッシュ機構 を準備 ◦ Layoutフェーズで行われる計算結果を保持し ておき Drawingフェーズで再計算されないよ うにしておく
MeasurePolicy を実装する 23 • Layoutフェーズで中間省略を実行 ◦ キャッシュがあればそのまま返却 ◦ 計測結果をキャッシュしておく •
呼び出し元からもレイアウト結果を確 認できるようにする ◦ Text コンポーネントを踏襲 • layout でサイズを決定
Layoutコンポーネントによる描画 24 • TextLayoutResult から Canvas に文 字列を描画 ◦ canvas
に対して multiParagraph.paint する ことで複数行テキストの描画が実現 • Modifier.semantics に加工前の全文 を渡す ◦ 省略後の文字列が TalkBack などで読まれない ように対策する
実際に実行してみる 25
26 デフォルト実装の場合(maxLines=2) 😢
27 複数行に対応した実装の場合(maxLines=2)
まとめ 28
まとめ • 長い文章の先頭と末尾に重要な情報がある場合は中間省略を検討してみよう ◦ ファイルパス、単行本のタイトルなど • 標準機能では複数行の中間省略は未サポートなので自前実装が必要 ◦ TextLayoutResult を駆使することで実現可能
• いくつか考慮すべきポイントを押さえておこう ◦ 文字列の描画や再計算にかかるコスト ▪ SubcomposeLayout の利用を避けて、 Layout コンポーネントでパフォーマンスを意識 ▪ キャッシュを活用してフェーズをまたいだ再計算が走らないようにする ◦ アクセシビリティの考慮 ▪ Modifier.semantics で省略前の文字列を渡しておく ▪ 全文表示されるUIも用意する 29
参考文献 • (APIリファレンス)TextLayoutResult ◦ https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextLayoutResult • Jetpack Composeのフェーズ ◦ https://developer.android.com/develop/ui/compose/phases
• Jetpack Composeで真ん中省略のテキストを実装する ◦ https://qiita.com/Nabe1216/items/d6434507d38cd642efcd • Compose でパフォーマンスの高いレイアウトを作る ◦ https://qiita.com/mx_albert/items/7332a7c2236ef44a8b2e 30 Supported by Gemini 3 Pro