Merge pull request #17 from gedoor/master

get update
pull/379/head
口口吕 5 years ago committed by GitHub
commit 98706adae6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      app/src/main/assets/updateLog.md
  2. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  3. 14
      app/src/main/java/io/legado/app/help/BookHelp.kt
  4. 34
      app/src/main/java/io/legado/app/help/FirstTopListUpCallback.kt
  5. 152
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  6. 8
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  7. 9
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  8. 2
      app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
  9. 6
      app/src/main/java/io/legado/app/receiver/TimeBatteryReceiver.kt
  10. 12
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  11. 4
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
  12. 4
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  13. 4
      app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
  14. 9
      app/src/main/java/io/legado/app/ui/book/read/Help.kt
  15. 44
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  16. 7
      app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
  17. 11
      app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
  18. 6
      app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
  19. 33
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  20. 7
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  21. 91
      app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
  22. 219
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  23. 35
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  24. 9
      app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
  25. 8
      app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt
  26. 56
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  27. 48
      app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
  28. 36
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
  29. 102
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  30. 77
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
  31. 4
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt
  32. 1
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
  33. 37
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
  34. 6
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPoint.kt
  35. 3
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  36. 8
      app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
  37. 4
      app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
  38. 4
      app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt
  39. 4
      app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt
  40. 4
      app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt
  41. 8
      app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt
  42. 8
      app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
  43. 3
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
  44. 4
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
  45. 4
      app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt
  46. 8
      app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
  47. 3
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  48. 2
      app/src/main/java/io/legado/app/ui/widget/LabelsBar.kt
  49. 8
      app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
  50. 169
      app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt
  51. 9
      app/src/main/java/io/legado/app/ui/widget/recycler/VerticalDivider.kt
  52. 42
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  53. 2
      app/src/main/res/drawable/recyclerview_divider_horizontal.xml
  54. 4
      app/src/main/res/layout/dialog_read_aloud.xml
  55. 1
      app/src/main/res/layout/dialog_read_padding.xml
  56. 1
      app/src/main/res/values/strings.xml

@ -2,6 +2,10 @@
* 旧版数据导入教程:
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/02/24**
* 滚动暂时可以滚了,先这样吧,头大
* 紧急修复朗读报错的bug
**2020/02/23**
* 修复BUG
* 本地目录正则自定义完成

@ -37,4 +37,5 @@ object PreferKey {
const val lastBackup = "lastBackup"
const val bodyIndent = "textIndent"
const val shareLayout = "shareLayout"
const val readStyleSelect = "readStyleSelect"
}

@ -237,8 +237,14 @@ object BookHelp {
private var bookName: String? = null
private var bookOrigin: String? = null
private var replaceRules: List<ReplaceRule> = arrayListOf()
val bodyIndent
get() = " ".repeat(App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2))
var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)
set(value) {
field = value
App.INSTANCE.putPrefInt(PreferKey.bodyIndent, value)
bodyIndent = " ".repeat(value)
}
var bodyIndent = " ".repeat(bodyIndentCount)
fun disposeContent(
title: String,
@ -268,7 +274,9 @@ object BookHelp {
}
}
}
c = "$title\n$c"
if (!c.substringBefore("\n").contains(title)) {
c = "$title\n$c"
}
when (AppConfig.chineseConverterType) {
1 -> c = ZhConvertBootstrap.newInstance().toSimple(c)
2 -> c = ZhConvertBootstrap.newInstance().toTraditional(c)

@ -1,34 +0,0 @@
package io.legado.app.help
import androidx.recyclerview.widget.ListUpdateCallback
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.base.adapter.ItemViewHolder
class FirstTopListUpCallback : ListUpdateCallback {
var firstInsert = -1
lateinit var adapter: RecyclerView.Adapter<ItemViewHolder>
override fun onChanged(position: Int, count: Int, payload: Any?) {
adapter.notifyItemRangeChanged(position, count, payload)
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
if (toPosition == 0) {
firstInsert = 0
}
adapter.notifyItemMoved(fromPosition, toPosition)
}
override fun onInserted(position: Int, count: Int) {
if (firstInsert == -1 || firstInsert > position) {
firstInsert = position
}
adapter.notifyItemRangeInserted(position, count)
}
override fun onRemoved(position: Int, count: Int) {
adapter.notifyItemRangeRemoved(position, count)
}
}

@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.ui.book.read.page.ChapterProvider
import io.legado.app.utils.*
@ -23,12 +24,25 @@ object ReadBookConfig {
val json = String(App.INSTANCE.assets.open(readConfigFileName).readBytes())
GSON.fromJsonArray<Config>(json)!!
}
val durConfig
get() = getConfig(styleSelect)
var styleSelect
get() = App.INSTANCE.getPrefInt("readStyleSelect")
set(value) = App.INSTANCE.putPrefInt("readStyleSelect", value)
val durConfig get() = getConfig(styleSelect)
private val shareConfig get() = getConfig(5)
var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect)
set(value) {
field = value
App.INSTANCE.putPrefInt(PreferKey.readStyleSelect, value)
}
var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout)
set(value) {
field = value
App.INSTANCE.putPrefBoolean(PreferKey.shareLayout)
}
var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim)
set(value) {
field = value
isScroll = value == 3
App.INSTANCE.putPrefInt(PreferKey.pageAnim, value)
}
var isScroll = pageAnim == 3
var bg: Drawable? = null
init {
@ -40,6 +54,9 @@ object ReadBookConfig {
if (configList.size < 5) {
resetAll()
}
if (configList.size < 6) {
configList.add(Config())
}
return configList[index]
}
@ -94,17 +111,118 @@ object ReadBookConfig {
}
}
data class Config(
var bgStr: String = "#EEEEEE",//白天背景
var bgStrNight: String = "#000000",//夜间背景
var bgType: Int = 0,//白天背景类型
var bgTypeNight: Int = 0,//夜间背景类型
var darkStatusIcon: Boolean = true,//白天是否暗色状态栏
var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
var textColor: String = "#3E3D3B",//白天文字颜色
var textColorNight: String = "#adadad",//夜间文字颜色
//配置写入读取
var hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)
var hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
var textBold: Boolean
get() = if (shareLayout) shareConfig.textBold else durConfig.textBold
set(value) = if (shareLayout) shareConfig.textBold = value else durConfig.textBold = value
var textSize: Int
get() = if (shareLayout) shareConfig.textSize else durConfig.textSize
set(value) = if (shareLayout) shareConfig.textSize = value else durConfig.textSize = value
var letterSpacing: Float
get() = if (shareLayout) shareConfig.letterSpacing else durConfig.letterSpacing
set(value) =
if (shareLayout) shareConfig.letterSpacing = value else durConfig.letterSpacing = value
var lineSpacingExtra: Int
get() = if (shareLayout) shareConfig.lineSpacingExtra else durConfig.lineSpacingExtra
set(value) =
if (shareLayout) shareConfig.lineSpacingExtra = value
else durConfig.lineSpacingExtra = value
var paragraphSpacing: Int
get() = if (shareLayout) shareConfig.paragraphSpacing else durConfig.paragraphSpacing
set(value) =
if (shareLayout) shareConfig.paragraphSpacing = value
else durConfig.paragraphSpacing = value
var titleCenter: Boolean
get() = if (shareLayout) shareConfig.titleCenter else durConfig.titleCenter
set(value) =
if (shareLayout) shareConfig.titleCenter = value else durConfig.titleCenter = value
var paddingBottom: Int
get() = if (shareLayout) shareConfig.paddingBottom else durConfig.paddingBottom
set(value) =
if (shareLayout) shareConfig.paddingBottom = value else durConfig.paddingBottom = value
var paddingLeft: Int
get() = if (shareLayout) shareConfig.paddingLeft else durConfig.paddingLeft
set(value) =
if (shareLayout) shareConfig.paddingLeft = value else durConfig.paddingLeft = value
var paddingRight: Int
get() = if (shareLayout) shareConfig.paddingRight else durConfig.paddingRight
set(value) =
if (shareLayout) shareConfig.paddingRight = value else durConfig.paddingRight = value
var paddingTop: Int
get() = if (shareLayout) shareConfig.paddingTop else durConfig.paddingTop
set(value) =
if (shareLayout) shareConfig.paddingTop = value else durConfig.paddingTop = value
var headerPaddingBottom: Int
get() = if (shareLayout) shareConfig.headerPaddingBottom else durConfig.headerPaddingBottom
set(value) =
if (shareLayout) shareConfig.headerPaddingBottom = value
else durConfig.headerPaddingBottom = value
var headerPaddingLeft: Int
get() = if (shareLayout) shareConfig.headerPaddingLeft else durConfig.headerPaddingLeft
set(value) =
if (shareLayout) shareConfig.headerPaddingLeft = value
else durConfig.headerPaddingLeft = value
var headerPaddingRight: Int
get() = if (shareLayout) shareConfig.headerPaddingRight else durConfig.headerPaddingRight
set(value) =
if (shareLayout) shareConfig.headerPaddingRight = value
else durConfig.headerPaddingRight = value
var headerPaddingTop: Int
get() = if (shareLayout) shareConfig.headerPaddingTop else durConfig.headerPaddingTop
set(value) =
if (shareLayout) shareConfig.headerPaddingTop = value
else durConfig.headerPaddingTop = value
var footerPaddingBottom: Int
get() = if (shareLayout) shareConfig.footerPaddingBottom else durConfig.footerPaddingBottom
set(value) =
if (shareLayout) shareConfig.footerPaddingBottom = value
else durConfig.footerPaddingBottom = value
var footerPaddingLeft: Int
get() = if (shareLayout) shareConfig.footerPaddingLeft else durConfig.footerPaddingLeft
set(value) =
if (shareLayout) shareConfig.footerPaddingLeft = value
else durConfig.footerPaddingLeft = value
var footerPaddingRight: Int
get() = if (shareLayout) shareConfig.footerPaddingRight else durConfig.footerPaddingRight
set(value) =
if (shareLayout) shareConfig.footerPaddingRight = value
else durConfig.footerPaddingRight = value
var footerPaddingTop: Int
get() = if (shareLayout) shareConfig.footerPaddingTop else durConfig.footerPaddingTop
set(value) =
if (shareLayout) shareConfig.footerPaddingTop = value
else durConfig.footerPaddingTop = value
class Config(
private var bgStr: String = "#EEEEEE",//白天背景
private var bgStrNight: String = "#000000",//夜间背景
private var bgType: Int = 0,//白天背景类型
private var bgTypeNight: Int = 0,//夜间背景类型
private var darkStatusIcon: Boolean = true,//白天是否暗色状态栏
private var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
private var textColor: String = "#3E3D3B",//白天文字颜色
private var textColorNight: String = "#ADADAD",//夜间文字颜色
var textBold: Boolean = false,//是否粗体字
var textSize: Int = 15,//文字大小
var textSize: Int = 20,//文字大小
var letterSpacing: Float = 1f,//字间距
var lineSpacingExtra: Int = 12,//行间距
var paragraphSpacing: Int = 12,//段距
@ -138,7 +256,7 @@ object ReadBookConfig {
} else {
textColor = "#${color.hexString}"
}
ChapterProvider.upStyle(this)
ChapterProvider.upStyle()
}
fun setStatusIconDark(isDark: Boolean) {

@ -100,12 +100,16 @@ object Backup {
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
val doc = treeDoc.findFile(fileName) ?: treeDoc.createFile("", fileName)
var doc = treeDoc.findFile(fileName)
if (null != doc && doc.exists()) {
doc.delete()
}
doc = treeDoc.createFile("", fileName)
doc?.let {
DocumentUtils.writeText(
context,
file.readText(),
doc.uri
it.uri
)
}
}

@ -104,7 +104,14 @@ object Restore {
else -> Unit
}
edit.putInt(PreferKey.versionCode, App.INSTANCE.versionCode)
edit.commit()
edit.apply()
}
ReadBookConfig.apply {
styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect)
shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout)
pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim)
hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)
hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
}
}
LauncherIconHelp.changeIcon(App.INSTANCE.getPrefString(PreferKey.launcherIcon))

