首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 閱讀器實現文字兩端對齊:WebView 方案最省力,TextView 自定義排版可全平台

     編輯:程式設計綜合問答

想達到 iReader 級別的兩端對齊(分散對齊),Android 原生 TextView 不支援 `textAlign=justify`。兩條成熟路徑任君選擇。

方案一:WebView + CSS `text-align: justify`(三行代碼搞定)

WebView wv = findViewById(R.id.webView);
String html = "%s";
wv.loadData(String.format(html, content), "text/html", "utf-8");

優缺點

優點缺點
零排版代碼、支援複雜富文本、自動換行換頁啟動較慢、記憶體佔用高、字體渲染受 WebView 版本影響、難以精確控制行間距/字間距

方案二:自定義 TextView 排版演算法(高性能、純原生)

核心邏輯(Kotlin 版)

class JustifiedTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {

    override fun onDraw(canvas: Canvas) {
        val text = this.text.toString()
        if (text.isBlank()) { super.onDraw(canvas); return }
        
        val paint = this.paint
        val width = measuredWidth - paddingLeft - paddingRight
        val lines = breakLines(text, paint, width)
        val y = paddingTop + paint.textSize
        
        lines.forEachIndexed { idx, line ->
            if (idx == lines.lastIndex && line.length < width * 0.6) {
                // 最後一行不拉伸
                canvas.drawText(line, paddingLeft.toFloat(), y, paint)
            } else {
                drawJustifiedLine(canvas, line, width, y, paint)
            }
            y += paint.fontMetricsInt.descent - paint.fontMetricsInt.ascent + lineSpacingExtra
        }
    }
    
    private fun drawJustifiedLine(canvas: Canvas, line: String, width: Int, y: Float, paint: TextPaint) {
        val words = line.split(" ").filter { it.isNotBlank() }
        if (words.size <= 1) { canvas.drawText(line, paddingLeft.toFloat(), y, paint); return }
        val textWidth = words.sumOf { paint.measureText(it).toInt() }
        val spaceCount = words.size - 1
        val totalSpace = width - textWidth
        val baseSpace = totalSpace / spaceCount
        val extra = totalSpace % spaceCount
        var x = paddingLeft.toFloat()
        words.forEachIndexed { i, w ->
            canvas.drawText(w, x, y, paint)
            x += paint.measureText(w)
            if (i < spaceCount) {
                val sp = baseSpace + if (i < extra) 1 else 0
                x += sp
            }
        }
    }
    
    private fun breakLines(text: String, paint: TextPaint, width: Int): List {
        // 簡易斷行:按空格切詞,貪心填滿
        val words = text.split("\\s+".toRegex()).filter { it.isNotBlank() }
        val lines = mutableListOf()
        var current = ""
        words.forEach { w ->
            if (paint.measureText(current + " " + w) <= width) {
                current = if (current.isEmpty()) w else current + " " + w
            } else {
                lines.add(current)
                current = w
            }
        }
        if (current.isNotEmpty()) lines.add(current)
        return lines
    }
}

XML 使用

<com.example.JustifiedTextView
    android:id="@+id/tv_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:lineSpacingMultiplier="1.5"
    android:textSize="16sp" />

選型建議

  • 閱讀器主流程 → 自定義 TextView,流暢度、記憶體、字體一致性全面勝出。
  • 雜誌/圖文混排、臨時需求 → WebView,開發效率最高。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved