Razl-Dazl

eyecatch
Copyright © Google

HtmlCompatでHTMLテキストをより正確に表示したい

Posted at — 2023-07-18

2023/07/19 正規表現部分を修正

TextViewにHTMLテキストを表示させたいときって多分HtmlCompatを使うと思うんですけど、細かい表示が想定通りにならなかったりするのでいろいろ調整してました

現在制作中のMastodonアプリから例を挙げていきます


改行が省略される

誤:

正:

こんな感じで改行が複数個連続する場合に自動的に省略されてしまう事があります

フラグが間違っているだけなので修正は簡単です

誤:

editText.text = 
	HtmlCompat.fromHtml(content, HtmlCompat.FROM_HTML_MODE_COMPACT)

正:

editText.text = 
	HtmlCompat.fromHtml(content, HtmlCompat.FROM_HTML_MODE_LEGACY)

fromHtml()の引数がHtmlCompat.FROM_HTML_MODE_COMPACTの場合は行間が詰められてしまうので、そこを修正するだけです

フラグ名見ればすぐ想像つくと思いますが、一応例として・・・


文末にめっちゃ空白出来る

誤:

正:

流石に間が空きすぎでは・・・

HTMLテキストとして●●正直にレンダリングしようとしてそうなってるのかな・・・

こういう場合はですね、fromHtml()の戻り値がSpannedなテキストなのでそれをさらに加工してあげればよいです

editText.text = 
	HtmlCompat.fromHtml(content, HtmlCompat.FROM_HTML_MODE_LEGACY)
		.trimEnd()

trimEnd()を使うと文末のあらゆる空白を削除することが出来ます

ちなみにtrim()は使わないで下さい! 何故ならtrim()だと文頭の空白も削除されてしまうので・・・

trim()するとこうなってしまう↓

何気全角スペースがしっかり考慮されていることに感心


半角スペースが1個しか表示されない

いちばん苦労したとこ!!!!!

すみませんロジックが不完全だったので直しました

誤:

正:

原因はシンプルで、HTMLの仕様上そうなります

解決方法も通常のHTMLテキストに対する方法と一緒です

半角スペースを に置き換えてしまえば、しっかり個数分のスペースが表示されるようになります

みんなだいすき正規表現の出番です!!!

ただ、例えば<br />みたいに、HTMLタグ内にスペースが存在する場合もあるので、そういう時は除外しないといけません

HTMLタグで囲まれた部分の中だけを照査せねばなりません・・・

n時間格闘して出来た結果がこちらです↓

fun parseHtmlSpace(text: String): String {  
    val hankakuSpaceReplace = "(>[^<]*?)( +)([^<]*<)"  // 1
  
    val transform: (MatchResult) -> CharSequence = {  
        val group = it.groupValues // 2
  
        if (group.size < 4) {  
            it.value // 3
        } else {  
            val spaces = StringBuilder().apply {  
                val spaceLength = group[2].length  
                val space = "&nbsp;"  
                for(i in 1..spaceLength) { append(space) } // 4
            }  
            val result = group[1] + spaces + group[3] // 5
            parseHtmlSpace(result) // 6
        }  
    }  
  
    return Regex(hankakuSpaceReplace).replace(text,  transform)  
}

高階関数(val transform)を引数とする記述があるのでぱっと見はちょっとややこしいですが、考え方(≠実際の処理)しては

  1. HTMLタグの「>」と「<」で囲まれたとこを抜き出す
  2. 一致した部分について、カッコでくくられた部分ごとにパーツとして切り出す
  3. 条件にあわない場合はそのままの文字列を返す
  4. 半角スペースの個数分だけ&nbsp;に置き換える
  5. 元のパーツを&nbsp;くんの前後にくっつけて文字列を元に戻す
  6. スペースが複数箇所へ存在する可能性があるので再帰処理する

このようになります

正直言うと1を作る部分に9割の時間を投入しました 正規表現で「一致」の条件はよく書くと思うんですけど、除外する条件を書くことって今まであんまり無かったのでそこが難しかったです

追記: 正規表現のパターンを修正し、更に再帰処理するようにしました (再帰の必要が無くなったら手順3へ飛んで再帰から脱出するようになります)

・・・なんか久々にちゃんと説明を書いたかもしれない()

Author@zakuro

Mastodon: 396@vivaldi.net