diff --git a/app/src/main/assets/readConfig.json b/app/src/main/assets/readConfig.json index 4f81b9f55..c26c81554 100644 --- a/app/src/main/assets/readConfig.json +++ b/app/src/main/assets/readConfig.json @@ -9,8 +9,7 @@ "darkStatusIcon": true, "textSize": 24, "letterSpacing": 0, - "lineSpacingExtra": 10, - "lineSpacingMultiplier": 1.2 + "lineSpacingExtra": 10 }, { "bgStr": "#DDC090", @@ -22,8 +21,7 @@ "darkStatusIcon": true, "textSize": 24, "letterSpacing": 0, - "lineSpacingExtra": 10, - "lineSpacingMultiplier": 1.2 + "lineSpacingExtra": 10 }, { "bgStr": "#C2D8AA", @@ -35,8 +33,7 @@ "darkStatusIcon": false, "textSize": 24, "letterSpacing": 0, - "lineSpacingExtra": 10, - "lineSpacingMultiplier": 1.2 + "lineSpacingExtra": 10 }, { "bgStr": "#DBB8E2", @@ -48,8 +45,7 @@ "darkStatusIcon": false, "textSize": 24, "letterSpacing": 0, - "lineSpacingExtra": 10, - "lineSpacingMultiplier": 1.2 + "lineSpacingExtra": 10 }, { "bgStr": "#ABCEE0", @@ -61,7 +57,6 @@ "darkStatusIcon": false, "textSize": 24, "letterSpacing": 0, - "lineSpacingExtra": 10, - "lineSpacingMultiplier": 1.2 + "lineSpacingExtra": 10 } ] \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index db258d68a..7310dcef8 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -19,7 +19,6 @@ import io.legado.app.help.AppConfig import io.legado.app.help.CrashHandler import io.legado.app.help.ReadBookConfig import io.legado.app.lib.theme.ThemeStore -import io.legado.app.ui.book.read.page.ChapterProvider import io.legado.app.utils.getCompatColor import io.legado.app.utils.getPrefInt @@ -92,7 +91,7 @@ class App : Application() { ) .apply() } - ChapterProvider.upReadAloudSpan() +// ChapterProvider.upReadAloudSpan() } fun applyDayNight() { diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 9d42ab86e..3a9eab787 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -35,4 +35,5 @@ object PreferKey { const val launcherIcon = "launcherIcon" const val selectText = "selectText" const val lastBackup = "lastBackup" + const val bodyIndent = "textIndent" } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index 569a802a0..1184e4143 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -237,6 +237,8 @@ object BookHelp { private var bookName: String? = null private var bookOrigin: String? = null private var replaceRules: List = arrayListOf() + val bodyIndent + get() = " ".repeat(App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)) fun disposeContent( title: String, @@ -273,7 +275,6 @@ object BookHelp { 1 -> c = ZhConvertBootstrap.newInstance().toSimple(c) 2 -> c = ZhConvertBootstrap.newInstance().toTraditional(c) } - val indent = App.INSTANCE.getPrefInt("textIndent", 2) - return c.replace("\\s*\\n+\\s*".toRegex(), "\n" + " ".repeat(indent)) + return c.replace("\\s*\\n+\\s*".toRegex(), "\n$bodyIndent") } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt index 37a8d0212..5dc2d4817 100644 --- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt @@ -7,6 +7,7 @@ import android.graphics.drawable.Drawable import io.legado.app.App import io.legado.app.R import io.legado.app.help.coroutine.Coroutine +import io.legado.app.ui.book.read.page.ChapterProvider import io.legado.app.utils.* import java.io.File @@ -22,6 +23,8 @@ object ReadBookConfig { val json = String(App.INSTANCE.assets.open(readConfigFileName).readBytes()) GSON.fromJsonArray(json)!! } + val durConfig + get() = getConfig(styleSelect) var styleSelect get() = App.INSTANCE.getPrefInt("readStyleSelect") @@ -33,7 +36,7 @@ object ReadBookConfig { } @Synchronized - fun getConfig(index: Int = styleSelect): Config { + fun getConfig(index: Int): Config { if (configList.size < 5) { resetAll() } @@ -64,7 +67,7 @@ object ReadBookConfig { val dm = resources.displayMetrics val width = dm.widthPixels val height = dm.heightPixels - bg = getConfig().bgDrawable(width, height) + bg = durConfig.bgDrawable(width, height) } fun save() { @@ -76,8 +79,8 @@ object ReadBookConfig { fun resetDur() { defaultConfigs[styleSelect].let { - getConfig().setBg(it.bgType(), it.bgStr()) - getConfig().setTextColor(it.textColor()) + durConfig.setBg(it.bgType(), it.bgStr()) + durConfig.setTextColor(it.textColor()) upBg() save() } @@ -104,7 +107,7 @@ object ReadBookConfig { var textSize: Int = 15,//文字大小 var letterSpacing: Float = 1f,//字间距 var lineSpacingExtra: Int = 12,//行间距 - var lineSpacingMultiplier: Float = 1.2f,//行倍距 + var paragraphSpacing: Int = 12, var paddingBottom: Int = 6, var paddingLeft: Int = 16, var paddingRight: Int = 16, @@ -134,6 +137,7 @@ object ReadBookConfig { } else { textColor = "#${color.hexString}" } + ChapterProvider.upStyle(this) } fun setStatusIconDark(isDark: Boolean) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/Help.kt b/app/src/main/java/io/legado/app/ui/book/read/Help.kt index d1717c204..3a3e04191 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/Help.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/Help.kt @@ -56,7 +56,7 @@ object Help { } activity.window.decorView.systemUiVisibility = flag if (toolBarHide) { - ATH.setLightStatusBar(activity, ReadBookConfig.getConfig().statusIconDark()) + ATH.setLightStatusBar(activity, ReadBookConfig.durConfig.statusIconDark()) } else { ATH.setLightStatusBarAuto( activity, diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 2bfc4b7c3..39e07a458 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -44,7 +44,6 @@ import io.legado.app.ui.replacerule.edit.ReplaceEditDialog import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.utils.* import kotlinx.android.synthetic.main.activity_book_read.* -import kotlinx.android.synthetic.main.view_book_page.* import kotlinx.android.synthetic.main.view_read_menu.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main @@ -117,7 +116,6 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo * 初始化View */ private fun initView() { - ChapterProvider.textView = content_text_view tv_chapter_name.onClick { ReadBook.webBook?.let { startActivityForResult( @@ -446,7 +444,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo /** * colorSelectDialog */ - override fun onColorSelected(dialogId: Int, color: Int) = with(ReadBookConfig.getConfig()) { + override fun onColorSelected(dialogId: Int, color: Int) = with(ReadBookConfig.durConfig) { when (dialogId) { TEXT_COLOR -> { setTextColor(color) @@ -507,7 +505,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo ReadBook.curTextChapter?.let { textChapter -> val page = textChapter.page(ReadBook.durPageIndex) if (page != null && page.text is SpannableStringBuilder) { - page.text.removeSpan(ChapterProvider.readAloudSpan) + page.removePageAloudSpan() page_view.upContent() } } @@ -531,6 +529,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo content_view.upStyle() page_view.upBg() page_view.upStyle() + ChapterProvider.upStyle(ReadBookConfig.durConfig) if (it) { ReadBook.loadContent() } else { diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt index 4b2aa6d9c..f4f1d9ff8 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt @@ -81,7 +81,7 @@ class BgTextConfigDialog : DialogFragment() { } @SuppressLint("InflateParams") - private fun initData() = with(ReadBookConfig.getConfig()) { + private fun initData() = with(ReadBookConfig.durConfig) { sw_dark_status_icon.isChecked = statusIconDark() adapter = BgAdapter(requireContext()) recycler_view.layoutManager = @@ -99,7 +99,7 @@ class BgTextConfigDialog : DialogFragment() { } } - private fun initView() = with(ReadBookConfig.getConfig()) { + private fun initView() = with(ReadBookConfig.durConfig) { sw_dark_status_icon.onCheckedChange { buttonView, isChecked -> if (buttonView?.isPressed == true) { setStatusIconDark(isChecked) @@ -156,7 +156,7 @@ class BgTextConfigDialog : DialogFragment() { holder.itemView.apply { this.onClick { getItem(holder.layoutPosition)?.let { - ReadBookConfig.getConfig().setBg(1, it) + ReadBookConfig.durConfig.setBg(1, it) ReadBookConfig.upBg() postEvent(EventBus.UP_CONFIG, false) } @@ -180,7 +180,7 @@ class BgTextConfigDialog : DialogFragment() { FileUtils.createFileIfNotExist(file.absolutePath + File.separator + "bg" + File.separator + doc.name) DocumentUtils.readBytes(requireContext(), uri)?.let { file.writeBytes(it) - ReadBookConfig.getConfig().setBg(2, file.absolutePath) + ReadBookConfig.durConfig.setBg(2, file.absolutePath) ReadBookConfig.upBg() postEvent(EventBus.UP_CONFIG, false) } @@ -194,7 +194,7 @@ class BgTextConfigDialog : DialogFragment() { .rationale(R.string.bg_image_per) .onGranted { RealPathUtil.getPath(requireContext(), uri)?.let { path -> - ReadBookConfig.getConfig().setBg(2, path) + ReadBookConfig.durConfig.setBg(2, path) ReadBookConfig.upBg() postEvent(EventBus.UP_CONFIG, false) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt index abdbafeb1..1cb0715e3 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt @@ -49,7 +49,7 @@ class PaddingConfigDialog : DialogFragment() { ReadBookConfig.save() } - private fun initData() = with(ReadBookConfig.getConfig()) { + private fun initData() = with(ReadBookConfig.durConfig) { //正文 dsb_padding_top.progress = paddingTop dsb_padding_bottom.progress = paddingBottom @@ -67,7 +67,7 @@ class PaddingConfigDialog : DialogFragment() { dsb_footer_padding_right.progress = footerPaddingRight } - private fun initView() = with(ReadBookConfig.getConfig()) { + private fun initView() = with(ReadBookConfig.durConfig) { //正文 dsb_padding_top.onChanged = { paddingTop = it diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index b7279da4a..decd54ee8 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt @@ -91,7 +91,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { postEvent(EventBus.UP_CONFIG, true) } tv_text_bold.onClick { - with(ReadBookConfig.getConfig()) { + with(ReadBookConfig.durConfig) { textBold = !textBold tv_text_bold.isSelected = textBold } @@ -105,7 +105,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { title = getString(R.string.text_indent), items = resources.getStringArray(R.array.indent).toList() ) { _, index -> - putPrefInt("textIndent", index) + putPrefInt(PreferKey.bodyIndent, index) postEvent(EventBus.UP_CONFIG, true) } } @@ -117,15 +117,19 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { } } dsb_text_size.onChanged = { - ReadBookConfig.getConfig().textSize = it + 5 + ReadBookConfig.durConfig.textSize = it + 5 postEvent(EventBus.UP_CONFIG, true) } dsb_text_letter_spacing.onChanged = { - ReadBookConfig.getConfig().letterSpacing = (it - 5) / 10f + ReadBookConfig.durConfig.letterSpacing = (it - 5) / 10f postEvent(EventBus.UP_CONFIG, true) } dsb_line_size.onChanged = { - ReadBookConfig.getConfig().lineSpacingExtra = it + ReadBookConfig.durConfig.lineSpacingExtra = it + postEvent(EventBus.UP_CONFIG, true) + } + dsb_paragraph_spacing.onChanged = { + ReadBookConfig.durConfig.paragraphSpacing = it postEvent(EventBus.UP_CONFIG, true) } rg_page_anim.onCheckedChange { _, checkedId -> @@ -173,11 +177,12 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { } private fun upStyle() { - ReadBookConfig.getConfig().let { + ReadBookConfig.durConfig.let { tv_text_bold.isSelected = it.textBold dsb_text_size.progress = it.textSize - 5 dsb_text_letter_spacing.progress = (it.letterSpacing * 10).toInt() + 5 dsb_line_size.progress = it.lineSpacingExtra + dsb_paragraph_spacing.progress = it.paragraphSpacing } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt index cdc79aa7d..d2c405de5 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt @@ -1,106 +1,270 @@ package io.legado.app.ui.book.read.page -import android.text.Spannable -import android.text.SpannableStringBuilder -import android.text.style.ForegroundColorSpan -import android.text.style.RelativeSizeSpan -import androidx.core.text.HtmlCompat -import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT -import io.legado.app.App +import android.graphics.Point +import android.text.Layout +import android.text.StaticLayout +import android.text.TextPaint import io.legado.app.data.entities.BookChapter -import io.legado.app.lib.theme.accentColor +import io.legado.app.help.BookHelp +import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.entities.TextChapter +import io.legado.app.ui.book.read.page.entities.TextChar +import io.legado.app.ui.book.read.page.entities.TextLine import io.legado.app.ui.book.read.page.entities.TextPage +import io.legado.app.utils.dp object ChapterProvider { - var readAloudSpan = ForegroundColorSpan(App.INSTANCE.accentColor) - private val titleSpan = RelativeSizeSpan(1.2f) + var viewWidth = 0 + var viewHeight = 0 + private var visibleWidth = 0 + private var visibleHeight = 0 + private var paddingLeft = 0 + private var paddingTop = 0 + private var lineSpacingExtra = 0 + private var paragraphSpacing = 0 + var titlePaint = TextPaint() + var contentPaint = TextPaint() - var textView: ContentTextView? = null + init { + upStyle(ReadBookConfig.durConfig) + } + + fun upStyle(config: ReadBookConfig.Config) { + titlePaint.color = config.textColor() + titlePaint.letterSpacing = config.letterSpacing + contentPaint.color = config.textColor() + contentPaint.letterSpacing = config.letterSpacing + lineSpacingExtra = config.lineSpacingExtra + paragraphSpacing = config.paragraphSpacing + titlePaint.textSize = (config.textSize + 2).dp.toFloat() + contentPaint.textSize = config.textSize.dp.toFloat() + upSize(config) + } + + fun upSize(config: ReadBookConfig.Config) { + paddingLeft = config.paddingLeft.dp + paddingTop = config.paddingTop.dp + visibleWidth = viewWidth - paddingLeft - config.paddingRight.dp + visibleHeight = viewHeight - paddingTop - config.paddingBottom.dp + } + @Suppress("DEPRECATION") fun getTextChapter( bookChapter: BookChapter, content: String, chapterSize: Int, isHtml: Boolean = false ): TextChapter { - textView?.let { - val textPages = arrayListOf() - val pageLines = arrayListOf() - val pageLengths = arrayListOf() - var surplusText = content - var pageIndex = 0 - while (surplusText.isNotEmpty()) { - val spannableStringBuilder = - if (isHtml) { - HtmlCompat.fromHtml( - surplusText, - FROM_HTML_MODE_COMPACT - ) as SpannableStringBuilder - } else { - SpannableStringBuilder(surplusText) - } - if (pageIndex == 0) { - val end = surplusText.indexOf("\n") - if (end > 0) { - spannableStringBuilder.setSpan( - titleSpan, - 0, - end, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE + val bodyIndent = BookHelp.bodyIndent + val textPages = arrayListOf() + val pageLines = arrayListOf() + val pageLengths = arrayListOf() + var surplusText = content + var durY = 0 + textPages.add(TextPage()) + while (surplusText.isNotEmpty()) { + if (textPages.first().textLines.isEmpty()) { + //title + val end = surplusText.indexOf("\n") + if (end > 0) { + val title = surplusText.substring(0, end) + surplusText = surplusText.substring(end + 1) + val layout = StaticLayout( + title, titlePaint, visibleWidth, + Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra.toFloat(), false + ) + for (lineIndex in 0 until layout.lineCount) { + durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) + val textLine = TextLine(isTitle = true) + if (durY < visibleHeight) { + textPages.last().textLines.add(textLine) + } else { + durY = 0 + textPages.add(TextPage()) + textPages.last().textLines.add(textLine) + } + textLine.lineBottom = layout.getLineBottom(lineIndex) + textLine.lineTop = layout.getLineTop(lineIndex) + val words = title.substring( + layout.getLineStart(lineIndex), + layout.getLineEnd(lineIndex) ) + val desiredWidth = layout.getLineMax(lineIndex) + if (lineIndex != layout.lineCount - 1) { + val gapCount: Int = words.length - 1 + val d = (visibleWidth - desiredWidth) / gapCount + var x = 0 + for (i in words.indices) { + val char = words[i].toString() + val cw = StaticLayout.getDesiredWidth(char, titlePaint) + val x1 = if (i != words.lastIndex) { + (x + cw + d).toInt() + } else { + (x + cw).toInt() + } + val textChar = TextChar( + charData = char, + leftBottomPosition = Point(paddingLeft + x, paddingTop + durY), + rightTopPosition = Point( + paddingLeft + x1, + paddingTop + durY - (textLine.lineBottom - textLine.lineTop) + ) + ) + textLine.textChars.add(textChar) + x = x1 + } + } else { + //最后一行 + var x = 0 + for (i in words.indices) { + val char = words[i].toString() + val cw = StaticLayout.getDesiredWidth(char, titlePaint) + val x1 = (x + cw).toInt() + val textChar = TextChar( + charData = char, + leftBottomPosition = Point(paddingLeft + x, paddingTop + durY), + rightTopPosition = Point( + paddingLeft + x1, + paddingTop + durY - (textLine.lineBottom - textLine.lineTop) + ) + ) + textLine.textChars.add(textChar) + x = x1 + } + } } + durY += paragraphSpacing } - it.text = spannableStringBuilder - val lastLine = it.getLineNum() - val lastCharNum = it.getCharNum(lastLine) - if (lastCharNum == 0) { - break + } else { + //正文 + val end = surplusText.indexOf("\n") + val text: String + if (end >= 0) { + text = surplusText.substring(0, end) + surplusText = surplusText.substring(end + 1) } else { - pageLines.add(lastLine) - pageLengths.add(lastCharNum) - textPages.add( - TextPage( - index = pageIndex, - text = spannableStringBuilder.delete( - lastCharNum, - spannableStringBuilder.length - ), - title = bookChapter.title, - chapterSize = chapterSize, - chapterIndex = bookChapter.index + text = surplusText + surplusText = "" + } + val layout = StaticLayout( + text, contentPaint, visibleWidth, + Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra.toFloat(), false + ) + for (lineIndex in 0 until layout.lineCount) { + val textLine = TextLine(isTitle = false) + durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) + if (durY < visibleHeight) { + textPages.last().textLines.add(textLine) + } else { + durY = 0 + textPages.add(TextPage()) + textPages.last().textLines.add(textLine) + } + textLine.lineBottom = layout.getLineBottom(lineIndex) + textLine.lineTop = layout.getLineTop(lineIndex) + var words = + text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) + val desiredWidth = layout.getLineMax(lineIndex) + if (lineIndex == 0 && layout.lineCount > 1) { + //第一行 + var x = 0 + val icw = StaticLayout.getDesiredWidth(bodyIndent, contentPaint) + var x1 = (x + icw).toInt() + val textChar = TextChar( + charData = bodyIndent, + leftBottomPosition = Point(paddingLeft + x, paddingTop + durY), + rightTopPosition = Point( + paddingLeft + x1, + paddingTop + durY - (textLine.lineBottom - textLine.lineTop) + ) ) - ) - surplusText = surplusText.substring(lastCharNum) - pageIndex++ + textLine.textChars.add(textChar) + x = x1 + words = words.replaceFirst(bodyIndent, "") + val gapCount: Int = words.length - 1 + val d = (visibleWidth - desiredWidth) / gapCount + for (i in words.indices) { + val char = words[i].toString() + val cw = StaticLayout.getDesiredWidth(char, contentPaint) + x1 = if (i != words.lastIndex) { + (x + cw + d).toInt() + } else { + (x + cw).toInt() + } + val textChar1 = TextChar( + charData = char, + leftBottomPosition = Point(paddingLeft + x, paddingTop + durY), + rightTopPosition = Point( + paddingLeft + x1, + paddingTop + durY - (textLine.lineBottom - textLine.lineTop) + ) + ) + textLine.textChars.add(textChar1) + x = x1 + } + } else if (lineIndex == layout.lineCount - 1) { + //最后一行 + var x = 0 + for (i in words.indices) { + val char = words[i].toString() + val cw = StaticLayout.getDesiredWidth(char, contentPaint) + val x1 = (x + cw).toInt() + val textChar = TextChar( + charData = char, + leftBottomPosition = Point(paddingLeft + x, paddingTop + durY), + rightTopPosition = Point( + paddingLeft + x1, + paddingTop + durY - (textLine.lineBottom - textLine.lineTop) + ) + ) + textLine.textChars.add(textChar) + x = x1 + } + } else { + //中间行 + val gapCount: Int = words.length - 1 + val d = (visibleWidth - desiredWidth) / gapCount + var x = 0 + for (i in words.indices) { + val char = words[i].toString() + val cw = StaticLayout.getDesiredWidth(char, contentPaint) + val x1 = if (i != words.lastIndex) { + (x + cw + d).toInt() + } else { + (x + cw).toInt() + } + val textChar = TextChar( + charData = char, + leftBottomPosition = Point(paddingLeft + x, paddingTop + durY), + rightTopPosition = Point( + paddingLeft + x1, + paddingTop + durY - (textLine.lineBottom - textLine.lineTop) + ) + ) + textLine.textChars.add(textChar) + x = x1 + } + } } + durY += paragraphSpacing } - for (item in textPages) { - item.pageSize = textPages.size - } - return TextChapter( - bookChapter.index, - bookChapter.title, - bookChapter.url, - textPages, - pageLines, - pageLengths, - chapterSize - ) - } ?: return TextChapter( + } + for ((index, item) in textPages.withIndex()) { + item.index = index + item.pageSize = textPages.size + item.chapterIndex = bookChapter.index + item.chapterSize = chapterSize + item.title = bookChapter.title + } + return TextChapter( bookChapter.index, bookChapter.title, bookChapter.url, - arrayListOf(), - arrayListOf(), - arrayListOf(), + textPages, + pageLines, + pageLengths, chapterSize ) - - } - - fun upReadAloudSpan() { - readAloudSpan = ForegroundColorSpan(App.INSTANCE.accentColor) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index 5e3ab0611..daf4e57da 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -1,11 +1,14 @@ package io.legado.app.ui.book.read.page import android.content.Context +import android.graphics.Canvas import android.util.AttributeSet -import io.legado.app.ui.widget.text.InertiaScrollTextView +import android.view.View +import io.legado.app.help.ReadBookConfig +import io.legado.app.ui.book.read.page.entities.TextPage -class ContentTextView : InertiaScrollTextView { +class ContentTextView : View { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) @@ -13,19 +16,45 @@ class ContentTextView : InertiaScrollTextView { constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - /** - * 获取当前页总字数 - */ - fun getCharNum(lineNum: Int = getLineNum()): Int { - return layout?.getLineEnd(lineNum) ?: 0 + var textPage: TextPage? = null + + fun setContent(textPage: TextPage?) { + this.textPage = textPage + invalidate() + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + ReadBookConfig.durConfig.let { + ChapterProvider.viewWidth = w + ChapterProvider.viewHeight = h + ChapterProvider.upSize(ReadBookConfig.durConfig) + } } - /** - * 获取当前页总行数 - */ - fun getLineNum(): Int { - val topOfLastLine = height - paddingTop - paddingBottom - lineHeight - return layout?.getLineForVertical(topOfLastLine) ?: 0 + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + textPage?.let { textPage -> + textPage.textLines.forEach { textLine -> + textLine.textChars.forEach { + if (textLine.isTitle) { + canvas.drawText( + it.charData, + it.leftBottomPosition.x.toFloat(), + it.leftBottomPosition.y.toFloat(), + ChapterProvider.titlePaint + ) + } else { + canvas.drawText( + it.charData, + it.leftBottomPosition.x.toFloat(), + it.leftBottomPosition.y.toFloat(), + ChapterProvider.contentPaint + ) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt index 14594d4e9..cbc4cb52a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt @@ -2,10 +2,8 @@ package io.legado.app.ui.book.read.page import android.annotation.SuppressLint import android.content.Context -import android.graphics.Typeface import android.graphics.drawable.Drawable import android.util.AttributeSet -import android.view.Gravity import android.widget.FrameLayout import io.legado.app.R import io.legado.app.constant.AppConst.TIME_FORMAT @@ -14,8 +12,6 @@ import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.utils.* import kotlinx.android.synthetic.main.view_book_page.view.* -import org.jetbrains.anko.sdk27.listeners.onScrollChange -import java.io.File import java.util.* @@ -39,20 +35,10 @@ class ContentView : FrameLayout { inflate(context, R.layout.view_book_page, this) upStyle() upTime() - content_text_view.customSelectionActionModeCallback = - ContentSelectActionCallback(content_text_view) - content_text_view.onScrollChange { _, _, scrollY, _, _ -> - content_text_view.layout?.getLineForVertical(scrollY)?.let { line -> - callBack?.scrollToLine(line) - } - if (content_text_view.atBottom()) { - callBack?.scrollToLast() - } - } } fun upStyle() { - ReadBookConfig.getConfig().apply { + ReadBookConfig.durConfig.apply { val rootPaddingTop = if (context.getPrefBoolean(PreferKey.hideStatusBar, false)) { //显示状态栏时隐藏header ll_header.visible() @@ -82,30 +68,13 @@ class ContentView : FrameLayout { footerPaddingRight.dp, footerPaddingBottom.dp ) - content_text_view.textSize = textSize.toFloat() - content_text_view.setLineSpacing(lineSpacingExtra.toFloat(), lineSpacingMultiplier) - content_text_view.letterSpacing = letterSpacing - content_text_view.paint.isFakeBoldText = textBold textColor().let { - content_text_view.setTextColor(it) tv_top_left.setTextColor(it) tv_top_right.setTextColor(it) tv_bottom_left.setTextColor(it) tv_bottom_right.setTextColor(it) } } - context.getPrefString(PreferKey.readBookFont)?.let { - if (it.isNotEmpty()) { - val file = File(it) - if (file.exists()) { - content_text_view.typeface = Typeface.createFromFile(it) - return@let - } else { - context.putPrefString(PreferKey.readBookFont, "") - } - } - content_text_view.typeface = Typeface.DEFAULT - } } fun setBg(bg: Drawable?) { @@ -121,15 +90,11 @@ class ContentView : FrameLayout { } fun setContent(textPage: TextPage?) { + content_text_view.setContent(textPage) if (textPage != null) { - content_text_view.gravity = Gravity.START - content_text_view.text = textPage.text tv_bottom_left.text = textPage.title pageSize = textPage.pageSize setPageIndex(textPage.index) - } else { - content_text_view.gravity = Gravity.CENTER - content_text_view.setText(R.string.data_loading) } } @@ -141,7 +106,7 @@ class ContentView : FrameLayout { } fun isTextSelected(): Boolean { - return content_text_view.selectionEnd - content_text_view.selectionStart != 0 + return false } fun contentTextView(): ContentTextView? { @@ -151,19 +116,14 @@ class ContentView : FrameLayout { fun scrollTo(pos: Int?) { if (pos != null) { content_text_view.post { - if (content_text_view.layout.lineCount >= pos) { - content_text_view.scrollTo(0, content_text_view.layout.getLineTop(pos)) - } + } } } fun scrollToBottom() { content_text_view.post { - content_text_view.scrollTo( - 0, - content_text_view.layout.getLineTop(content_text_view.lineCount) - ) + } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index f036de572..45c42f077 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt @@ -51,9 +51,9 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie override fun upSelectAble() { pageView.curPage.contentTextView()?.apply { if (context.getPrefBoolean(PreferKey.selectText)) { - setTextIsSelectable(true) +// setTextIsSelectable(true) } else { - setTextIsSelectable(false) +// setTextIsSelectable(false) } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt index dcd27ff39..0f2f283ed 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt @@ -192,8 +192,8 @@ abstract class PageDelegate(protected val pageView: PageView) { if (event.action == MotionEvent.ACTION_DOWN) { curPage.let { it.contentTextView()?.let { contentTextView -> - atTop = contentTextView.atTop() - atBottom = contentTextView.atBottom() + // atTop = contentTextView.atTop() +// atBottom = contentTextView.atBottom() } it.dispatchTouchEvent(event) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt index f6311374d..814d6427e 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt @@ -2,7 +2,6 @@ package io.legado.app.ui.book.read.page.delegate import android.graphics.Canvas import android.graphics.Matrix -import android.text.method.ScrollingMovementMethod import android.view.MotionEvent import io.legado.app.constant.PreferKey import io.legado.app.ui.book.read.page.PageView @@ -122,10 +121,10 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) { override fun upSelectAble() { pageView.curPage.contentTextView()?.apply { if (context.getPrefBoolean(PreferKey.selectText)) { - setTextIsSelectable(true) +// setTextIsSelectable(true) } else { - setTextIsSelectable(false) - movementMethod = ScrollingMovementMethod.getInstance() +// setTextIsSelectable(false) +// movementMethod = ScrollingMovementMethod.getInstance() } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt index 54f8729c7..38da6fcd6 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt @@ -3,9 +3,8 @@ package io.legado.app.ui.book.read.page.entities import android.graphics.Point data class TextChar( - val charData: Char, + val charData: String, var selected: Boolean = false, - var isReadAloud: Boolean = false, val leftBottomPosition: Point, val rightTopPosition: Point ) \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt index 81c8fe089..eb4b7d236 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt @@ -1,6 +1,9 @@ package io.legado.app.ui.book.read.page.entities data class TextLine( - val textChars: List, - val lineBottom: Int + val textChars: ArrayList = arrayListOf(), + var lineTop: Int = 0, + var lineBottom: Int = 0, + val isTitle: Boolean = false, + var isReadAloud: Boolean = false ) \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt index 9a77e26c5..9378439d6 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt @@ -1,40 +1,33 @@ package io.legado.app.ui.book.read.page.entities -import android.text.Spannable import android.text.SpannableStringBuilder import io.legado.app.App import io.legado.app.R -import io.legado.app.ui.book.read.page.ChapterProvider data class TextPage( - val index: Int, - val text: CharSequence = App.INSTANCE.getString(R.string.data_loading), - val title: String, + var index: Int = 0, + var text: CharSequence = App.INSTANCE.getString(R.string.data_loading), + var title: String = "", + val textLines: ArrayList = arrayListOf(), var pageSize: Int = 0, var chapterSize: Int = 0, var chapterIndex: Int = 0 ) { fun removePageAloudSpan(): TextPage { - if (text is SpannableStringBuilder) { - text.removeSpan(ChapterProvider.readAloudSpan) + textLines.forEach { textLine -> + textLine.isReadAloud = false } return this } fun upPageAloudSpan(pageStart: Int) { if (text is SpannableStringBuilder) { - text.removeSpan(ChapterProvider.readAloudSpan) + removePageAloudSpan() var end = text.indexOf("\n", pageStart) if (end == -1) end = text.length var start = text.lastIndexOf("\n", pageStart) if (start == -1) start = 0 - text.setSpan( - ChapterProvider.readAloudSpan, - start, - end, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE - ) } } } diff --git a/app/src/main/res/layout/dialog_read_book_style.xml b/app/src/main/res/layout/dialog_read_book_style.xml index 7783eaa07..109b5adc9 100644 --- a/app/src/main/res/layout/dialog_read_book_style.xml +++ b/app/src/main/res/layout/dialog_read_book_style.xml @@ -112,12 +112,22 @@ app:title="@string/line_size" app:layout_constraintTop_toBottomOf="@+id/dsb_text_letter_spacing" /> + + + app:layout_constraintTop_toBottomOf="@+id/dsb_paragraph_spacing" />