@ -45,7 +45,7 @@ class MediaButtonReceiver : BroadcastReceiver() {
}
}
}
return false
return true
}
private fun readAloud(context: Context) {

@ -9,12 +9,12 @@ import io.legado.app.constant.EventBus
import io.legado.app.utils.postEvent
class TimeElectricityReceiver : BroadcastReceiver() {
class TimeBatteryReceiver : BroadcastReceiver() {
companion object {
fun register(context: Context): TimeElectricityReceiver {
val receiver = TimeElectricityReceiver()
fun register(context: Context): TimeBatteryReceiver {
val receiver = TimeBatteryReceiver()
val filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_TICK)
filter.addAction(Intent.ACTION_BATTERY_CHANGED)

@ -26,10 +26,10 @@ object ReadBook {
var book: Book? = null
var inBookshelf = false
var chapterSize = 0
var callBack: CallBack? = null
var durChapterIndex = 0
var durPageIndex = 0
var isLocalBook = true
var callBack: CallBack? = null
var prevTextChapter: TextChapter? = null
var curTextChapter: TextChapter? = null
var nextTextChapter: TextChapter? = null
@ -127,7 +127,13 @@ object ReadBook {
saveRead()
}
fun curPageChanged() {
fun setPageIndex(pageIndex: Int) {
durPageIndex = pageIndex
saveRead()
curPageChanged()
}
private fun curPageChanged() {
callBack?.upPageProgress()
if (BaseReadAloudService.isRun) {
readAloud(!BaseReadAloudService.pause)
@ -306,7 +312,7 @@ object ReadBook {
}
interface CallBack {
fun upContent(position: Int = 0)
fun upContent(relativePosition: Int = 0)
fun upView()
fun upPageProgress()
fun contentLoadFinish()

@ -19,8 +19,8 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.group.GroupManageDialog
import io.legado.app.ui.book.group.GroupSelectDialog
import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_arrange_book.*
@ -60,7 +60,7 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = ArrangeBookAdapter(this, this)
recycler_view.adapter = adapter
select_action_bar.setMainActionText(R.string.move_to_group)

@ -28,8 +28,8 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@ -77,7 +77,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
.isChecked = AppConst.bookGroupAudioShow
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, Observer {
val diffResult =

@ -28,8 +28,8 @@ import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_book_group_picker.*
@ -98,7 +98,7 @@ class GroupSelectDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.menu.findItem(R.id.menu_group_audio).isVisible = false
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter

@ -10,7 +10,6 @@ import android.view.View.NO_ID
import android.widget.EditText
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Bookmark
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
@ -23,7 +22,6 @@ import io.legado.app.lib.theme.ThemeStore
import io.legado.app.service.help.Download
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.applyTint
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_download_choice.view.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@ -42,15 +40,14 @@ object Help {
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
val hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
if (hideNavigationBar) {
if (ReadBookConfig.hideNavigationBar) {
flag = flag or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
}
if (toolBarHide) {
if (App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)) {
if (ReadBookConfig.hideStatusBar) {
flag = flag or View.SYSTEM_UI_FLAG_FULLSCREEN
}
if (hideNavigationBar) {
if (ReadBookConfig.hideNavigationBar) {
flag = flag or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}
}

@ -7,6 +7,7 @@ import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.view.*
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.appcompat.view.menu.MenuItemImpl
import androidx.core.view.get
import androidx.core.view.isVisible
@ -25,7 +26,7 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.accentColor
import io.legado.app.receiver.TimeElectricityReceiver
import io.legado.app.receiver.TimeBatteryReceiver
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud
import io.legado.app.service.help.ReadBook
@ -80,7 +81,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
private val keepScreenRunnable: Runnable = Runnable { Help.keepScreenOn(window, false) }
private var screenTimeOut: Long = 0
private var timeElectricityReceiver: TimeElectricityReceiver? = null
private var timeBatteryReceiver: TimeBatteryReceiver? = null
override val pageFactory: TextPageFactory get() = page_view.pageFactory
override val headerHeight: Int get() = page_view.curPage.headerHeight
@ -105,15 +106,15 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override fun onResume() {
super.onResume()
upSystemUiVisibility()
timeElectricityReceiver = TimeElectricityReceiver.register(this)
timeBatteryReceiver = TimeBatteryReceiver.register(this)
page_view.upTime()
}
override fun onPause() {
super.onPause()
timeElectricityReceiver?.let {
timeBatteryReceiver?.let {
unregisterReceiver(it)
timeElectricityReceiver = null
timeBatteryReceiver = null
}
upSystemUiVisibility()
}
@ -379,20 +380,17 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
textActionMenu ?: let {
textActionMenu = TextActionMenu(this, this)
}
val x = cursor_left.x.toInt() + cursor_left.width
val y = if (cursor_left.y - statusBarHeight > ReadBookConfig.textSize.dp * 1.5 + 20.dp) {
(page_view.height - cursor_left.y + ReadBookConfig.textSize.dp * 1.5).toInt()
} else {
(page_view.height - cursor_left.y - cursor_left.height - 40.dp).toInt()
}
textActionMenu?.let { popup ->
if (!popup.isShowing) {
popup.showAtLocation(
cursor_left,
Gravity.BOTTOM or Gravity.START,
cursor_left.x.toInt() + cursor_left.width,
page_view.height - cursor_left.y.toInt() + ReadBookConfig.durConfig.textSize.dp + popup.height
)
popup.showAtLocation(cursor_left, Gravity.BOTTOM or Gravity.START, x, y)
} else {
popup.update(
cursor_left.x.toInt() + cursor_left.width,
page_view.height - cursor_left.y.toInt() + ReadBookConfig.durConfig.textSize.dp + popup.height,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
)
popup.update(x, y, WRAP_CONTENT, WRAP_CONTENT)
}
}
}
@ -458,9 +456,9 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
/**
* 更新内容
*/
override fun upContent(position: Int) {
override fun upContent(relativePosition: Int) {
launch {
page_view.upContent(position)
page_view.upContent(relativePosition)
}
}
@ -507,12 +505,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
viewModel.changeTo(book)
}
override fun setPageIndex(pageIndex: Int) {
ReadBook.durPageIndex = pageIndex
ReadBook.saveRead()
ReadBook.curPageChanged()
}
override fun clickCenter() {
if (BaseReadAloudService.isRun) {
showReadAloudDialog()
@ -665,7 +657,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
upSystemUiVisibility()
page_view.upBg()
page_view.upStyle()
ChapterProvider.upStyle(ReadBookConfig.durConfig)
ChapterProvider.upStyle()
if (it) {
ReadBook.loadContent()
} else {
@ -721,7 +713,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
Help.keepScreenOn(window, true)
return
}
val t = screenTimeOut - getScreenOffTime()
val t = screenTimeOut - sysScreenOffTime
if (t > 0) {
mHandler.removeCallbacks(keepScreenRunnable)
Help.keepScreenOn(window, true)

@ -10,8 +10,8 @@ import android.widget.SeekBar
import androidx.core.view.isVisible
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.buttonDisabledColor
import io.legado.app.service.help.ReadBook
@ -199,9 +199,8 @@ class ReadMenu : FrameLayout {
vw_menu_bg.onClick { runMenuOut() }
vwNavigationBar.layoutParams = vwNavigationBar.layoutParams.apply {
height =
if (context.getPrefBoolean(PreferKey.hideNavigationBar)
&& Help.isNavigationBarExist(activity)
) context.getNavigationBarHeight()
if (ReadBookConfig.hideNavigationBar && Help.isNavigationBarExist(activity))
context.navigationBarHeight
else 0
}
}

@ -14,6 +14,7 @@ import androidx.preference.PreferenceFragmentCompat
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.read.Help
import io.legado.app.utils.getPrefBoolean
@ -92,8 +93,14 @@ class MoreConfigDialog : DialogFragment() {
key: String?
) {
when (key) {
PreferKey.hideStatusBar -> postEvent(EventBus.UP_CONFIG, true)
PreferKey.hideNavigationBar -> postEvent(EventBus.UP_CONFIG, true)
PreferKey.hideStatusBar -> {
ReadBookConfig.hideStatusBar = getPrefBoolean(PreferKey.hideStatusBar)
postEvent(EventBus.UP_CONFIG, true)
}
PreferKey.hideNavigationBar -> {
ReadBookConfig.hideNavigationBar = getPrefBoolean(PreferKey.hideNavigationBar)
postEvent(EventBus.UP_CONFIG, true)
}
PreferKey.keepLight -> postEvent(key, true)
PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key))
}

@ -11,6 +11,7 @@ import io.legado.app.constant.EventBus
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.Help
import io.legado.app.utils.postEvent
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_read_padding.*
class PaddingConfigDialog : DialogFragment() {
@ -49,13 +50,14 @@ class PaddingConfigDialog : DialogFragment() {
ReadBookConfig.save()
}
private fun initData() = with(ReadBookConfig.durConfig) {
private fun initData() = with(ReadBookConfig) {
//正文
dsb_padding_top.progress = paddingTop
dsb_padding_bottom.progress = paddingBottom
dsb_padding_left.progress = paddingLeft
dsb_padding_right.progress = paddingRight
//页眉
tv_header_padding.visible(hideStatusBar)
dsb_header_padding_top.progress = headerPaddingTop
dsb_header_padding_bottom.progress = headerPaddingBottom
dsb_header_padding_left.progress = headerPaddingLeft
@ -67,7 +69,7 @@ class PaddingConfigDialog : DialogFragment() {
dsb_footer_padding_right.progress = footerPaddingRight
}
private fun initView() = with(ReadBookConfig.durConfig) {
private fun initView() = with(ReadBookConfig) {
//正文
dsb_padding_top.onChanged = {
paddingTop = it

@ -11,6 +11,7 @@ import androidx.fragment.app.DialogFragment
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.BookHelp
import io.legado.app.help.ImageLoader
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.dialogs.selector
@ -19,7 +20,9 @@ import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.book.read.Help
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.widget.font.FontSelectDialog
import io.legado.app.utils.*
import io.legado.app.utils.getPrefString
import io.legado.app.utils.postEvent
import io.legado.app.utils.putPrefString
import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.dialog_read_book_style.*
import org.jetbrains.anko.sdk27.listeners.onCheckedChange
@ -76,8 +79,8 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
}
private fun initData() {
cb_share_layout.isChecked = getPrefBoolean(PreferKey.shareLayout)
requireContext().getPrefInt(PreferKey.pageAnim).let {
cb_share_layout.isChecked = ReadBookConfig.shareLayout
ReadBookConfig.pageAnim.let {
if (it >= 0 && it < rg_page_anim.childCount) {
rg_page_anim.check(rg_page_anim[it].id)
}
@ -92,14 +95,14 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
postEvent(EventBus.UP_CONFIG, true)
}
tv_title_center.onClick {
ReadBookConfig.durConfig.apply {
ReadBookConfig.apply {
titleCenter = !titleCenter
tv_title_center.isSelected = titleCenter
}
postEvent(EventBus.UP_CONFIG, true)
}
tv_text_bold.onClick {
ReadBookConfig.durConfig.apply {
ReadBookConfig.apply {
textBold = !textBold
tv_text_bold.isSelected = textBold
}
@ -113,7 +116,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
title = getString(R.string.text_indent),
items = resources.getStringArray(R.array.indent).toList()
) { _, index ->
putPrefInt(PreferKey.bodyIndent, index)
BookHelp.bodyIndentCount = index
postEvent(EventBus.UP_CONFIG, true)
}
}
@ -125,28 +128,28 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
}
}
dsb_text_size.onChanged = {
ReadBookConfig.durConfig.textSize = it + 5
ReadBookConfig.textSize = it + 5
postEvent(EventBus.UP_CONFIG, true)
}
dsb_text_letter_spacing.onChanged = {
ReadBookConfig.durConfig.letterSpacing = (it - 50) / 100f
ReadBookConfig.letterSpacing = (it - 50) / 100f
postEvent(EventBus.UP_CONFIG, true)
}
dsb_line_size.onChanged = {
ReadBookConfig.durConfig.lineSpacingExtra = it
ReadBookConfig.lineSpacingExtra = it
postEvent(EventBus.UP_CONFIG, true)
}
dsb_paragraph_spacing.onChanged = {
ReadBookConfig.durConfig.paragraphSpacing = it
ReadBookConfig.paragraphSpacing = it
postEvent(EventBus.UP_CONFIG, true)
}
rg_page_anim.onCheckedChange { _, checkedId ->
for (i in 0 until rg_page_anim.childCount) {
if (checkedId == rg_page_anim[i].id) {
requireContext().putPrefInt(PreferKey.pageAnim, i)
ReadBookConfig.pageAnim = i
val activity = activity
if (activity is ReadBookActivity) {
activity.page_view.upPageAnim(i)
activity.page_view.upPageAnim()
}
break
}
@ -154,7 +157,9 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
}
cb_share_layout.onCheckedChangeListener = { checkBox, isChecked ->
if (checkBox.isPressed) {
putPrefBoolean(PreferKey.shareLayout, isChecked)
ReadBookConfig.shareLayout = isChecked
upStyle()
postEvent(EventBus.UP_CONFIG, true)
}
}
bg0.onClick { changeBg(0) }
@ -190,7 +195,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
}
private fun upStyle() {
ReadBookConfig.durConfig.let {
ReadBookConfig.let {
tv_title_center.isSelected = it.titleCenter
tv_text_bold.isSelected = it.textBold
dsb_text_size.progress = it.textSize - 5

@ -28,10 +28,9 @@ import io.legado.app.lib.dialogs.cancelButton
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.okButton
import io.legado.app.model.localBook.AnalyzeTxtFile
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getVerticalDivider
import kotlinx.android.synthetic.main.dialog_toc_regex.*
import kotlinx.android.synthetic.main.dialog_toc_regex_edit.*
import kotlinx.android.synthetic.main.dialog_toc_regex_edit.view.*
import kotlinx.android.synthetic.main.item_toc_regex.view.*
import kotlinx.coroutines.Dispatchers.IO
@ -76,7 +75,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
private fun initView() {
adapter = TocRegexAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
@ -150,7 +149,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
}
}
okButton {
rootView?.let {
rootView?.apply {
tocRule.name = tv_rule_name.text.toString()
tocRule.rule = tv_rule_regex.text.toString()
saveRule(tocRule, rule)

@ -10,7 +10,10 @@ import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.*
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
import io.legado.app.utils.getPrefString
import io.legado.app.utils.removePref
@ -20,10 +23,10 @@ import io.legado.app.utils.removePref
object ChapterProvider {
var viewWidth = 0
var viewHeight = 0
private var visibleWidth = 0
private var visibleHeight = 0
private var paddingLeft = 0
private var paddingTop = 0
var visibleWidth = 0
var visibleHeight = 0
var paddingLeft = 0
var paddingTop = 0
private var lineSpacingExtra = 0f
private var paragraphSpacing = 0
var typeface: Typeface = Typeface.SANS_SERIF
@ -32,7 +35,7 @@ object ChapterProvider {
private var bodyIndent = BookHelp.bodyIndent
init {
upStyle(ReadBookConfig.durConfig)
upStyle()
}
/**
@ -116,12 +119,12 @@ object ChapterProvider {
Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra, true
)
for (lineIndex in 0 until layout.lineCount) {
textPages.last().height = durY
durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
val textLine = TextLine(isTitle = true)
if (durY < visibleHeight) {
textPages.last().textLines.add(textLine)
} else {
textPages.last().height = durY
textPages.last().text = stringBuilder.toString()
stringBuilder.clear()
pageLines.add(textPages.last().textLines.size)
@ -131,10 +134,11 @@ object ChapterProvider {
textPages.add(TextPage())
textPages.last().textLines.add(textLine)
}
textLine.lineBottom = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineTop = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat()
textLine.lineBase = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineBottom = textLine.lineBase + titlePaint.fontMetrics.descent
val words =
title.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words)
@ -150,8 +154,8 @@ object ChapterProvider {
val x1 = if (i != words.lastIndex) (x + cw + d) else (x + cw)
val textChar = TextChar(
charData = char,
leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
start = paddingLeft + x,
end = paddingLeft + x1
)
textLine.textChars.add(textChar)
x = x1
@ -160,7 +164,7 @@ object ChapterProvider {
//最后一行
textLine.text = "$words\n"
stringBuilder.append("\n")
var x = if (ReadBookConfig.durConfig.titleCenter)
var x = if (ReadBookConfig.titleCenter)
(visibleWidth - layout.getLineMax(lineIndex)) / 2
else 0f
for (i in words.indices) {
@ -169,14 +173,13 @@ object ChapterProvider {
val x1 = x + cw
val textChar = TextChar(
charData = char,
leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
start = paddingLeft + x,
end = paddingLeft + x1
)
textLine.textChars.add(textChar)
x = x1
}
}
textLine.lineBottom = textLine.lineBottom + titlePaint.fontMetrics.descent
}
durY += paragraphSpacing
return durY
@ -199,12 +202,12 @@ object ChapterProvider {
Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra, true
)
for (lineIndex in 0 until layout.lineCount) {
val textLine = TextLine(isTitle = false)
textPages.last().height = durY
durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
val textLine = TextLine()
if (durY < visibleHeight) {
textPages.last().textLines.add(textLine)
} else {
textPages.last().height = durY
textPages.last().text = stringBuilder.toString()
stringBuilder.clear()
pageLines.add(textPages.last().textLines.size)
@ -214,10 +217,11 @@ object ChapterProvider {
textPages.add(TextPage())
textPages.last().textLines.add(textLine)
}
textLine.lineBottom = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineTop = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat()
textLine.lineBase = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent
var words =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words)
@ -231,8 +235,8 @@ object ChapterProvider {
val x1 = x + icw
val textChar = TextChar(
charData = bodyIndent[i].toString(),
leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
start = paddingLeft + x,
end = paddingLeft + x1
)
textLine.textChars.add(textChar)
x = x1
@ -246,8 +250,8 @@ object ChapterProvider {
val x1 = if (i != words.lastIndex) x + cw + d else x + cw
val textChar1 = TextChar(
charData = char,
leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
start = paddingLeft + x,
end = paddingLeft + x1
)
textLine.textChars.add(textChar1)
x = x1
@ -263,8 +267,8 @@ object ChapterProvider {
val x1 = x + cw
val textChar = TextChar(
charData = char,
leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
start = paddingLeft + x,
end = paddingLeft + x1
)
textLine.textChars.add(textChar)
x = x1
@ -280,14 +284,13 @@ object ChapterProvider {
val x1 = if (i != words.lastIndex) x + cw + d else x + cw
val textChar = TextChar(
charData = char,
leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
start = paddingLeft + x,
end = paddingLeft + x1
)
textLine.textChars.add(textChar)
x = x1
}
}
textLine.lineBottom = textLine.lineBottom + contentPaint.fontMetrics.descent
}
durY += paragraphSpacing
return durY
@ -297,7 +300,7 @@ object ChapterProvider {
/**
* 更新样式
*/
fun upStyle(config: ReadBookConfig.Config) {
fun upStyle() {
typeface = try {
val fontPath = App.INSTANCE.getPrefString(PreferKey.readBookFont)
if (!TextUtils.isEmpty(fontPath)) {
@ -311,34 +314,34 @@ object ChapterProvider {
}
//标题
titlePaint.isAntiAlias = true
titlePaint.color = config.textColor()
titlePaint.letterSpacing = config.letterSpacing
titlePaint.color = ReadBookConfig.durConfig.textColor()
titlePaint.letterSpacing = ReadBookConfig.letterSpacing
titlePaint.typeface = Typeface.create(typeface, Typeface.BOLD)
//正文
contentPaint.isAntiAlias = true
contentPaint.color = config.textColor()
contentPaint.letterSpacing = config.letterSpacing
val bold = if (config.textBold) Typeface.BOLD else Typeface.NORMAL
contentPaint.color = ReadBookConfig.durConfig.textColor()
contentPaint.letterSpacing = ReadBookConfig.letterSpacing
val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
contentPaint.typeface = Typeface.create(typeface, bold)
//间距
lineSpacingExtra = config.lineSpacingExtra.dp.toFloat()
paragraphSpacing = config.paragraphSpacing.dp
titlePaint.textSize = (config.textSize + 2).dp.toFloat()
contentPaint.textSize = config.textSize.dp.toFloat()
lineSpacingExtra = ReadBookConfig.lineSpacingExtra.dp.toFloat()
paragraphSpacing = ReadBookConfig.paragraphSpacing.dp
titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat()
contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat()
bodyIndent = BookHelp.bodyIndent
upSize(config)
upSize()
}
/**
* 更新View尺寸
*/
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
fun upSize() {
paddingLeft = ReadBookConfig.paddingLeft.dp
paddingTop = ReadBookConfig.paddingTop.dp
visibleWidth = viewWidth - paddingLeft - ReadBookConfig.paddingRight.dp
visibleHeight = viewHeight - paddingTop - ReadBookConfig.paddingBottom.dp
}
}

@ -3,14 +3,13 @@ package io.legado.app.ui.book.read.page
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.StaticLayout
import android.util.AttributeSet
import android.view.View
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.book.read.page.entities.TextChar
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.utils.activity
import io.legado.app.utils.getCompatColor
@ -18,6 +17,8 @@ import io.legado.app.utils.getPrefBoolean
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
var selectAble = context.getPrefBoolean(PreferKey.textSelectAble)
var upView: ((TextPage) -> Unit)? = null
private val selectedPaint by lazy {
Paint().apply {
color = context.getCompatColor(R.color.btn_bg_press_2)
@ -25,7 +26,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
}
private var callBack: CallBack
var selectAble = context.getPrefBoolean(PreferKey.textSelectAble)
private var selectLineStart = 0
private var selectCharStart = 0
private var selectLineEnd = 0
@ -36,7 +36,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
private val maxScrollOffset = 100f
private var pageOffset = 0f
private var linePos = 0
private var isLastPage = false
init {
callBack = activity as CallBack
@ -51,70 +50,105 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
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)
}
ChapterProvider.viewWidth = w
ChapterProvider.viewHeight = h
ChapterProvider.upSize()
textPage.format()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (textPage.textLines.isEmpty()) {
drawMsg(canvas, textPage.text)
if (ReadBookConfig.isScroll) {
drawScrollPage(canvas)
} else {
drawHorizontalPage(canvas)
}
}
@Suppress("DEPRECATION")
private fun drawMsg(canvas: Canvas, msg: String) {
val layout = StaticLayout(
msg, ChapterProvider.contentPaint, width,
Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false
)
val y = (height - layout.height) / 2f
for (lineIndex in 0 until layout.lineCount) {
val x = (width - layout.getLineMax(lineIndex)) / 2
val words =
msg.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
canvas.drawText(words, x, y, ChapterProvider.contentPaint)
private fun drawHorizontalPage(canvas: Canvas) {
textPage.textLines.forEach { textLine ->
drawChars(
canvas,
textLine.textChars,
textLine.lineTop,
textLine.lineBase,
textLine.lineBottom,
textLine.isTitle,
textLine.isReadAloud
)
}
}
private fun drawHorizontalPage(canvas: Canvas) {
private fun drawScrollPage(canvas: Canvas) {
val mPageOffset = pageOffset
textPage.textLines.forEach { textLine ->
val textPaint = if (textLine.isTitle) {
ChapterProvider.titlePaint
} else {
ChapterProvider.contentPaint
}
textPaint.color = if (textLine.isReadAloud) {
context.accentColor
} else {
ReadBookConfig.durConfig.textColor()
}
textLine.textChars.forEach {
canvas.drawText(
it.charData,
it.leftBottomPosition.x,
it.leftBottomPosition.y,
textPaint
)
if (it.selected) {
canvas.drawRect(
it.leftBottomPosition.x,
textLine.lineTop,
it.rightTopPosition.x,
textLine.lineBottom,
selectedPaint
)
}
val lineTop = textLine.lineTop + mPageOffset
val lineBase = textLine.lineBase + mPageOffset
val lineBottom = textLine.lineBottom + mPageOffset
drawChars(
canvas,
textLine.textChars,
lineTop,
lineBase,
lineBottom,
textLine.isTitle,
textLine.isReadAloud
)
}
pageFactory.nextPage?.textLines?.forEach { textLine ->
val yPy = mPageOffset + textPage.height - ChapterProvider.paddingTop
val lineTop = textLine.lineTop + yPy
val lineBase = textLine.lineBase + yPy
val lineBottom = textLine.lineBottom + yPy
drawChars(
canvas,
textLine.textChars,
lineTop,
lineBase,
lineBottom,
textLine.isTitle,
textLine.isReadAloud
)
}
pageFactory.prevPage?.textLines?.forEach { textLine ->
val yPy = mPageOffset + ChapterProvider.paddingTop
val lineTop = -textLine.lineTop + yPy
val lineBase = -textLine.lineBase + yPy
val lineBottom = -textLine.lineBottom + yPy
drawChars(
canvas,
textLine.textChars,
lineTop,
lineBase,
lineBottom,
textLine.isTitle,
textLine.isReadAloud
)
}
}
private fun drawChars(
canvas: Canvas,
textChars: List<TextChar>,
lineTop: Float,
lineBase: Float,
lineBottom: Float,
isTitle: Boolean,
isReadAloud: Boolean
) {
val textPaint = if (isTitle) ChapterProvider.titlePaint else ChapterProvider.contentPaint
textPaint.color =
if (isReadAloud) context.accentColor else ReadBookConfig.durConfig.textColor()
textChars.forEach {
canvas.drawText(it.charData, it.start, lineBase, textPaint)
if (it.selected) {
canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
}
}
}
fun onScroll(mOffset: Float) {
if (mOffset == 0f) return
var offset = mOffset
if (offset > maxScrollOffset) {
offset = maxScrollOffset
@ -122,78 +156,56 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
offset = -maxScrollOffset
}
if (!isLastPage || offset < 0) {
pageOffset += offset
isLastPage = false
}
// 首页
if (pageOffset < 0 && !pageFactory.hasPrev()) {
pageOffset = 0f
}
val cHeight = if (textPage.height > 0) textPage.height else height
if (offset > 0 && pageOffset > cHeight) {
pageOffset += offset
if (pageOffset > 0) {
pageFactory.moveToPrev()
textPage = pageFactory.currentPage ?: TextPage().format()
pageOffset -= textPage.height
upView?.invoke(textPage)
} else if (pageOffset < -textPage.height) {
pageOffset += textPage.height
pageFactory.moveToNext()
textPage = pageFactory.currentPage ?: TextPage().format()
upView?.invoke(textPage)
}
invalidate()
}
fun resetPageOffset() {
pageOffset = 0f
linePos = 0
isLastPage = false
}
private fun switchToPageOffset(offset: Int) {
when (offset) {
1 -> {
}
-1 -> {
}
}
}
fun selectText(x: Float, y: Float): Boolean {
fun selectText(x: Float, y: Float, select: (lineIndex: Int, charIndex: Int) -> Unit) {
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
if (y > textLine.lineTop && y < textLine.lineBottom) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.leftBottomPosition.x && x < textChar.rightTopPosition.x) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectLineStart = lineIndex
selectCharStart = charIndex
selectLineEnd = lineIndex
selectCharEnd = charIndex
upSelectedStart(
textChar.leftBottomPosition.x,
textChar.leftBottomPosition.y
)
upSelectedEnd(
textChar.rightTopPosition.x,
textChar.leftBottomPosition.y
)
return true
upSelectedStart(textChar.start, textLine.lineBottom)
upSelectedEnd(textChar.end, textLine.lineBottom)
select(lineIndex, charIndex)
}
}
break
}
}
return false
}
fun selectStartMove(x: Float, y: Float) {
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
if (y > textLine.lineTop && y < textLine.lineBottom) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.leftBottomPosition.x && x < textChar.rightTopPosition.x) {
if (x > textChar.start && x < textChar.end) {
if (selectLineStart != lineIndex || selectCharStart != charIndex) {
selectLineStart = lineIndex
selectCharStart = charIndex
upSelectedStart(
textChar.leftBottomPosition.x,
textChar.leftBottomPosition.y
)
upSelectedStart(textChar.start, textLine.lineBottom)
upSelectChars(textPage)
}
break
@ -204,18 +216,24 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
}
fun selectStartMoveIndex(lineIndex: Int, charIndex: Int) {
selectLineStart = lineIndex
selectCharStart = charIndex
val textLine = textPage.textLines[lineIndex]
val textChar = textLine.textChars[charIndex]
upSelectedStart(textChar.start, textLine.lineBottom)
upSelectChars(textPage)
}
fun selectEndMove(x: Float, y: Float) {
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
if (y > textLine.lineTop && y < textLine.lineBottom) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.leftBottomPosition.x && x < textChar.rightTopPosition.x) {
if (x > textChar.start && x < textChar.end) {
if (selectLineEnd != lineIndex || selectCharEnd != charIndex) {
selectLineEnd = lineIndex
selectCharEnd = charIndex
upSelectedEnd(
textChar.rightTopPosition.x,
textChar.leftBottomPosition.y
)
upSelectedEnd(textChar.end, textLine.lineBottom)
upSelectChars(textPage)
}
break
@ -226,6 +244,15 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
}
fun selectEndMoveIndex(lineIndex: Int, charIndex: Int) {
selectLineEnd = lineIndex
selectCharEnd = charIndex
val textLine = textPage.textLines[lineIndex]
val textChar = textLine.textChars[charIndex]
upSelectedEnd(textChar.end, textLine.lineBottom)
upSelectChars(textPage)
}
private fun upSelectChars(textPage: TextPage) {
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {

@ -7,7 +7,6 @@ import android.view.MotionEvent
import android.widget.FrameLayout
import io.legado.app.R
import io.legado.app.constant.AppConst.TIME_FORMAT
import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.utils.*
@ -25,18 +24,23 @@ class ContentView(context: Context) : FrameLayout(context) {
upStyle()
upTime()
content_text_view.upView = {
tv_bottom_left.text = it.title
pageSize = it.pageSize
setPageIndex(it.index)
}
}
fun upStyle() {
ReadBookConfig.durConfig.apply {
ReadBookConfig.apply {
tv_top_left.typeface = ChapterProvider.typeface
tv_top_right.typeface = ChapterProvider.typeface
tv_bottom_left.typeface = ChapterProvider.typeface
tv_bottom_right.typeface = ChapterProvider.typeface
//显示状态栏时隐藏header
if (context.getPrefBoolean(PreferKey.hideStatusBar, false)) {
if (hideStatusBar) {
ll_header.layoutParams =
ll_header.layoutParams.apply { height = context.getStatusBarHeight() }
ll_header.layoutParams.apply { height = context.statusBarHeight }
ll_header.setPadding(
headerPaddingLeft.dp,
headerPaddingTop.dp,
@ -47,7 +51,7 @@ class ContentView(context: Context) : FrameLayout(context) {
page_panel.setPadding(0, 0, 0, 0)
} else {
ll_header.gone()
page_panel.setPadding(0, context.getStatusBarHeight(), 0, 0)
page_panel.setPadding(0, context.statusBarHeight, 0, 0)
}
content_text_view.setPadding(
paddingLeft.dp,
@ -61,7 +65,7 @@ class ContentView(context: Context) : FrameLayout(context) {
footerPaddingRight.dp,
footerPaddingBottom.dp
)
textColor().let {
durConfig.textColor().let {
tv_top_left.setTextColor(it)
tv_top_right.setTextColor(it)
tv_bottom_left.setTextColor(it)
@ -72,10 +76,10 @@ class ContentView(context: Context) : FrameLayout(context) {
val headerHeight: Int
get() {
return if (context.getPrefBoolean(PreferKey.hideStatusBar, false)) {
return if (ReadBookConfig.hideStatusBar) {
ll_header.height
} else {
context.getStatusBarHeight()
context.statusBarHeight
}
}
@ -93,10 +97,11 @@ class ContentView(context: Context) : FrameLayout(context) {
fun setContent(textPage: TextPage?) {
if (textPage != null) {
content_text_view.setContent(textPage)
tv_bottom_left.text = textPage.title
pageSize = textPage.pageSize
setPageIndex(textPage.index)
content_text_view.resetPageOffset()
content_text_view.setContent(textPage)
}
}
@ -115,19 +120,27 @@ class ContentView(context: Context) : FrameLayout(context) {
content_text_view.selectAble = selectAble
}
fun selectText(e: MotionEvent): Boolean {
fun selectText(e: MotionEvent, select: (lineIndex: Int, charIndex: Int) -> Unit) {
val y = e.y - headerHeight
return content_text_view.selectText(e.x, y)
return content_text_view.selectText(e.x, y, select)
}
fun selectStartMove(x: Float, y: Float) {
content_text_view.selectStartMove(x, y - headerHeight)
}
fun selectStartMoveIndex(lineIndex: Int, charIndex: Int) {
content_text_view.selectStartMoveIndex(lineIndex, charIndex)
}
fun selectEndMove(x: Float, y: Float) {
content_text_view.selectEndMove(x, y - headerHeight)
}
fun selectEndMoveIndex(lineIndex: Int, charIndex: Int) {
content_text_view.selectEndMoveIndex(lineIndex, charIndex)
}
fun cancelSelect() {
content_text_view.cancelSelect()
}

@ -1,15 +1,11 @@
package io.legado.app.ui.book.read.page
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.TextChapter
interface DataSource {
val isScrollDelegate: Boolean
val pageIndex: Int
fun setPageIndex(pageIndex: Int)
fun getChapterPosition(): Int
val pageIndex: Int get() = ReadBook.durChapterPos()
fun getCurrentChapter(): TextChapter?
@ -20,5 +16,4 @@ interface DataSource {
fun hasNextChapter(): Boolean
fun hasPrevChapter(): Boolean
}

@ -8,13 +8,13 @@ abstract class PageFactory<DATA>(protected val dataSource: DataSource) {
abstract fun moveToNext():Boolean
abstract fun moveToPrevious(): Boolean
abstract fun moveToPrev(): Boolean
abstract fun nextPage(): DATA?
abstract val nextPage: DATA?
abstract fun previousPage(): DATA?
abstract val prevPage: DATA?
abstract fun currentPage(): DATA?
abstract val currentPage: DATA?
abstract fun hasNext(): Boolean

@ -6,13 +6,11 @@ import android.graphics.Canvas
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.delegate.*
import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.utils.activity
import io.legado.app.utils.getPrefInt
class PageView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs),
@ -37,7 +35,7 @@ class PageView(context: Context, attrs: AttributeSet) :
upBg()
setWillNotDraw(false)
pageFactory = TextPageFactory(this)
upPageAnim(context.getPrefInt(PreferKey.pageAnim))
upPageAnim()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
@ -64,8 +62,9 @@ class PageView(context: Context, attrs: AttributeSet) :
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
pageDelegate?.onTouch(event)
callBack.screenOffTimerStart()
return pageDelegate?.onTouch(event) ?: super.onTouchEvent(event)
return true
}
fun onDestroy() {
@ -76,7 +75,7 @@ class PageView(context: Context, attrs: AttributeSet) :
fun fillPage(direction: PageDelegate.Direction) {
when (direction) {
PageDelegate.Direction.PREV -> {
pageFactory.moveToPrevious()
pageFactory.moveToPrev()
upContent()
}
PageDelegate.Direction.NEXT -> {
@ -87,10 +86,10 @@ class PageView(context: Context, attrs: AttributeSet) :
}
}
fun upPageAnim(pageAnim: Int) {
fun upPageAnim() {
pageDelegate?.onDestroy()
pageDelegate = null
pageDelegate = when (pageAnim) {
pageDelegate = when (ReadBookConfig.pageAnim) {
0 -> CoverPageDelegate(this)
1 -> SlidePageDelegate(this)
2 -> SimulationPageDelegate(this)
@ -100,15 +99,17 @@ class PageView(context: Context, attrs: AttributeSet) :
upContent()
}
fun upContent(position: Int = 0) {
pageFactory.let {
when (position) {
-1 -> prevPage.setContent(it.previousPage())
1 -> nextPage.setContent(it.nextPage())
fun upContent(relativePosition: Int = 0) {
if (ReadBookConfig.isScroll) {
curPage.setContent(pageFactory.currentPage)
} else {
when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevPage)
1 -> nextPage.setContent(pageFactory.nextPage)
else -> {
curPage.setContent(it.currentPage())
nextPage.setContent(it.nextPage())
prevPage.setContent(it.previousPage())
curPage.setContent(pageFactory.currentPage)
nextPage.setContent(pageFactory.nextPage)
prevPage.setContent(pageFactory.prevPage)
}
}
}
@ -163,20 +164,6 @@ class PageView(context: Context, attrs: AttributeSet) :
nextPage.upBattery(battery)
}
override val isScrollDelegate: Boolean
get() = pageDelegate is ScrollPageDelegate
override val pageIndex: Int
get() = ReadBook.durChapterPos()
override fun setPageIndex(pageIndex: Int) {
callBack.setPageIndex(pageIndex)
}
override fun getChapterPosition(): Int {
return ReadBook.durChapterIndex
}
override fun getCurrentChapter(): TextChapter? {
return if (callBack.isInitFinish) ReadBook.textChapter(0) else null
}
@ -198,19 +185,8 @@ class PageView(context: Context, attrs: AttributeSet) :
}
interface CallBack {
val isInitFinish: Boolean
/**
* 保存页数
*/
fun setPageIndex(pageIndex: Int)
/**
* 点击屏幕中间
*/
fun clickCenter()
fun screenOffTimerStart()
}
}

@ -14,63 +14,65 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
}
override fun moveToFirst() {
dataSource.setPageIndex(0)
ReadBook.setPageIndex(0)
}
override fun moveToLast() = with(dataSource) {
getCurrentChapter()?.let {
if (it.pageSize() == 0) {
setPageIndex(0)
ReadBook.setPageIndex(0)
} else {
setPageIndex(it.pageSize().minus(1))
ReadBook.setPageIndex(it.pageSize().minus(1))
}
} ?: setPageIndex(0)
} ?: ReadBook.setPageIndex(0)
}
override fun moveToNext(): Boolean = with(dataSource) {
return if (hasNext()) {
if (getCurrentChapter()?.isLastIndex(pageIndex) == true
) {
if (getCurrentChapter()?.isLastIndex(pageIndex) == true) {
ReadBook.moveToNextChapter(false)
} else {
setPageIndex(pageIndex.plus(1))
ReadBook.setPageIndex(pageIndex.plus(1))
}
true
} else
false
}
override fun moveToPrevious(): Boolean = with(dataSource) {
override fun moveToPrev(): Boolean = with(dataSource) {
return if (hasPrev()) {
if (pageIndex <= 0) {
ReadBook.moveToPrevChapter(false)
} else {
setPageIndex(pageIndex.minus(1))
ReadBook.setPageIndex(pageIndex.minus(1))
}
true
} else
false
}
override fun currentPage(): TextPage? = with(dataSource) {
return getCurrentChapter()?.page(pageIndex)
}
override val currentPage: TextPage?
get() = with(dataSource) {
return getCurrentChapter()?.page(pageIndex)
}
override fun nextPage(): TextPage? = with(dataSource) {
getCurrentChapter()?.let {
if (pageIndex < it.pageSize() - 1) {
return getCurrentChapter()?.page(pageIndex + 1)?.removePageAloudSpan()
override val nextPage: TextPage?
get() = with(dataSource) {
getCurrentChapter()?.let {
if (pageIndex < it.pageSize() - 1) {
return getCurrentChapter()?.page(pageIndex + 1)?.removePageAloudSpan()
}
}
return getNextChapter()?.page(0)?.removePageAloudSpan()
}
return getNextChapter()?.page(0)?.removePageAloudSpan()
}
override fun previousPage(): TextPage? = with(dataSource) {
if (pageIndex > 0) {
return getCurrentChapter()?.page(pageIndex - 1)?.removePageAloudSpan()
override val prevPage: TextPage?
get() = with(dataSource) {
if (pageIndex > 0) {
return getCurrentChapter()?.page(pageIndex - 1)?.removePageAloudSpan()
}
return getPreviousChapter()?.lastPage()?.removePageAloudSpan()
}
return getPreviousChapter()?.lastPage()?.removePageAloudSpan()
}
}

@ -6,19 +6,29 @@ import kotlin.math.abs
abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageView) {
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
override fun onTouch(event: MotionEvent) {
when (event.action) {
MotionEvent.ACTION_MOVE -> {
if (isTextSelected) {
selectText(event)
} else {
onScroll(event)
}
}
}
super.onTouch(event)
}
private fun onScroll(event: MotionEvent) {
//判断是否移动了
if (!isMoved) {
if (abs(distanceX) > abs(distanceY)) {
if (distanceX < 0) {
isMoved = abs(startX - event.x) > slop || abs(startY - event.y) > slop
if (isMoved) {
if (event.x - startX > 0) {
//如果上一页不存在
if (!hasPrev()) {
noNext = true
return true
return
}
setDirection(Direction.PREV)
setBitmap()
@ -26,21 +36,19 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
//如果不存在表示没有下一页了
if (!hasNext()) {
noNext = true
return true
return
}
setDirection(Direction.NEXT)
setBitmap()
}
isMoved = true
}
}
if (isMoved) {
isCancel = if (mDirection == Direction.NEXT) distanceX < 0 else distanceX > 0
isCancel = if (mDirection == Direction.NEXT) touchX > lastX else touchX < lastX
isRunning = true
//设置触摸点
setTouchPoint(e2.x, e2.y)
setTouchPoint(event.x, event.y)
}
return isMoved
}
}

@ -6,15 +6,14 @@ import android.graphics.Canvas
import android.graphics.RectF
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.annotation.CallSuper
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
import com.google.android.material.snackbar.Snackbar
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.ui.book.read.page.ContentView
import io.legado.app.ui.book.read.page.PageView
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.screenshot
import kotlin.math.abs
@ -25,44 +24,36 @@ abstract class PageDelegate(protected val pageView: PageView) :
pageView.width * 0.66f, pageView.height * 0.66f
)
protected val context: Context = pageView.context
protected val slop = ViewConfiguration.get(context).scaledTouchSlop
//起始点
protected var startX: Float = 0f
protected var startY: Float = 0f
//上一个触碰点
protected var lastX: Float = 0f
protected var lastY: Float = 0f
//触碰点
protected var touchX: Float = 0f
protected var touchY: Float = 0f
protected val nextPage: ContentView
get() = pageView.nextPage
protected val curPage: ContentView
get() = pageView.curPage
protected val prevPage: ContentView
get() = pageView.prevPage
protected val nextPage: ContentView get() = pageView.nextPage
protected val curPage: ContentView get() = pageView.curPage
protected val prevPage: ContentView get() = pageView.prevPage
protected var bitmap: Bitmap? = null
protected var viewWidth: Int = pageView.width
protected var viewHeight: Int = pageView.height
//textView在顶端或低端
protected var atTop: Boolean = false
protected var atBottom: Boolean = false
private val snackBar: Snackbar by lazy {
Snackbar.make(pageView, "", Snackbar.LENGTH_SHORT)
}
private val scroller: Scroller by lazy {
Scroller(pageView.context, FastOutLinearInInterpolator())
Scroller(pageView.context, DecelerateInterpolator())
}
private val detector: GestureDetector by lazy {
GestureDetector(pageView.context, this).apply {
setIsLongpressEnabled(context.getPrefBoolean(PreferKey.textSelectAble))
}
GestureDetector(pageView.context, this)
}
var isMoved = false
@ -74,26 +65,32 @@ abstract class PageDelegate(protected val pageView: PageView) :
var isRunning = false
var isStarted = false
var isTextSelected = false
var selectedOnDown = false
var firstLineIndex: Int = 0
var firstCharIndex: Int = 0
open fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
lastX = x
lastY = y
touchX = x
touchY = y
if (invalidate) {
invalidate()
pageView.invalidate()
}
}
open fun setTouchPoint(x: Float, y: Float, invalidate: Boolean = true) {
lastX = touchX
lastY = touchY
touchX = x
touchY = y
if (invalidate) {
invalidate()
pageView.invalidate()
}
onScroll()
@ -103,10 +100,6 @@ abstract class PageDelegate(protected val pageView: PageView) :
detector.setIsLongpressEnabled(selectAble)
}
protected fun invalidate() {
pageView.invalidate()
}
open fun fling(
startX: Int, startY: Int, velocityX: Int, velocityY: Int,
minX: Int, maxX: Int, minY: Int, maxY: Int
@ -114,7 +107,7 @@ abstract class PageDelegate(protected val pageView: PageView) :
scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY)
isRunning = true
isStarted = true
invalidate()
pageView.invalidate()
}
protected fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int) {
@ -127,13 +120,13 @@ abstract class PageDelegate(protected val pageView: PageView) :
)
isRunning = true
isStarted = true
invalidate()
pageView.invalidate()
}
private fun stopScroll() {
isRunning = false
isStarted = false
invalidate()
pageView.invalidate()
bitmap?.recycle()
bitmap = null
}
@ -141,7 +134,7 @@ abstract class PageDelegate(protected val pageView: PageView) :
fun setViewSize(width: Int, height: Int) {
viewWidth = width
viewHeight = height
invalidate()
pageView.invalidate()
centerRectF.set(
width * 0.33f, height * 0.33f,
width * 0.66f, height * 0.66f
@ -152,7 +145,6 @@ abstract class PageDelegate(protected val pageView: PageView) :
if (scroller.computeScrollOffset()) {
setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat())
} else if (isStarted) {
setTouchPoint(scroller.finalX.toFloat(), scroller.finalY.toFloat(), false)
onAnimStop()
stopScroll()
}
@ -192,14 +184,13 @@ abstract class PageDelegate(protected val pageView: PageView) :
onAnimStart()
}
abstract fun onAnimStart()//scroller start
open fun onAnimStart() {}//scroller start
abstract fun onDraw(canvas: Canvas)//绘制
open fun onDraw(canvas: Canvas) {}//绘制
abstract fun onAnimStop()//scroller finish
open fun onAnimStop() {}//scroller finish
open fun onScroll() {//移动contentView, slidePage
}
open fun onScroll() {}//移动contentView, slidePage
@CallSuper
open fun setDirection(direction: Direction) {
@ -218,18 +209,17 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 触摸事件处理
*/
@CallSuper
open fun onTouch(event: MotionEvent): Boolean {
if (isStarted) return false
open fun onTouch(event: MotionEvent) {
if (isStarted) return
if (!detector.onTouchEvent(event)) {
//GestureDetector.onFling小幅移动不会触发,所以要自己判断
if (event.action == MotionEvent.ACTION_UP && isMoved) {
if (isTextSelected) {
isTextSelected = false
if (selectedOnDown) {
selectedOnDown = false
}
if (!noNext) onAnimStart()
}
}
return true
}
/**
@ -238,6 +228,8 @@ abstract class PageDelegate(protected val pageView: PageView) :
override fun onDown(e: MotionEvent): Boolean {
if (isTextSelected) {
curPage.cancelSelect()
isTextSelected = false
selectedOnDown = true
}
//是否移动
isMoved = false
@ -258,8 +250,8 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 单击
*/
override fun onSingleTapUp(e: MotionEvent): Boolean {
if (isTextSelected) {
isTextSelected = false
if (selectedOnDown) {
selectedOnDown = false
return true
}
val x = e.x
@ -272,15 +264,11 @@ abstract class PageDelegate(protected val pageView: PageView) :
AppConfig.clickAllNext
) {
//设置动画方向
if (!hasNext()) {
return true
}
if (!hasNext()) return true
setDirection(Direction.NEXT)
setBitmap()
} else {
if (!hasPrev()) {
return true
}
if (!hasPrev()) return true
setDirection(Direction.PREV)
setBitmap()
}
@ -294,7 +282,25 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 长按选择
*/
override fun onLongPress(e: MotionEvent) {
isTextSelected = curPage.selectText(e)
curPage.selectText(e) { lineIndex, charIndex ->
isTextSelected = true
firstLineIndex = lineIndex
firstCharIndex = charIndex
}
}
protected fun selectText(event: MotionEvent) {
curPage.selectText(event) { lineIndex, charIndex ->
if (lineIndex > firstLineIndex
|| (lineIndex == firstLineIndex && charIndex > firstCharIndex)
) {
curPage.selectStartMoveIndex(firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(lineIndex, charIndex)
} else {
curPage.selectEndMoveIndex(firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(lineIndex, charIndex)
}
}
}
/**

@ -1,6 +1,5 @@
package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas
import android.view.MotionEvent
import android.view.VelocityTracker
import io.legado.app.ui.book.read.page.PageView
@ -21,70 +20,38 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
)
}
override fun onDraw(canvas: Canvas) {
curPage.onScroll(lastY - touchY)
override fun onScroll() {
curPage.onScroll(touchY - lastY)
}
override fun onAnimStop() {
if (!isCancel) {
pageView.fillPage(mDirection)
override fun onTouch(event: MotionEvent) {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
setStartPoint(event.x, event.y)
abort()
mVelocity.clear()
}
MotionEvent.ACTION_MOVE -> {
if (isTextSelected) {
selectText(event)
} else {
onScroll(event)
}
}
}
super.onTouch(event)
}
override fun onDown(e: MotionEvent): Boolean {
abort()
mVelocity.clear()
mVelocity.addMovement(e)
return super.onDown(e)
}
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
mVelocity.addMovement(e2)
private fun onScroll(event: MotionEvent) {
mVelocity.addMovement(event)
mVelocity.computeCurrentVelocity(velocityDuration)
if (!isMoved && abs(distanceX) < abs(distanceY)) {
if (distanceY < 0) {
if (atTop) {
//如果上一页不存在
if (!hasPrev()) {
noNext = true
return true
}
setDirection(Direction.PREV)
}
} else {
if (atBottom) {
//如果不存在表示没有下一页了
if (!hasNext()) {
noNext = true
return true
}
setDirection(Direction.NEXT)
}
}
isMoved = true
setTouchPoint(event.x, event.y)
if (!isMoved) {
isMoved = abs(startX - event.x) > slop || abs(startY - event.y) > slop
}
if (isMoved) {
isRunning = true
//设置触摸点
setTouchPoint(e2.x, e2.y)
}
return isMoved
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent?,
velocityX: Float,
velocityY: Float
): Boolean {
mVelocity.addMovement(e2)
return super.onFling(e1, e2, velocityX, velocityY)
}
override fun onDestroy() {

@ -3,6 +3,6 @@ package io.legado.app.ui.book.read.page.entities
data class TextChar(
val charData: String,
var selected: Boolean = false,
val leftBottomPosition: TextPoint,
val rightTopPosition: TextPoint
val start: Float,
val end: Float
)

@ -4,6 +4,7 @@ data class TextLine(
var text: String = "",
val textChars: ArrayList<TextChar> = arrayListOf(),
var lineTop: Float = 0f,
var lineBase: Float = 0f,
var lineBottom: Float = 0f,
val isTitle: Boolean = false,
var isReadAloud: Boolean = false

@ -1,7 +1,10 @@
package io.legado.app.ui.book.read.page.entities
import android.text.Layout
import android.text.StaticLayout
import io.legado.app.App
import io.legado.app.R
import io.legado.app.ui.book.read.page.ChapterProvider
data class TextPage(
var index: Int = 0,
@ -14,6 +17,40 @@ data class TextPage(
var height: Int = 0
) {
@Suppress("DEPRECATION")
fun format(): TextPage {
if (textLines.isEmpty() && ChapterProvider.visibleWidth > 0) {
val layout = StaticLayout(
text, ChapterProvider.contentPaint, ChapterProvider.visibleWidth,
Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false
)
var y = (ChapterProvider.visibleHeight - layout.height) / 2f
if (y < 0) y = 0f
for (lineIndex in 0 until layout.lineCount) {
val textLine = TextLine()
textLine.lineTop = (ChapterProvider.paddingTop + y -
(layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)))
textLine.lineBase = (ChapterProvider.paddingTop + y -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex)))
textLine.lineBottom =
textLine.lineBase + ChapterProvider.contentPaint.fontMetrics.descent
var x = (ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2
textLine.text =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
for (i in textLine.text.indices) {
val char = textLine.text[i].toString()
val cw = StaticLayout.getDesiredWidth(char, ChapterProvider.contentPaint)
val x1 = x + cw
textLine.textChars.add(TextChar(charData = char, start = x, end = x1))
x = x1
}
textLines.add(textLine)
}
height = ChapterProvider.visibleHeight
}
return this
}
fun removePageAloudSpan(): TextPage {
textLines.forEach { textLine ->
textLine.isReadAloud = false

@ -1,6 +0,0 @@
package io.legado.app.ui.book.read.page.entities
data class TextPoint(
val x: Float,
val y: Float
)

@ -30,6 +30,7 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_book_source.*
@ -109,7 +110,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = BookSourceAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()

@ -22,7 +22,11 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.utils.*
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@ -60,7 +64,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.bookSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf<String>()

@ -18,8 +18,8 @@ import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import io.legado.app.utils.putPrefBoolean
import kotlinx.android.synthetic.main.dialog_change_source.*
@ -92,7 +92,7 @@ class ChangeSourceDialog : DialogFragment(),
private fun initRecyclerView() {
adapter = ChangeSourceAdapter(requireContext(), this)
recycler_view.layoutManager = LinearLayoutManager(context)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {

@ -14,7 +14,7 @@ import io.legado.app.R
import io.legado.app.base.VMBaseFragment
import io.legado.app.data.entities.Bookmark
import io.legado.app.lib.theme.ATH
import io.legado.app.utils.getVerticalDivider
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModelOfActivity
import kotlinx.android.synthetic.main.fragment_bookmark.*
@ -38,7 +38,7 @@ class BookmarkFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragment_
ATH.applyEdgeEffectColor(recycler_view)
adapter = BookmarkAdapter(this)
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
}

@ -15,7 +15,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.UpLinearLayoutManager
import io.legado.app.utils.getVerticalDivider
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.observeEvent
import kotlinx.android.synthetic.main.fragment_chapter_list.*
@ -48,7 +48,7 @@ class ChapterListFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragme
adapter = ChapterListAdapter(requireContext(), this)
mLayoutManager = UpLinearLayoutManager(requireContext())
recycler_view.layoutManager = mLayoutManager
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
}

@ -10,7 +10,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.widget.recycler.LoadMoreView
import io.legado.app.utils.getVerticalDivider
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_explore_show.*
import org.jetbrains.anko.startActivity
@ -34,7 +34,7 @@ class ExploreShowActivity : VMBaseActivity<ExploreShowViewModel>(R.layout.activi
private fun initRecyclerView() {
adapter = ExploreShowAdapter(this, this)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
recycler_view.adapter = adapter
loadMoreView = LoadMoreView(this)
adapter.addFooterView(loadMoreView)

@ -15,7 +15,11 @@ import io.legado.app.R
import io.legado.app.constant.Theme
import io.legado.app.ui.filechooser.adapter.FileAdapter
import io.legado.app.ui.filechooser.adapter.PathAdapter
import io.legado.app.utils.*
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.FileUtils
import io.legado.app.utils.applyTint
import io.legado.app.utils.gone
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_file_chooser.*
@ -133,7 +137,7 @@ class FileChooserDialog : DialogFragment(),
fileAdapter = FileAdapter(requireContext(), this)
pathAdapter = PathAdapter(requireContext(), this)
rv_file.addItemDecoration(rv_file.getVerticalDivider())
rv_file.addItemDecoration(VerticalDivider(requireContext()))
rv_file.layoutManager = LinearLayoutManager(activity)
rv_file.adapter = fileAdapter

@ -22,7 +22,11 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.utils.*
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@ -60,7 +64,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.replaceRuleDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf<String>()

@ -28,6 +28,7 @@ import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.replacerule.edit.ReplaceEditDialog
import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_replace_rule.*
@ -77,7 +78,7 @@ class ReplaceRuleActivity : VMBaseActivity<ReplaceRuleViewModel>(R.layout.activi
recycler_view.layoutManager = LinearLayoutManager(this)
adapter = ReplaceRuleAdapter(this, this)
recycler_view.adapter = adapter
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
itemTouchCallback.isCanDrag = true

@ -15,7 +15,7 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.ui.rss.read.ReadRssActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.ui.widget.recycler.LoadMoreView
import io.legado.app.utils.getVerticalDivider
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_rss_artivles.*
import kotlinx.android.synthetic.main.view_load_more.view.*
@ -70,7 +70,7 @@ class RssArticlesActivity : VMBaseActivity<RssArticlesViewModel>(R.layout.activi
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssArticlesAdapter(this, this)
recycler_view.adapter = adapter
loadMoreView = LoadMoreView(this)

@ -10,7 +10,7 @@ import io.legado.app.base.BaseActivity
import io.legado.app.data.entities.RssStar
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.rss.read.ReadRssActivity
import io.legado.app.utils.getVerticalDivider
import io.legado.app.ui.widget.recycler.VerticalDivider
import kotlinx.android.synthetic.main.view_refresh_recycler.*
import org.jetbrains.anko.startActivity
@ -29,7 +29,7 @@ class RssFavoritesActivity : BaseActivity(R.layout.activity_rss_favorites),
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssFavoritesAdapter(this, this)
recycler_view.adapter = adapter
}

@ -22,7 +22,11 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.utils.*
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@ -60,7 +64,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.rssSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf<String>()

@ -29,6 +29,7 @@ import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_source.*
@ -104,7 +105,7 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssSourceAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()

@ -12,6 +12,7 @@ class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context,
private val unUsedViews = arrayListOf<TextView>()
private val usedViews = arrayListOf<TextView>()
var textSize = 12f
fun setLabels(labels: Array<String>) {
clear()
@ -51,6 +52,7 @@ class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context,
unUsedViews.removeAt(unUsedViews.lastIndex)
}
}
tv.textSize = textSize
tv.text = label
addView(tv)
}

@ -12,8 +12,8 @@ import com.google.android.material.appbar.AppBarLayout
import io.legado.app.R
import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.activity
import io.legado.app.utils.getNavigationBarHeight
import io.legado.app.utils.getStatusBarHeight
import io.legado.app.utils.navigationBarHeight
import io.legado.app.utils.statusBarHeight
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.bottomPadding
import org.jetbrains.anko.topPadding
@ -132,11 +132,11 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a
}
if (a.getBoolean(R.styleable.TitleBar_fitStatusBar, true)) {
topPadding = context.getStatusBarHeight()
topPadding = context.statusBarHeight
}
if (a.getBoolean(R.styleable.TitleBar_fitNavigationBar, false)) {
bottomPadding = context.getNavigationBarHeight()
bottomPadding = context.navigationBarHeight
}
backgroundColor = context.primaryColor

@ -0,0 +1,169 @@
package io.legado.app.ui.widget.recycler
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.View
import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.roundToInt
/**
* 不画最后一条分隔线
*/
class DividerNoLast(context: Context, orientation: Int) :
RecyclerView.ItemDecoration() {
companion object {
const val HORIZONTAL = LinearLayout.HORIZONTAL
const val VERTICAL = LinearLayout.VERTICAL
}
private val tag = "DividerItem"
private val attrs = intArrayOf(android.R.attr.listDivider)
private var mDivider: Drawable? = null
/**
* Current orientation. Either [.HORIZONTAL] or [.VERTICAL].
*/
private var mOrientation = 0
private val mBounds = Rect()
init {
val a = context.obtainStyledAttributes(attrs)
mDivider = a.getDrawable(0)
if (mDivider == null) {
Log.w(
tag, "@android:attr/listDivider was not set in the theme used for this "
+ "DividerItemDecoration. Please set that attribute all call setDrawable()"
)
}
a.recycle()
setOrientation(orientation)
}
/**
* Sets the orientation for this divider. This should be called if
* [RecyclerView.LayoutManager] changes orientation.
*
* @param orientation [.HORIZONTAL] or [.VERTICAL]
*/
fun setOrientation(orientation: Int) {
require(!(orientation != HORIZONTAL && orientation != VERTICAL)) { "Invalid orientation. It should be either HORIZONTAL or VERTICAL" }
mOrientation = orientation
}
/**
* Sets the [Drawable] for this divider.
*
* @param drawable Drawable that should be used as a divider.
*/
fun setDrawable(drawable: Drawable) {
requireNotNull(drawable) { "Drawable cannot be null." }
mDivider = drawable
}
/**
* @return the [Drawable] for this divider.
*/
fun getDrawable(): Drawable? {
return mDivider
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
if (parent.layoutManager == null || mDivider == null) {
return
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent)
} else {
drawHorizontal(c, parent)
}
}
private fun drawVertical(
canvas: Canvas,
parent: RecyclerView
) {
canvas.save()
val left: Int
val right: Int
if (parent.clipToPadding) {
left = parent.paddingLeft
right = parent.width - parent.paddingRight
canvas.clipRect(
left, parent.paddingTop, right,
parent.height - parent.paddingBottom
)
} else {
left = 0
right = parent.width
}
val childCount = parent.childCount
for (i in 0 until childCount - 1) {
val child = parent.getChildAt(i)
parent.getDecoratedBoundsWithMargins(child, mBounds)
val bottom = mBounds.bottom + child.translationY.roundToInt()
val top = bottom - mDivider!!.intrinsicHeight
mDivider!!.setBounds(left, top, right, bottom)
mDivider!!.draw(canvas)
}
canvas.restore()
}
private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
canvas.save()
val top: Int
val bottom: Int
if (parent.clipToPadding) {
top = parent.paddingTop
bottom = parent.height - parent.paddingBottom
canvas.clipRect(
parent.paddingLeft, top,
parent.width - parent.paddingRight, bottom
)
} else {
top = 0
bottom = parent.height
}
val childCount = parent.childCount
for (i in 0 until childCount - 1) {
val child = parent.getChildAt(i)
parent.layoutManager!!.getDecoratedBoundsWithMargins(child, mBounds)
val right = mBounds.right + child.translationX.roundToInt()
val left = right - mDivider!!.intrinsicWidth
mDivider!!.setBounds(left, top, right, bottom)
mDivider!!.draw(canvas)
}
canvas.restore()
}
override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State
) {
if (mDivider == null) {
outRect[0, 0, 0] = 0
return
}
if (mOrientation == VERTICAL) {
outRect[0, 0, 0] = mDivider!!.intrinsicHeight
} else {
val childAdapterPosition = parent.getChildAdapterPosition(view)
val lastCount = parent.adapter!!.itemCount - 1
//如果不是最后一条 正常赋值 如果是最后一条 赋值为0
if (childAdapterPosition != lastCount) {
outRect[0, 0, mDivider!!.intrinsicWidth] = 0
} else {
outRect[0, 0, 0] = 0
}
}
}
}

@ -1,15 +1,16 @@
package io.legado.app.utils
package io.legado.app.ui.widget.recycler
import android.content.Context
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
class VerticalDivider(context: Context) : DividerItemDecoration(context, VERTICAL) {
fun RecyclerView.getVerticalDivider(): DividerItemDecoration {
return DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
init {
ContextCompat.getDrawable(context, R.drawable.ic_divider)?.let {
this.setDrawable(it)
}
}
}

@ -1,3 +1,4 @@
@file:Suppress("unused")
package io.legado.app.utils
import android.annotation.SuppressLint
@ -30,7 +31,6 @@ fun Context.getPrefBoolean(key: String, defValue: Boolean = false) =
fun Context.putPrefBoolean(key: String, value: Boolean = false) =
defaultSharedPreferences.edit { putBoolean(key, value) }
fun Context.getPrefInt(key: String, defValue: Int = 0) =
defaultSharedPreferences.getInt(key, defValue)
@ -69,24 +69,27 @@ fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
/**
* 系统息屏时间
*/
fun Context.getScreenOffTime(): Int {
var screenOffTime = 0
try {
screenOffTime = Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
} catch (e: Exception) {
e.printStackTrace()
}
return screenOffTime
val Context.sysScreenOffTime: Int
get() {
var screenOffTime = 0
try {
screenOffTime = Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
} catch (e: Exception) {
e.printStackTrace()
}
return screenOffTime
}
fun Context.getStatusBarHeight(): Int {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
val Context.statusBarHeight: Int
get() {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
}
fun Context.getNavigationBarHeight(): Int {
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
val Context.navigationBarHeight: Int
get() {
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
}
fun Context.shareText(title: String, text: String) {
@ -149,10 +152,11 @@ fun Context.sysIsDarkMode(): Boolean {
/**
* 获取电量
*/
fun Context.getBettery(): Int {
val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus = registerReceiver(null, iFilter)
return batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
val Context.sysBattery: Int
get() {
val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus = registerReceiver(null, iFilter)
return batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
}
fun Context.openUrl(url: String) {

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
<size android:width="1dp" />
<size android:width="0.5dp" />
<!--分割线的颜色-->
<solid android:color="@color/bg_divider_line" />
</shape>

@ -154,8 +154,8 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:tooltipText="@string/other_setting"
android:contentDescription="@string/other_setting"
android:tooltipText="@string/other_aloud_setting"
android:contentDescription="@string/other_aloud_setting"
android:src="@drawable/ic_settings"
android:tint="@color/tv_text_default"
tools:ignore="UnusedAttribute" />

@ -8,6 +8,7 @@
android:orientation="vertical">
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_header_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"

@ -626,4 +626,5 @@
<string name="more_menu">更多菜单</string>
<string name="reduce"></string>
<string name="plus"></string>
<string name="other_aloud_setting">其它朗读设置</string>
</resources>

Loading…
Cancel
Save