每本书可以单独设置翻页动画,在菜单里

pull/443/head
gedoor 4 years ago
parent c68923b728
commit 703e503810
  1. 22
      app/src/main/java/io/legado/app/data/entities/Book.kt
  2. 126
      app/src/main/java/io/legado/app/help/ContentHelp.kt
  3. 3
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  4. 14
      app/src/main/java/io/legado/app/lib/dialogs/AndroidSelectors.kt
  5. 13
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  6. 21
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  7. 14
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivityHelp.kt
  8. 10
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  9. 13
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  10. 36
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  11. 14
      app/src/main/res/menu/read_book.xml
  12. 4
      app/src/main/res/values-night/strings.xml
  13. 1
      app/src/main/res/values-zh-rHK/strings.xml
  14. 1
      app/src/main/res/values-zh-rTW/strings.xml
  15. 1
      app/src/main/res/values-zh/strings.xml
  16. 1
      app/src/main/res/values/strings.xml

@ -114,7 +114,7 @@ data class Book(
return charset(charset ?: "UTF-8")
}
fun rConfig(): ReadConfig {
private fun config(): ReadConfig {
if (readConfig == null) {
readConfig = ReadConfig()
}
@ -122,11 +122,27 @@ data class Book(
}
fun setUseReplaceRule(useReplaceRule: Boolean) {
rConfig().useReplaceRule = useReplaceRule
config().useReplaceRule = useReplaceRule
}
fun getUseReplaceRule(): Boolean {
return rConfig().useReplaceRule
return config().useReplaceRule
}
fun getReSegment(): Boolean {
return config().reSegment
}
fun setReSegment(reSegment: Boolean) {
config().reSegment = reSegment
}
fun getPageAnim(): Int {
return config().pageAnim
}
fun setPageAnim(pageAnim: Int) {
config().pageAnim = pageAnim
}
fun getFolderName(): String {

@ -14,73 +14,71 @@ object ContentHelp {
* @param chapterName 标题
* @return
*/
fun lightNovelParagraph2(content: String, chapterName: String): String {
fun reSegment(content: String, chapterName: String): String {
var content1 = content
if (true) {
val content2: String
val chapterNameLength = chapterName.trim { it <= ' ' }.length
content2 = if (chapterNameLength > 1) {
val regexp =
chapterName.trim { it <= ' ' }.replace("\\s+".toRegex(), "(\\\\s*)")
// 质量较低的页面,章节内可能重复出现章节标题
if (chapterNameLength > 5) content1.replace(regexp.toRegex(), "")
.trim { it <= ' ' } else content1.replaceFirst(
"^\\s*" + regexp.toRegex(),
""
).trim { it <= ' ' }
} else {
content1
}
var p = content2
.replace("&quot;".toRegex(), "")
.replace("[::]['\"‘”“]+".toRegex(), ":“")
.replace("[\"”“]+[\\s]*[\"”“][\\s\"”“]*".toRegex(), "\n")
.split("\n(\\s*)".toRegex()).toTypedArray()
//初始化StringBuffer的长度,在原content的长度基础上做冗余
var buffer = StringBuffer((content1.length * 1.15).toInt())
// 章节的文本格式为章节标题-空行-首段,所以处理段落时需要略过第一行文本。
buffer.append(" ")
if (chapterName.trim { it <= ' ' } != p[0].trim { it <= ' ' }) {
// 去除段落内空格。unicode 3000 象形字间隔(中日韩符号和标点),不包含在\s内
buffer.append(p[0].replace("[\u3000\\s]+".toRegex(), ""))
}
val content2: String
val chapterNameLength = chapterName.trim { it <= ' ' }.length
content2 = if (chapterNameLength > 1) {
val regexp =
chapterName.trim { it <= ' ' }.replace("\\s+".toRegex(), "(\\\\s*)")
// 质量较低的页面,章节内可能重复出现章节标题
if (chapterNameLength > 5) content1.replace(regexp.toRegex(), "")
.trim { it <= ' ' } else content1.replaceFirst(
"^\\s*" + regexp.toRegex(),
""
).trim { it <= ' ' }
} else {
content1
}
var p = content2
.replace("&quot;".toRegex(), "")
.replace("[::]['\"‘”“]+".toRegex(), ":“")
.replace("[\"”“]+[\\s]*[\"”“][\\s\"”“]*".toRegex(), "\n")
.split("\n(\\s*)".toRegex()).toTypedArray()
//初始化StringBuffer的长度,在原content的长度基础上做冗余
var buffer = StringBuffer((content1.length * 1.15).toInt())
// 章节的文本格式为章节标题-空行-首段,所以处理段落时需要略过第一行文本。
buffer.append(" ")
if (chapterName.trim { it <= ' ' } != p[0].trim { it <= ' ' }) {
// 去除段落内空格。unicode 3000 象形字间隔(中日韩符号和标点),不包含在\s内
buffer.append(p[0].replace("[\u3000\\s]+".toRegex(), ""))
}
//如果原文存在分段错误,需要把段落重新黏合
for (i in 1 until p.size) {
if (match(MARK_SENTENCES_END, buffer[buffer.length - 1])) buffer.append("\n")
// 段落开头以外的地方不应该有空格
// 去除段落内空格。unicode 3000 象形字间隔(中日韩符号和标点),不包含在\s内
buffer.append(p[i].replace("[\u3000\\s]".toRegex(), ""))
}
// 预分段预处理
// ”“处理为”\n“。
// ”。“处理为”。\n“。不考虑“?” “!”的情况。
// ”。xxx处理为 ”。\n xxx
p = buffer.toString()
.replace("[\"”“]+[\\s]*[\"”“]+".toRegex(), "\n")
.replace("[\"”“]+(?。!?!~)[\"”“]+".toRegex(), "$1\n")
.replace("[\"”“]+(?。!?!~)([^\"”“])".toRegex(), "$1\n$2")
.replace(
"([问说喊唱叫骂道着答])[\\.。]".toRegex(),
"$1。\n"
) // .replaceAll("([\\.。\\!!??])([^\"”“]+)[::][\"”“]", "$1\n$2:“")
.split("\n".toRegex()).toTypedArray()
buffer = StringBuffer((content1.length * 1.15).toInt())
for (s in p) {
buffer.append("\n")
buffer.append(
findNewLines(s)
)
}
buffer = reduceLength(buffer)
content1 = ("$chapterName\n\n" + buffer.toString() // 处理章节头部空格和换行
.replaceFirst("^\\s+".toRegex(), "")
.replace("\\s*[\"”“]+[\\s]*[\"”“][\\s\"”“]*".toRegex(), "\n")
.replace("[::][”“\"\\s]+".toRegex(), ":“")
.replace("\n[\"“”]([^\n\"“”]+)([,:,:][\"”“])([^\n\"“”]+)".toRegex(), "\n$1:“$3")
.replace("\n(\\s*)".toRegex(), "\n"))
//如果原文存在分段错误,需要把段落重新黏合
for (i in 1 until p.size) {
if (match(MARK_SENTENCES_END, buffer[buffer.length - 1])) buffer.append("\n")
// 段落开头以外的地方不应该有空格
// 去除段落内空格。unicode 3000 象形字间隔(中日韩符号和标点),不包含在\s内
buffer.append(p[i].replace("[\u3000\\s]".toRegex(), ""))
}
// 预分段预处理
// ”“处理为”\n“。
// ”。“处理为”。\n“。不考虑“?” “!”的情况。
// ”。xxx处理为 ”。\n xxx
p = buffer.toString()
.replace("[\"”“]+[\\s]*[\"”“]+".toRegex(), "\n")
.replace("[\"”“]+(?。!?!~)[\"”“]+".toRegex(), "$1\n")
.replace("[\"”“]+(?。!?!~)([^\"”“])".toRegex(), "$1\n$2")
.replace(
"([问说喊唱叫骂道着答])[\\.。]".toRegex(),
"$1。\n"
) // .replaceAll("([\\.。\\!!??])([^\"”“]+)[::][\"”“]", "$1\n$2:“")
.split("\n".toRegex()).toTypedArray()
buffer = StringBuffer((content1.length * 1.15).toInt())
for (s in p) {
buffer.append("\n")
buffer.append(
findNewLines(s)
)
}
buffer = reduceLength(buffer)
content1 = ("$chapterName\n\n" + buffer.toString() // 处理章节头部空格和换行
.replaceFirst("^\\s+".toRegex(), "")
.replace("\\s*[\"”“]+[\\s]*[\"”“][\\s\"”“]*".toRegex(), "\n")
.replace("[::][”“\"\\s]+".toRegex(), ":“")
.replace("\n[\"“”]([^\n\"“”]+)([,:,:][\"”“])([^\n\"“”]+)".toRegex(), "\n$1:“$3")
.replace("\n(\\s*)".toRegex(), "\n"))
return content1
}

@ -96,7 +96,6 @@ object ReadBookConfig {
bgMeanColor = color
}
}
isScroll = pageAnim == 3
}
fun save() {
@ -154,7 +153,6 @@ object ReadBookConfig {
App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value)
}
}
var isScroll = pageAnim == 3
val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true)
val textFullJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textFullJustify, true)
val textBottomJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textBottomJustify, true)
@ -167,7 +165,6 @@ object ReadBookConfig {
get() = config.curPageAnim()
set(value) {
config.setCurPageAnim(value)
isScroll = pageAnim == 3
}
var textFont: String

@ -42,3 +42,17 @@ fun Context.selector(
show().applyTint()
}
}
fun Context.selector(
titleSource: Int? = null,
items: List<CharSequence>,
onClick: (DialogInterface, Int) -> Unit
) {
with(AndroidAlertBuilder(this)) {
if (titleSource != null) {
this.title = getString(titleSource)
}
items(items, onClick)
show().applyTint()
}
}

