横屏双页

pull/1352/head
gedoor 3 years ago
parent 73a14a6d0a
commit e3e7756e39
  1. 62
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
  2. 174
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt

@ -10,7 +10,7 @@ import splitties.init.appCtx
import java.text.DecimalFormat import java.text.DecimalFormat
import kotlin.math.min import kotlin.math.min
@Suppress("unused") @Suppress("unused", "MemberVisibilityCanBePrivate")
data class TextPage( data class TextPage(
var index: Int = 0, var index: Int = 0,
var text: String = appCtx.getString(R.string.data_loading), var text: String = appCtx.getString(R.string.data_loading),
@ -20,6 +20,7 @@ data class TextPage(
var chapterSize: Int = 0, var chapterSize: Int = 0,
var chapterIndex: Int = 0, var chapterIndex: Int = 0,
var height: Float = 0f, var height: Float = 0f,
var leftLineSize: Int = 0
) { ) {
val lineSize get() = textLines.size val lineSize get() = textLines.size
@ -31,28 +32,53 @@ data class TextPage(
} }
} }
fun upLinesPosition() = ChapterProvider.apply { fun upLinesPosition() {
if (!ReadBookConfig.textBottomJustify) return@apply if (!ReadBookConfig.textBottomJustify) return
if (textLines.size <= 1) return@apply if (textLines.size <= 1) return
if (textLines.last().isImage) return@apply if (leftLineSize == 0) {
if (visibleHeight - height >= with(textLines.last()) { lineBottom - lineTop }) return@apply leftLineSize = lineSize
val surplus = (visibleBottom - textLines.last().lineBottom) }
if (surplus == 0f) return@apply ChapterProvider.run {
height += surplus val lastLine = textLines[leftLineSize - 1]
val tj = surplus / (textLines.size - 1) if (lastLine.isImage) return@run
for (i in 1 until textLines.size) { val lastLineHeight = with(lastLine) { lineBottom - lineTop }
val line = textLines[i] if (visibleHeight - lastLine.lineBottom >= lastLineHeight) return@run
line.lineTop = line.lineTop + tj * i val surplus = (visibleBottom - lastLine.lineBottom)
line.lineBase = line.lineBase + tj * i if (surplus == 0f) return@run
line.lineBottom = line.lineBottom + tj * i height += surplus
val tj = surplus / (leftLineSize - 1)
for (i in 1 until leftLineSize) {
val line = textLines[i]
line.lineTop = line.lineTop + tj * i
line.lineBase = line.lineBase + tj * i
line.lineBottom = line.lineBottom + tj * i
}
}
if (leftLineSize == lineSize) return
ChapterProvider.run {
val lastLine = textLines.last()
if (lastLine.isImage) return@run
val lastLineHeight = with(lastLine) { lineBottom - lineTop }
if (visibleHeight - lastLine.lineBottom >= lastLineHeight) return@run
val surplus = (visibleBottom - lastLine.lineBottom)
if (surplus == 0f) return@run
val tj = surplus / (textLines.size - leftLineSize - 1)
for (i in leftLineSize + 1 until textLines.size) {
val line = textLines[i]
val surplusIndex = i - leftLineSize
line.lineTop = line.lineTop + tj * surplusIndex
line.lineBase = line.lineBase + tj * surplusIndex
line.lineBottom = line.lineBottom + tj * surplusIndex
}
} }
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun format(): TextPage { fun format(): TextPage {
if (textLines.isEmpty() && ChapterProvider.visibleWidth > 0) { if (textLines.isEmpty() && ChapterProvider.viewWidth > 0) {
val visibleWidth = ChapterProvider.visibleRight - ChapterProvider.paddingLeft
val layout = StaticLayout( val layout = StaticLayout(
text, ChapterProvider.contentPaint, ChapterProvider.visibleWidth, text, ChapterProvider.contentPaint, visibleWidth,
Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false
) )
var y = (ChapterProvider.visibleHeight - layout.height) / 2f var y = (ChapterProvider.visibleHeight - layout.height) / 2f
@ -65,7 +91,7 @@ data class TextPage(
textLine.lineBottom = textLine.lineBottom =
ChapterProvider.paddingTop + y + layout.getLineBottom(lineIndex) ChapterProvider.paddingTop + y + layout.getLineBottom(lineIndex)
var x = ChapterProvider.paddingLeft + var x = ChapterProvider.paddingLeft +
(ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2 (visibleWidth - layout.getLineMax(lineIndex)) / 2
textLine.text = textLine.text =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
for (i in textLine.text.indices) { for (i in textLine.text.indices) {

@ -29,25 +29,40 @@ object ChapterProvider {
private const val srcReplaceChar = "" private const val srcReplaceChar = ""
@JvmStatic @JvmStatic
private var viewWidth = 0 var viewWidth = 0
private set
@JvmStatic @JvmStatic
private var viewHeight = 0 var viewHeight = 0
private set
@JvmStatic @JvmStatic
var paddingLeft = 0 var paddingLeft = 0
private set
@JvmStatic @JvmStatic
var paddingTop = 0 var paddingTop = 0
private set
@JvmStatic
var paddingRight = 0
private set
@JvmStatic
var paddingBottom = 0
private set
@JvmStatic @JvmStatic
var visibleWidth = 0 var visibleWidth = 0
private set
@JvmStatic @JvmStatic
var visibleHeight = 0 var visibleHeight = 0
private set
@JvmStatic @JvmStatic
var visibleRight = 0 var visibleRight = 0
private set
@JvmStatic @JvmStatic
var visibleBottom = 0 var visibleBottom = 0
@ -73,7 +88,7 @@ object ChapterProvider {
@JvmStatic @JvmStatic
val contentPaint: TextPaint = TextPaint() val contentPaint: TextPaint = TextPaint()
var isHorizontal = false var doublePage = false
init { init {
@ -92,6 +107,7 @@ object ChapterProvider {
): TextChapter { ): TextChapter {
val textPages = arrayListOf<TextPage>() val textPages = arrayListOf<TextPage>()
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
var absStartX = paddingLeft
var durY = 0f var durY = 0f
textPages.add(TextPage()) textPages.add(TextPage())
contents.forEachIndexed { index, content -> contents.forEachIndexed { index, content ->
@ -112,10 +128,13 @@ object ChapterProvider {
val isTitle = index == 0 val isTitle = index == 0
val textPaint = if (isTitle) titlePaint else contentPaint val textPaint = if (isTitle) titlePaint else contentPaint
if (!(isTitle && ReadBookConfig.titleMode == 2)) { if (!(isTitle && ReadBookConfig.titleMode == 2)) {
durY = setTypeText( setTypeText(
text, durY, textPages, stringBuilder, absStartX, durY, text, textPages, stringBuilder,
isTitle, textPaint, srcList isTitle, textPaint, srcList
) ).let {
absStartX = it.first
durY = it.second
}
} }
} else if (book.getImageStyle() != Book.imgStyleText) { } else if (book.getImageStyle() != Book.imgStyleText) {
val matcher = AppPattern.imgPattern.matcher(content) val matcher = AppPattern.imgPattern.matcher(content)
@ -126,10 +145,13 @@ object ChapterProvider {
val isTitle = index == 0 val isTitle = index == 0
val textPaint = if (isTitle) titlePaint else contentPaint val textPaint = if (isTitle) titlePaint else contentPaint
if (!(isTitle && ReadBookConfig.titleMode == 2)) { if (!(isTitle && ReadBookConfig.titleMode == 2)) {
durY = setTypeText( setTypeText(
text, durY, textPages, absStartX, durY, text, textPages,
stringBuilder, isTitle, textPaint stringBuilder, isTitle, textPaint
) ).let {
absStartX = it.first
durY = it.second
}
} }
} }
durY = setTypeImage( durY = setTypeImage(
@ -144,10 +166,13 @@ object ChapterProvider {
val isTitle = index == 0 val isTitle = index == 0
val textPaint = if (isTitle) titlePaint else contentPaint val textPaint = if (isTitle) titlePaint else contentPaint
if (!(isTitle && ReadBookConfig.titleMode == 2)) { if (!(isTitle && ReadBookConfig.titleMode == 2)) {
durY = setTypeText( setTypeText(
text, durY, textPages, absStartX, durY, text, textPages,
stringBuilder, isTitle, textPaint stringBuilder, isTitle, textPaint
) ).let {
absStartX = it.first
durY = it.second
}
} }
} }
} }
@ -240,14 +265,16 @@ object ChapterProvider {
* 排版文字 * 排版文字
*/ */
private fun setTypeText( private fun setTypeText(
text: String, x: Int,
y: Float, y: Float,
text: String,
textPages: ArrayList<TextPage>, textPages: ArrayList<TextPage>,
stringBuilder: StringBuilder, stringBuilder: StringBuilder,
isTitle: Boolean, isTitle: Boolean,
textPaint: TextPaint, textPaint: TextPaint,
srcList: LinkedList<String>? = null srcList: LinkedList<String>? = null
): Float { ): Pair<Int, Float> {
var absStartX = x
var durY = if (isTitle) y + titleTopSpacing else y var durY = if (isTitle) y + titleTopSpacing else y
val layout = if (ReadBookConfig.useZhLayout) { val layout = if (ReadBookConfig.useZhLayout) {
ZhLayout(text, textPaint, visibleWidth) ZhLayout(text, textPaint, visibleWidth)
@ -256,6 +283,25 @@ object ChapterProvider {
) )
for (lineIndex in 0 until layout.lineCount) { for (lineIndex in 0 until layout.lineCount) {
val textLine = TextLine(isTitle = isTitle) val textLine = TextLine(isTitle = isTitle)
if (durY + textPaint.textHeight > visibleHeight) {
val textPage = textPages.last()
if (doublePage && absStartX < viewWidth / 2) {
textPage.leftLineSize = textPage.lineSize
absStartX = viewWidth / 2 + paddingLeft
} else {
//当前页面结束,设置各种值
if (textPage.leftLineSize == 0) {
textPage.leftLineSize = textPage.lineSize
}
textPage.text = stringBuilder.toString()
textPage.height = durY
//新建页面
textPages.add(TextPage())
stringBuilder.clear()
absStartX = paddingLeft
}
durY = 0f
}
val words = val words =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
val desiredWidth = layout.getLineWidth(lineIndex) val desiredWidth = layout.getLineWidth(lineIndex)
@ -264,6 +310,7 @@ object ChapterProvider {
//第一行 //第一行
textLine.text = words textLine.text = words
addCharsToLineFirst( addCharsToLineFirst(
absStartX,
textLine, textLine,
words.toStringArray(), words.toStringArray(),
textPaint, textPaint,
@ -274,14 +321,22 @@ object ChapterProvider {
//最后一行 //最后一行
textLine.text = "$words\n" textLine.text = "$words\n"
isLastLine = true isLastLine = true
val x = if (isTitle && ReadBookConfig.titleMode == 1) val startX = if (isTitle && ReadBookConfig.titleMode == 1)
(visibleWidth - layout.getLineWidth(lineIndex)) / 2 (visibleWidth - layout.getLineWidth(lineIndex)) / 2
else 0f else 0f
addCharsToLineLast(textLine, words.toStringArray(), textPaint, x, srcList) addCharsToLineLast(
absStartX,
textLine,
words.toStringArray(),
textPaint,
startX,
srcList
)
} else { } else {
//中间行 //中间行
textLine.text = words textLine.text = words
addCharsToLineMiddle( addCharsToLineMiddle(
absStartX,
textLine, textLine,
words.toStringArray(), words.toStringArray(),
textPaint, textPaint,
@ -290,15 +345,6 @@ object ChapterProvider {
srcList srcList
) )
} }
if (durY + textPaint.textHeight > visibleHeight) {
//当前页面结束,设置各种值
textPages.last().text = stringBuilder.toString()
textPages.last().height = durY
//新建页面
textPages.add(TextPage())
stringBuilder.clear()
durY = 0f
}
stringBuilder.append(words) stringBuilder.append(words)
if (isLastLine) stringBuilder.append("\n") if (isLastLine) stringBuilder.append("\n")
textPages.last().textLines.add(textLine) textPages.last().textLines.add(textLine)
@ -308,13 +354,14 @@ object ChapterProvider {
} }
if (isTitle) durY += titleBottomSpacing if (isTitle) durY += titleBottomSpacing
durY += textPaint.textHeight * paragraphSpacing / 10f durY += textPaint.textHeight * paragraphSpacing / 10f
return durY return Pair(absStartX, durY)
} }
/** /**
* 有缩进,两端对齐 * 有缩进,两端对齐
*/ */
private fun addCharsToLineFirst( private fun addCharsToLineFirst(
absStartX: Int,
textLine: TextLine, textLine: TextLine,
words: Array<String>, words: Array<String>,
textPaint: TextPaint, textPaint: TextPaint,
@ -323,34 +370,23 @@ object ChapterProvider {
) { ) {
var x = 0f var x = 0f
if (!ReadBookConfig.textFullJustify) { if (!ReadBookConfig.textFullJustify) {
addCharsToLineLast(textLine, words, textPaint, x, srcList) addCharsToLineLast(absStartX, textLine, words, textPaint, x, srcList)
return return
} }
val bodyIndent = ReadBookConfig.paragraphIndent val bodyIndent = ReadBookConfig.paragraphIndent
val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length
bodyIndent.toStringArray().forEach { bodyIndent.toStringArray().forEach {
val x1 = x + icw val x1 = x + icw
if (srcList != null && it == srcReplaceChar) { textLine.textChars.add(
textLine.textChars.add( TextChar(
TextChar( it, start = absStartX + x, end = absStartX + x1
srcList.removeFirst(),
start = paddingLeft + x,
end = paddingLeft + x1,
isImage = true
)
)
} else {
textLine.textChars.add(
TextChar(
it, start = paddingLeft + x, end = paddingLeft + x1
)
) )
} )
x = x1 x = x1
} }
if (words.size > bodyIndent.length) { if (words.size > bodyIndent.length) {
val words1 = words.copyOfRange(bodyIndent.length, words.size) val words1 = words.copyOfRange(bodyIndent.length, words.size)
addCharsToLineMiddle(textLine, words1, textPaint, desiredWidth, x, srcList) addCharsToLineMiddle(absStartX, textLine, words1, textPaint, desiredWidth, x, srcList)
} }
} }
@ -358,6 +394,7 @@ object ChapterProvider {
* 无缩进,两端对齐 * 无缩进,两端对齐
*/ */
private fun addCharsToLineMiddle( private fun addCharsToLineMiddle(
absStartX: Int,
textLine: TextLine, textLine: TextLine,
words: Array<String>, words: Array<String>,
textPaint: TextPaint, textPaint: TextPaint,
@ -366,7 +403,7 @@ object ChapterProvider {
srcList: LinkedList<String>? srcList: LinkedList<String>?
) { ) {
if (!ReadBookConfig.textFullJustify) { if (!ReadBookConfig.textFullJustify) {
addCharsToLineLast(textLine, words, textPaint, startX, srcList) addCharsToLineLast(absStartX, textLine, words, textPaint, startX, srcList)
return return
} }
val gapCount: Int = words.lastIndex val gapCount: Int = words.lastIndex
@ -379,15 +416,15 @@ object ChapterProvider {
textLine.textChars.add( textLine.textChars.add(
TextChar( TextChar(
srcList.removeFirst(), srcList.removeFirst(),
start = paddingLeft + x, start = absStartX + x,
end = paddingLeft + x1, end = absStartX + x1,
isImage = true isImage = true
) )
) )
} else { } else {
textLine.textChars.add( textLine.textChars.add(
TextChar( TextChar(
s, start = paddingLeft + x, end = paddingLeft + x1 s, start = absStartX + x, end = absStartX + x1
) )
) )
} }
@ -400,6 +437,7 @@ object ChapterProvider {
* 最后一行,自然排列 * 最后一行,自然排列
*/ */
private fun addCharsToLineLast( private fun addCharsToLineLast(
absStartX: Int,
textLine: TextLine, textLine: TextLine,
words: Array<String>, words: Array<String>,
textPaint: TextPaint, textPaint: TextPaint,
@ -414,15 +452,15 @@ object ChapterProvider {
textLine.textChars.add( textLine.textChars.add(
TextChar( TextChar(
srcList.removeFirst(), srcList.removeFirst(),
start = paddingLeft + x, start = absStartX + x,
end = paddingLeft + x1, end = absStartX + x1,
isImage = true isImage = true
) )
) )
} else { } else {
textLine.textChars.add( textLine.textChars.add(
TextChar( TextChar(
it, start = paddingLeft + x, end = paddingLeft + x1 it, start = absStartX + x, end = absStartX + x1
) )
) )
} }
@ -435,17 +473,17 @@ object ChapterProvider {
* 超出边界处理 * 超出边界处理
*/ */
private fun exceed(textLine: TextLine, words: Array<String>) { private fun exceed(textLine: TextLine, words: Array<String>) {
val endX = textLine.textChars.lastOrNull()?.end ?: return // val endX = textLine.textChars.lastOrNull()?.end ?: return
if (endX > visibleRight) { // if (endX > visibleRight) {
val cc = (endX - visibleRight) / words.size // val cc = (endX - visibleRight) / words.size
for (i in 0..words.lastIndex) { // for (i in 0..words.lastIndex) {
textLine.getTextCharReverseAt(i).let { // textLine.getTextCharReverseAt(i).let {
val py = cc * (words.size - i) // val py = cc * (words.size - i)
it.start = it.start - py // it.start = it.start - py
it.end = it.end - py // it.end = it.end - py
} // }
} // }
} // }
} }
/** /**
@ -529,7 +567,7 @@ object ChapterProvider {
if (width > 0 && height > 0 && (width != viewWidth || height != viewHeight)) { if (width > 0 && height > 0 && (width != viewWidth || height != viewHeight)) {
viewWidth = width viewWidth = width
viewHeight = height viewHeight = height
isHorizontal = width > height doublePage = width > height
upVisibleSize() upVisibleSize()
postEvent(EventBus.UP_CONFIG, true) postEvent(EventBus.UP_CONFIG, true)
} }
@ -542,13 +580,15 @@ object ChapterProvider {
if (viewWidth > 0 && viewHeight > 0) { if (viewWidth > 0 && viewHeight > 0) {
paddingLeft = ReadBookConfig.paddingLeft.dp paddingLeft = ReadBookConfig.paddingLeft.dp
paddingTop = ReadBookConfig.paddingTop.dp paddingTop = ReadBookConfig.paddingTop.dp
visibleWidth = if (isHorizontal) { paddingRight = ReadBookConfig.paddingRight.dp
viewWidth / 2 - paddingLeft - ReadBookConfig.paddingRight.dp paddingBottom = ReadBookConfig.paddingBottom.dp
visibleWidth = if (doublePage) {
viewWidth / 2 - paddingLeft - paddingRight
} else { } else {
viewWidth - paddingLeft - ReadBookConfig.paddingRight.dp viewWidth - paddingLeft - paddingRight
} }
visibleHeight = viewHeight - paddingTop - ReadBookConfig.paddingBottom.dp visibleHeight = viewHeight - paddingTop - paddingBottom
visibleRight = paddingLeft + visibleWidth visibleRight = viewWidth - paddingRight
visibleBottom = paddingTop + visibleHeight visibleBottom = paddingTop + visibleHeight
} }
} }

Loading…
Cancel
Save