@ -11,6 +11,7 @@ import io.legado.app.data.entities.ReadRecord
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService
@ -57,6 +58,7 @@ object ReadBook {
curTextChapter = null
nextTextChapter = null
titleDate.postValue(book.name)
callBack?.upPageAnim()
upWebBook(book)
ImageProvider.clearAllCache()
synchronized(this) {
@ -431,6 +433,16 @@ object ReadBook {
private val imageStyle get() = webBook?.bookSource?.ruleContent?.imageStyle
fun pageAnim(): Int {
book?.let {
return if (it.getPageAnim() < 0)
ReadBookConfig.pageAnim
else
it.getPageAnim()
}
return ReadBookConfig.pageAnim
}
fun setCharset(charset: String) {
book?.let {
it.charset = charset
@ -460,6 +472,7 @@ object ReadBook {
fun upView()
fun pageChanged()
fun contentLoadFinish()
fun upPageAnim()
}
}

@ -91,7 +91,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override val scope: CoroutineScope get() = this
override val isInitFinish: Boolean get() = viewModel.isInitFinish
override val isScroll: Boolean get() = page_view.isScroll
private val mHandler = Handler()
private val keepScreenRunnable: Runnable =
Runnable { ReadBookActivityHelp.keepScreenOn(window, false) }
@ -228,8 +228,9 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
R.id.menu_group_text -> item.isVisible = book.isLocalTxt()
R.id.menu_group_login ->
item.isVisible = !ReadBook.webBook?.bookSource?.loginUrl.isNullOrEmpty()
else -> if (item.itemId == R.id.menu_enable_replace) {
item.isChecked = book.getUseReplaceRule()
else -> when (item.itemId) {
R.id.menu_enable_replace -> item.isChecked = book.getUseReplaceRule()
R.id.menu_re_segment -> item.isChecked = book.getReSegment()
}
}
}
@ -267,6 +268,14 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.getUseReplaceRule()
onReplaceRuleSave()
}
R.id.menu_re_segment -> ReadBook.book?.let {
it.setReSegment(!it.getReSegment())
menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment()
ReadBook.loadContent(false)
}
R.id.menu_page_anim -> ReadBookActivityHelp.showPageAnimConfig(this) {
page_view.upPageAnim()
}
R.id.menu_book_info -> ReadBook.book?.let {
startActivity<BookInfoActivity>(
Pair("name", it.name),
@ -582,6 +591,12 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
}
}
override fun upPageAnim() {
launch {
page_view.upPageAnim()
}
}
/**
* 页面改变
*/

@ -189,6 +189,20 @@ object ReadBookActivityHelp {
}.show().applyTint()
}
fun showPageAnimConfig(context: Context, success: () -> Unit) = with(context) {
val items = arrayListOf<String>()
items.add(getString(R.string.btn_default_s))
items.add(getString(R.string.page_anim_cover))
items.add(getString(R.string.page_anim_slide))
items.add(getString(R.string.page_anim_simulation))
items.add(getString(R.string.page_anim_scroll))
items.add(getString(R.string.page_anim_none))
selector(R.string.page_anim, items) { _, i ->
ReadBook.book?.setPageAnim(i - 1)
success()
}
}
fun isPrevKey(context: Context, keyCode: Int): Boolean {
val prevKeysStr = context.getPrefString(PreferKey.prevKeys)
return prevKeysStr?.split(",")?.contains(keyCode.toString()) ?: false

@ -17,6 +17,7 @@ import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.widget.font.FontSelectDialog
import io.legado.app.utils.*
@ -138,6 +139,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
TipConfigDialog().show(childFragmentManager, "tipConfigDialog")
}
rg_page_anim.onCheckedChange { _, checkedId ->
ReadBook.book?.setPageAnim(-1)
ReadBookConfig.pageAnim = rg_page_anim.getIndexById(checkedId)
callBack?.page_view?.upPageAnim()
}
@ -216,10 +218,12 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
}
private fun upView() {
ReadBookConfig.let {
if (it.pageAnim >= 0 && it.pageAnim < rg_page_anim.childCount) {
rg_page_anim.check(rg_page_anim[it.pageAnim].id)
ReadBook.pageAnim().let {
if (it >= 0 && it < rg_page_anim.childCount) {
rg_page_anim.check(rg_page_anim[it].id)
}
}
ReadBookConfig.let {
dsb_text_size.progress = it.textSize - 5
dsb_text_letter_spacing.progress = (it.letterSpacing * 100).toInt() + 50
dsb_line_size.progress = it.lineSpacingExtra

@ -86,7 +86,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
textPage.textLines.forEach { textLine ->
draw(canvas, textLine, relativeOffset)
}
if (!ReadBookConfig.isScroll) return
if (!callBack.isScroll) return
//滚动翻页
if (!pageFactory.hasNext()) return
val nextPage = relativePage(1)
@ -215,7 +215,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
relativeOffset = relativeOffset(relativePos)
if (relativePos > 0) {
//滚动翻页
if (!ReadBookConfig.isScroll) return
if (!callBack.isScroll) return
if (relativeOffset >= ChapterProvider.visibleHeight) return
}
val page = relativePage(relativePos)
@ -252,7 +252,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
relativeOffset = relativeOffset(relativePos)
if (relativePos > 0) {
//滚动翻页
if (!ReadBookConfig.isScroll) return
if (!callBack.isScroll) return
if (relativeOffset >= ChapterProvider.visibleHeight) return
}
for ((lineIndex, textLine) in relativePage(relativePos).textLines.withIndex()) {
@ -295,7 +295,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
relativeOffset = relativeOffset(relativePos)
if (relativePos > 0) {
//滚动翻页
if (!ReadBookConfig.isScroll) return
if (!callBack.isScroll) return
if (relativeOffset >= ChapterProvider.visibleHeight) return
}
Log.e("y", "$y")
@ -358,7 +358,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
private fun upSelectChars() {
val last = if (ReadBookConfig.isScroll) 2 else 0
val last = if (callBack.isScroll) 2 else 0
for (relativePos in 0..last) {
for ((lineIndex, textLine) in relativePage(relativePos).textLines.withIndex()) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
@ -397,7 +397,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
fun cancelSelect() {
val last = if (ReadBookConfig.isScroll) 2 else 0
val last = if (callBack.isScroll) 2 else 0
for (relativePos in 0..last) {
relativePage(relativePos).textLines.forEach { textLine ->
textLine.textChars.forEach {
@ -499,5 +499,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
val headerHeight: Int
val pageFactory: TextPageFactory
val scope: CoroutineScope
val isScroll: Boolean
}
}

@ -29,7 +29,13 @@ class PageView(context: Context, attrs: AttributeSet) :
val callBack: CallBack get() = activity as CallBack
var pageFactory: TextPageFactory = TextPageFactory(this)
var pageDelegate: PageDelegate? = null
private set(value) {
field?.onDestroy()
field = null
field = value
upContent()
}
var isScroll = ReadBook.pageAnim() == 3
var prevPage: ContentView = ContentView(context)
var curPage: ContentView = ContentView(context)
var nextPage: ContentView = ContentView(context)
@ -304,20 +310,28 @@ class PageView(context: Context, attrs: AttributeSet) :
}
fun upPageAnim() {
pageDelegate?.onDestroy()
pageDelegate = null
pageDelegate = when (ReadBookConfig.pageAnim) {
0 -> CoverPageDelegate(this)
1 -> SlidePageDelegate(this)
2 -> SimulationPageDelegate(this)
3 -> ScrollPageDelegate(this)
else -> NoAnimPageDelegate(this)
isScroll = ReadBook.pageAnim() == 3
when (ReadBook.pageAnim()) {
0 -> if (pageDelegate !is CoverPageDelegate) {
pageDelegate = CoverPageDelegate(this)
}
1 -> if (pageDelegate !is SlidePageDelegate) {
pageDelegate = SlidePageDelegate(this)
}
2 -> if (pageDelegate !is SimulationPageDelegate) {
pageDelegate = SimulationPageDelegate(this)
}
3 -> if (pageDelegate !is ScrollPageDelegate) {
pageDelegate = ScrollPageDelegate(this)
}
else -> if (pageDelegate !is NoAnimPageDelegate) {
pageDelegate = NoAnimPageDelegate(this)
}
}
upContent()
}
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
if (ReadBookConfig.isScroll && !callBack.isAutoPage) {
if (isScroll && !callBack.isAutoPage) {
curPage.setContent(pageFactory.currentPage, resetPageOffset)
} else {
curPage.resetPageOffset()

@ -70,6 +70,12 @@
android:title="@string/copy_text"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_page_anim"
android:icon="@drawable/ic_auto_page"
android:title="@string/page_anim"
app:showAsAction="never" />
<item
android:id="@+id/menu_enable_replace"
android:icon="@drawable/ic_find_replace"
@ -78,6 +84,14 @@
android:checked="true"
app:showAsAction="never" />
<item
android:id="@+id/menu_re_segment"
android:icon="@drawable/ic_toc"
android:title="@string/re_segment"
android:checkable="true"
android:checked="false"
app:showAsAction="never" />
<item
android:id="@+id/menu_update_toc"
android:icon="@drawable/ic_update"

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

@ -773,5 +773,6 @@
<string name="theme_name">主题名称</string>
<string name="auto_clear_expired">自动清除过期搜索数据</string>
<string name="auto_clear_expired_summary">超过一天的搜索数据</string>
<string name="re_segment">重新分段</string>
</resources>

@ -773,5 +773,6 @@
<string name="theme_name">主题名称</string>
<string name="auto_clear_expired">自动清除过期搜索数据</string>
<string name="auto_clear_expired_summary">超过一天的搜索数据</string>
<string name="re_segment">重新分段</string>
</resources>

@ -776,5 +776,6 @@
<string name="theme_name">主题名称</string>
<string name="auto_clear_expired">自动清除过期搜索数据</string>
<string name="auto_clear_expired_summary">超过一天的搜索数据</string>
<string name="re_segment">重新分段</string>
</resources>

@ -779,5 +779,6 @@
<string name="theme_name">主题名称</string>
<string name="auto_clear_expired">自动清除过期搜索数据</string>
<string name="auto_clear_expired_summary">超过一天的搜索数据</string>
<string name="re_segment">重新分段</string>
</resources>

Loading…
Cancel
Save