Merge pull request #18 from gedoor/master

get update
pull/379/head
口口吕 5 years ago committed by GitHub
commit 5b59bcde90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      app/src/main/assets/updateLog.md
  2. 12
      app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
  3. 2
      app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
  4. 5
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  5. 6
      app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
  6. 10
      app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
  7. 8
      app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
  8. 507
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  9. 36
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  10. 6
      app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
  11. 8
      app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt
  12. 9
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  13. 58
      app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
  14. 3
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
  15. 45
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  16. 20
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt
  17. 7
      app/src/main/res/layout/dialog_read_padding.xml
  18. 2
      build.gradle
  19. 4
      gradle/wrapper/gradle-wrapper.properties

@ -2,6 +2,9 @@
* 旧版数据导入教程:
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/02/25**
* 优化文本选择和滚动,感觉很完美了
**2020/02/24**
* 滚动暂时可以滚了,先这样吧,头大
* 紧急修复朗读报错的bug

@ -14,19 +14,19 @@ interface BookSourceDao {
@Query("select * from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey order by customOrder asc")
fun liveDataSearch(searchKey: String = ""): LiveData<List<BookSource>>
@Query("select * from book_sources where enabled = 1 and enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' order by customOrder asc")
@Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc")
fun liveExplore(): LiveData<List<BookSource>>
@Query("select * from book_sources where enabled = 1 and enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' and (bookSourceGroup like :key or bookSourceName like :key) order by customOrder asc")
@Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and (bookSourceGroup like :key or bookSourceName like :key) order by customOrder asc")
fun liveExplore(key: String): LiveData<List<BookSource>>
@Query("select bookSourceGroup from book_sources where bookSourceGroup is not null and bookSourceGroup <> ''")
@Query("select bookSourceGroup from book_sources where trim(bookSourceGroup) <> ''")
fun liveGroup(): LiveData<List<String>>
@Query("select bookSourceGroup from book_sources where enabled = 1 and bookSourceGroup is not null and bookSourceGroup <> ''")
@Query("select bookSourceGroup from book_sources where enabled = 1 and trim(bookSourceGroup) <> ''")
fun liveGroupEnabled(): LiveData<List<String>>
@Query("select bookSourceGroup from book_sources where enabled = 1 and enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' and bookSourceGroup is not null and bookSourceGroup <> ''")
@Query("select bookSourceGroup from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and trim(bookSourceGroup) <> ''")
fun liveGroupExplore(): LiveData<List<String>>
@Query("select distinct enabled from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey")
@ -41,7 +41,7 @@ interface BookSourceDao {
@Query("select * from book_sources where enabled = 1 and bookSourceGroup like '%' || :group || '%'")
fun getEnabledByGroup(group: String): List<BookSource>
@get:Query("select * from book_sources where bookUrlPattern is not null || bookUrlPattern <> ''")
@get:Query("select * from book_sources where trim(bookUrlPattern) <> ''")
val hasBookUrlPattern: List<BookSource>
@get:Query("select * from book_sources where bookSourceGroup is null or bookSourceGroup = ''")

@ -28,7 +28,7 @@ interface RssSourceDao {
@Query("SELECT * FROM rssSources where enabled = 1 order by customOrder")
fun liveEnabled(): LiveData<List<RssSource>>
@Query("select sourceGroup from rssSources where sourceGroup is not null and sourceGroup <> ''")
@Query("select sourceGroup from rssSources where trim(sourceGroup) <> ''")
fun liveGroup(): LiveData<List<String>>
@get:Query("select min(customOrder) from rssSources")

@ -8,7 +8,6 @@ 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
import androidx.core.view.size
@ -403,8 +402,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
/**
* 文本选择菜单操作
*/
override fun onMenuItemSelected(item: MenuItemImpl): Boolean {
when (item.itemId) {
override fun onMenuItemSelected(itemId: Int): Boolean {
when (itemId) {
R.id.menu_replace -> {
ReplaceEditDialog.show(supportFragmentManager, pattern = selectedText)
return true

@ -27,7 +27,7 @@ import kotlinx.android.synthetic.main.popup_action_menu.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.toast
@SuppressLint("RestrictedApi")
class TextActionMenu(private val context: Context, private val callBack: CallBack) :
PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) {
@ -70,7 +70,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.onClick {
getItem(holder.layoutPosition)?.let {
if (!callBack.onMenuItemSelected(it)) {
if (!callBack.onMenuItemSelected(it.itemId)) {
onMenuItemSelected(it)
}
}
@ -150,7 +150,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
interface CallBack {
val selectedText: String
fun onMenuItemSelected(item: MenuItemImpl): Boolean
fun onMenuItemSelected(itemId: Int): Boolean
fun onMenuActionFinally()
}

@ -10,6 +10,7 @@ import io.legado.app.R
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.dp
import io.legado.app.utils.postEvent
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_read_padding.*
@ -51,13 +52,20 @@ class PaddingConfigDialog : DialogFragment() {
}
private fun initData() = with(ReadBookConfig) {
if (hideStatusBar) {
tv_header_padding.visible()
dsb_header_padding_top.visible()
dsb_header_padding_bottom.visible()
dsb_header_padding_left.visible()
dsb_header_padding_right.visible()
tv_body_padding.setPadding(0, 10.dp, 0, 10.dp)
}
//正文
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

@ -23,10 +23,12 @@ import io.legado.app.utils.removePref
object ChapterProvider {
var viewWidth = 0
var viewHeight = 0
var visibleWidth = 0
var visibleHeight = 0
var paddingLeft = 0
var paddingTop = 0
var visibleWidth = 0
var visibleHeight = 0
var visibleRight = 0
var visibleBottom = 0
private var lineSpacingExtra = 0f
private var paragraphSpacing = 0
var typeface: Typeface = Typeface.SANS_SERIF
@ -342,6 +344,8 @@ object ChapterProvider {
paddingTop = ReadBookConfig.paddingTop.dp
visibleWidth = viewWidth - paddingLeft - ReadBookConfig.paddingRight.dp
visibleHeight = viewHeight - paddingTop - ReadBookConfig.paddingBottom.dp
visibleRight = paddingLeft + visibleWidth
visibleBottom = paddingTop + visibleHeight
}
}

@ -3,6 +3,7 @@ package io.legado.app.ui.book.read.page
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View
import io.legado.app.R
@ -26,16 +27,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
}
private var callBack: CallBack
private var selectLineStart = 0
private var selectCharStart = 0
private var selectLineEnd = 0
private var selectCharEnd = 0
private val visibleRect = RectF()
private val selectStart = arrayOf(0, 0, 0)
private val selectEnd = arrayOf(0, 0, 0)
private var textPage: TextPage = TextPage()
//滚动参数
private val pageFactory: TextPageFactory get() = callBack.pageFactory
private val maxScrollOffset = 100f
private var pageOffset = 0f
private var linePos = 0
init {
callBack = activity as CallBack
@ -53,38 +52,30 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
ChapterProvider.viewWidth = w
ChapterProvider.viewHeight = h
ChapterProvider.upSize()
visibleRect.set(
ChapterProvider.paddingLeft.toFloat(),
ChapterProvider.paddingTop.toFloat(),
ChapterProvider.visibleRight.toFloat(),
ChapterProvider.visibleBottom.toFloat()
)
textPage.format()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (ReadBookConfig.isScroll) {
drawScrollPage(canvas)
} else {
drawHorizontalPage(canvas)
}
}
private fun drawHorizontalPage(canvas: Canvas) {
textPage.textLines.forEach { textLine ->
drawChars(
canvas,
textLine.textChars,
textLine.lineTop,
textLine.lineBase,
textLine.lineBottom,
textLine.isTitle,
textLine.isReadAloud
)
}
canvas.clipRect(visibleRect)
drawPage(canvas)
}
private fun drawScrollPage(canvas: Canvas) {
val mPageOffset = pageOffset
/**
* 绘制页面
*/
private fun drawPage(canvas: Canvas) {
var relativeOffset = relativeOffset(0)
textPage.textLines.forEach { textLine ->
val lineTop = textLine.lineTop + mPageOffset
val lineBase = textLine.lineBase + mPageOffset
val lineBottom = textLine.lineBottom + mPageOffset
val lineTop = textLine.lineTop + relativeOffset
val lineBase = textLine.lineBase + relativeOffset
val lineBottom = textLine.lineBottom + relativeOffset
drawChars(
canvas,
textLine.textChars,
@ -95,11 +86,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
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
if (!ReadBookConfig.isScroll) return
//滚动翻页
val nextPage = relativePage(1)
relativeOffset = relativeOffset(1)
nextPage.textLines.forEach { textLine ->
val lineTop = textLine.lineTop + relativeOffset
val lineBase = textLine.lineBase + relativeOffset
val lineBottom = textLine.lineBottom + relativeOffset
drawChars(
canvas,
textLine.textChars,
@ -110,23 +104,28 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
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
)
relativeOffset = relativeOffset(2)
if (relativeOffset < ChapterProvider.visibleHeight) {
relativePage(2).textLines.forEach { textLine ->
val lineTop = textLine.lineTop + relativeOffset
val lineBase = textLine.lineBase + relativeOffset
val lineBottom = textLine.lineBottom + relativeOffset
drawChars(
canvas,
textLine.textChars,
lineTop,
lineBase,
lineBottom,
textLine.isTitle,
textLine.isReadAloud
)
}
}
}
/**
* 绘制文字
*/
private fun drawChars(
canvas: Canvas,
textChars: List<TextChar>,
@ -147,6 +146,9 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
}
/**
* 滚动事件
*/
fun onScroll(mOffset: Float) {
if (mOffset == 0f) return
var offset = mOffset
@ -159,13 +161,13 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
pageOffset += offset
if (pageOffset > 0) {
pageFactory.moveToPrev()
textPage = pageFactory.currentPage ?: TextPage().format()
textPage = pageFactory.currentPage
pageOffset -= textPage.height
upView?.invoke(textPage)
} else if (pageOffset < -textPage.height) {
pageOffset += textPage.height
pageFactory.moveToNext()
textPage = pageFactory.currentPage ?: TextPage().format()
textPage = pageFactory.currentPage
upView?.invoke(textPage)
}
invalidate()
@ -173,99 +175,290 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
fun resetPageOffset() {
pageOffset = 0f
linePos = 0
}
fun selectText(x: Float, y: Float, select: (lineIndex: Int, charIndex: Int) -> Unit) {
/**
* 选择初始文字
*/
fun selectText(
x: Float,
y: Float,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
) {
if (!visibleRect.contains(x, y)) return
var relativeOffset = relativeOffset(0)
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
if (y > textLine.lineTop && y < textLine.lineBottom) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectStart[0] = 0
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 0
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(0, lineIndex, charIndex)
return
}
}
return
}
}
if (!ReadBookConfig.isScroll) return
//滚动翻页
relativeOffset = relativeOffset(1)
if (relativeOffset >= ChapterProvider.visibleHeight) return
val nextPage = relativePage(1)
for ((lineIndex, textLine) in nextPage.textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectStart[0] = 1
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 1
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(1, lineIndex, charIndex)
return
}
}
return
}
}
relativeOffset = relativeOffset(2)
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectLineStart = lineIndex
selectCharStart = charIndex
selectLineEnd = lineIndex
selectCharEnd = charIndex
upSelectedStart(textChar.start, textLine.lineBottom)
upSelectedEnd(textChar.end, textLine.lineBottom)
select(lineIndex, charIndex)
selectStart[0] = 2
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 2
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(2, lineIndex, charIndex)
return
}
}
break
return
}
}
}
/**
* 开始选择符移动
*/
fun selectStartMove(x: Float, y: Float) {
if (!visibleRect.contains(x, y)) return
var relativeOffset = relativeOffset(0)
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
if (y > textLine.lineTop && y < textLine.lineBottom) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectLineStart != lineIndex || selectCharStart != charIndex) {
selectLineStart = lineIndex
selectCharStart = charIndex
upSelectedStart(textChar.start, textLine.lineBottom)
upSelectChars(textPage)
if (selectStart[0] != 0 || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
if (selectToInt(0, lineIndex, charIndex) > selectToInt(selectEnd)) {
return
}
selectStart[0] = 0
selectStart[1] = lineIndex
selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectChars()
}
break
return
}
}
break
return
}
}
if (!ReadBookConfig.isScroll) return
//滚动翻页
relativeOffset = relativeOffset(1)
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(1).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectStart[0] != 1 || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
if (selectToInt(1, lineIndex, charIndex) > selectToInt(selectEnd)) {
return
}
selectStart[0] = 1
selectStart[1] = lineIndex
selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectChars()
}
return
}
}
return
}
}
relativeOffset = relativeOffset(2)
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectStart[0] != 2 || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
if (selectToInt(2, lineIndex, charIndex) > selectToInt(selectEnd)) {
return
}
selectStart[0] = 2
selectStart[1] = lineIndex
selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectChars()
}
return
}
}
return
}
}
}
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) {
if (!visibleRect.contains(x, y)) return
var relativeOffset = relativeOffset(0)
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
if (y > textLine.lineTop && y < textLine.lineBottom) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectEnd[0] != 0 || selectEnd[1] != lineIndex || selectEnd[2] != charIndex) {
if (selectToInt(0, lineIndex, charIndex) < selectToInt(selectStart)) {
return
}
selectEnd[0] = 0
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
upSelectChars()
}
return
}
}
return
}
}
if (!ReadBookConfig.isScroll) return
//滚动翻页
relativeOffset = relativeOffset(1)
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(1).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectEnd[0] != 1 || selectEnd[1] != lineIndex || selectEnd[2] != charIndex) {
if (selectToInt(1, lineIndex, charIndex) < selectToInt(selectStart)) {
return
}
selectEnd[0] = 1
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
upSelectChars()
}
return
}
}
return
}
}
relativeOffset = relativeOffset(2)
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
if (selectLineEnd != lineIndex || selectCharEnd != charIndex) {
selectLineEnd = lineIndex
selectCharEnd = charIndex
upSelectedEnd(textChar.end, textLine.lineBottom)
upSelectChars(textPage)
if (selectEnd[0] != 2 || selectEnd[1] != lineIndex || selectEnd[2] != charIndex) {
if (selectToInt(2, lineIndex, charIndex) < selectToInt(selectStart)) {
return
}
selectEnd[0] = 2
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
upSelectChars()
}
break
return
}
}
break
return
}
}
}
fun selectEndMoveIndex(lineIndex: Int, charIndex: Int) {
selectLineEnd = lineIndex
selectCharEnd = charIndex
val textLine = textPage.textLines[lineIndex]
/**
* 选择开始文字
*/
fun selectStartMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
selectStart[0] = relativePage
selectStart[1] = lineIndex
selectStart[2] = charIndex
val textLine = relativePage(relativePage).textLines[lineIndex]
val textChar = textLine.textChars[charIndex]
upSelectedEnd(textChar.end, textLine.lineBottom)
upSelectChars(textPage)
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset(relativePage))
upSelectChars()
}
private fun upSelectChars(textPage: TextPage) {
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
textChar.selected =
if (lineIndex == selectLineStart && lineIndex == selectLineEnd) {
charIndex in selectCharStart..selectCharEnd
} else if (lineIndex == selectLineStart) {
charIndex >= selectCharStart
} else if (lineIndex == selectLineEnd) {
charIndex <= selectCharEnd
} else {
lineIndex in (selectLineStart + 1) until selectLineEnd
}
/**
* 选择结束文字
*/
fun selectEndMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
selectEnd[0] = relativePage
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
val textLine = relativePage(relativePage).textLines[lineIndex]
val textChar = textLine.textChars[charIndex]
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset(relativePage))
upSelectChars()
}
private fun upSelectChars() {
val last = if (ReadBookConfig.isScroll) 2 else 0
for (relativePos in 0..last) {
for ((lineIndex, textLine) in relativePage(relativePos).textLines.withIndex()) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
textChar.selected =
if (relativePos == selectStart[0]
&& relativePos == selectEnd[0]
&& lineIndex == selectStart[1]
&& lineIndex == selectEnd[1]
) {
charIndex in selectStart[2]..selectEnd[2]
} else if (relativePos == selectStart[0] && lineIndex == selectStart[1]) {
charIndex >= selectStart[2]
} else if (relativePos == selectEnd[0] && lineIndex == selectEnd[1]) {
charIndex <= selectEnd[2]
} else if (relativePos == selectStart[0] && relativePos == selectEnd[0]) {
lineIndex in (selectStart[1] + 1) until selectEnd[1]
} else if (relativePos == selectStart[0]) {
lineIndex > selectStart[1]
} else if (relativePos == selectEnd[0]) {
lineIndex < selectEnd[1]
} else {
relativePos in selectStart[0] + 1 until selectEnd[0]
}
}
}
}
invalidate()
@ -280,9 +473,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
fun cancelSelect() {
textPage.textLines.forEach { textLine ->
textLine.textChars.forEach {
it.selected = false
val last = if (ReadBookConfig.isScroll) 2 else 0
for (relativePos in 0..last) {
relativePage(relativePos).textLines.forEach { textLine ->
textLine.textChars.forEach {
it.selected = false
}
}
}
invalidate()
@ -292,31 +488,86 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
val selectedText: String
get() {
val stringBuilder = StringBuilder()
for (lineIndex in selectLineStart..selectLineEnd) {
if (lineIndex == selectLineStart && lineIndex == selectLineEnd) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(
selectCharStart,
selectCharEnd + 1
)
)
} else if (lineIndex == selectLineStart) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(
selectCharStart
)
)
} else if (lineIndex == selectLineEnd) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(0, selectCharEnd + 1)
)
} else {
stringBuilder.append(textPage.textLines[lineIndex].text)
for (relativePos in selectStart[0]..selectEnd[0]) {
val textPage = relativePage(relativePos)
if (relativePos == selectStart[0] && relativePos == selectEnd[0]) {
for (lineIndex in selectStart[1]..selectEnd[1]) {
if (lineIndex == selectStart[1] && lineIndex == selectEnd[1]) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(
selectStart[2],
selectEnd[2] + 1
)
)
} else if (lineIndex == selectStart[1]) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(
selectStart[2]
)
)
} else if (lineIndex == selectEnd[1]) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(0, selectEnd[2] + 1)
)
} else {
stringBuilder.append(textPage.textLines[lineIndex].text)
}
}
} else if (relativePos == selectStart[0]) {
for (lineIndex in selectStart[1] until relativePage(relativePos).textLines.size) {
if (lineIndex == selectStart[1]) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(
selectStart[2]
)
)
} else {
stringBuilder.append(textPage.textLines[lineIndex].text)
}
}
} else if (relativePos == selectEnd[0]) {
for (lineIndex in 0..selectEnd[1]) {
if (lineIndex == selectEnd[1]) {
stringBuilder.append(
textPage.textLines[lineIndex].text.substring(0, selectEnd[2] + 1)
)
} else {
stringBuilder.append(textPage.textLines[lineIndex].text)
}
}
} else if (relativePos in selectStart[0] + 1 until selectEnd[0]) {
for (lineIndex in selectStart[1]..selectEnd[1]) {
stringBuilder.append(textPage.textLines[lineIndex].text)
}
}
}
return stringBuilder.toString()
}
private fun selectToInt(page: Int, line: Int, char: Int): Int {
return page * 1000000 + line * 100000 + char
}
private fun selectToInt(select: Array<Int>): Int {
return select[0] * 1000000 + select[1] * 100000 + select[2]
}
private fun relativeOffset(relativePos: Int): Float {
return when (relativePos) {
0 -> pageOffset
1 -> pageOffset + textPage.height
else -> pageOffset + textPage.height + pageFactory.nextPage.height
}
}
private fun relativePage(relativePos: Int): TextPage {
return when (relativePos) {
0 -> textPage
1 -> pageFactory.nextPage
else -> pageFactory.nextPagePlus
}
}
interface CallBack {
fun upSelectedStart(x: Float, y: Float)
fun upSelectedEnd(x: Float, y: Float)

@ -15,7 +15,6 @@ import java.util.*
class ContentView(context: Context) : FrameLayout(context) {
private var pageSize: Int = 0
init {
//设置背景防止切换背景时文字重叠
@ -26,8 +25,7 @@ class ContentView(context: Context) : FrameLayout(context) {
upTime()
content_text_view.upView = {
tv_bottom_left.text = it.title
pageSize = it.pageSize
setPageIndex(it.index)
setPageIndex(it.index, it.pageSize)
}
}
@ -95,18 +93,19 @@ class ContentView(context: Context) : FrameLayout(context) {
tv_top_right.text = context.getString(R.string.battery_show, battery)
}
fun setContent(textPage: TextPage?) {
if (textPage != null) {
tv_bottom_left.text = textPage.title
pageSize = textPage.pageSize
setPageIndex(textPage.index)
content_text_view.resetPageOffset()
content_text_view.setContent(textPage)
}
fun setContent(textPage: TextPage) {
tv_bottom_left.text = textPage.title
setPageIndex(textPage.index, textPage.pageSize)
content_text_view.resetPageOffset()
content_text_view.setContent(textPage)
}
fun resetPageOffset() {
content_text_view.resetPageOffset()
}
@SuppressLint("SetTextI18n")
fun setPageIndex(pageIndex: Int?) {
fun setPageIndex(pageIndex: Int?, pageSize: Int) {
pageIndex?.let {
tv_bottom_right.text = "${pageIndex.plus(1)}/${pageSize}"
}
@ -120,7 +119,10 @@ class ContentView(context: Context) : FrameLayout(context) {
content_text_view.selectAble = selectAble
}
fun selectText(e: MotionEvent, select: (lineIndex: Int, charIndex: Int) -> Unit) {
fun selectText(
e: MotionEvent,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
) {
val y = e.y - headerHeight
return content_text_view.selectText(e.x, y, select)
}
@ -129,16 +131,16 @@ class ContentView(context: Context) : FrameLayout(context) {
content_text_view.selectStartMove(x, y - headerHeight)
}
fun selectStartMoveIndex(lineIndex: Int, charIndex: Int) {
content_text_view.selectStartMoveIndex(lineIndex, charIndex)
fun selectStartMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
content_text_view.selectStartMoveIndex(relativePage, 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 selectEndMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
content_text_view.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
fun cancelSelect() {

@ -7,11 +7,11 @@ interface DataSource {
val pageIndex: Int get() = ReadBook.durChapterPos()
fun getCurrentChapter(): TextChapter?
val currentChapter: TextChapter?
fun getNextChapter(): TextChapter?
val nextChapter: TextChapter?
fun getPreviousChapter(): TextChapter?
val prevChapter: TextChapter?
fun hasNextChapter(): Boolean

@ -10,11 +10,13 @@ abstract class PageFactory<DATA>(protected val dataSource: DataSource) {
abstract fun moveToPrev(): Boolean
abstract val nextPage: DATA?
abstract val nextPage: DATA
abstract val prevPage: DATA?
abstract val prevPage: DATA
abstract val currentPage: DATA?
abstract val currentPage: DATA
abstract val nextPagePlus: DATA
abstract fun hasNext(): Boolean

@ -164,15 +164,18 @@ class PageView(context: Context, attrs: AttributeSet) :
nextPage.upBattery(battery)
}
override fun getCurrentChapter(): TextChapter? {
override val currentChapter: TextChapter?
get() {
return if (callBack.isInitFinish) ReadBook.textChapter(0) else null
}
override fun getNextChapter(): TextChapter? {
override val nextChapter: TextChapter?
get() {
return if (callBack.isInitFinish) ReadBook.textChapter(1) else null
}
override fun getPreviousChapter(): TextChapter? {
override val prevChapter: TextChapter?
get() {
return if (callBack.isInitFinish) ReadBook.textChapter(-1) else null
}

@ -10,7 +10,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
}
override fun hasNext(): Boolean = with(dataSource) {
return hasNextChapter() || getCurrentChapter()?.isLastIndex(pageIndex) != true
return hasNextChapter() || currentChapter?.isLastIndex(pageIndex) != true
}
override fun moveToFirst() {
@ -18,7 +18,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
}
override fun moveToLast() = with(dataSource) {
getCurrentChapter()?.let {
currentChapter?.let {
if (it.pageSize() == 0) {
ReadBook.setPageIndex(0)
} else {
@ -29,7 +29,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
override fun moveToNext(): Boolean = with(dataSource) {
return if (hasNext()) {
if (getCurrentChapter()?.isLastIndex(pageIndex) == true) {
if (currentChapter?.isLastIndex(pageIndex) == true) {
ReadBook.moveToNextChapter(false)
} else {
ReadBook.setPageIndex(pageIndex.plus(1))
@ -51,28 +51,62 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
false
}
override val currentPage: TextPage?
override val currentPage: TextPage
get() = with(dataSource) {
return getCurrentChapter()?.page(pageIndex)
currentChapter?.let {
return@with it.page(pageIndex)
?: TextPage(title = it.title).format()
}
return TextPage().format()
}
override val nextPage: TextPage?
override val nextPage: TextPage
get() = with(dataSource) {
getCurrentChapter()?.let {
currentChapter?.let {
if (pageIndex < it.pageSize() - 1) {
return getCurrentChapter()?.page(pageIndex + 1)?.removePageAloudSpan()
return@with it.page(pageIndex + 1)?.removePageAloudSpan()
?: TextPage(title = it.title).format()
}
}
return getNextChapter()?.page(0)?.removePageAloudSpan()
nextChapter?.let {
return@with it.page(0)?.removePageAloudSpan()
?: TextPage(title = it.title).format()
}
return TextPage().format()
}
override val prevPage: TextPage?
override val prevPage: TextPage
get() = with(dataSource) {
if (pageIndex > 0) {
return getCurrentChapter()?.page(pageIndex - 1)?.removePageAloudSpan()
currentChapter?.let {
return@with it.page(pageIndex - 1)?.removePageAloudSpan()
?: TextPage(title = it.title).format()
}
}
return getPreviousChapter()?.lastPage()?.removePageAloudSpan()
prevChapter?.let {
return@with it.lastPage()?.removePageAloudSpan()
?: TextPage(title = it.title).format()
}
return TextPage().format()
}
override val nextPagePlus: TextPage
get() = with(dataSource) {
currentChapter?.let {
if (pageIndex < it.pageSize() - 2) {
return@with it.page(pageIndex + 2)?.removePageAloudSpan()
?: TextPage(title = it.title).format()
}
nextChapter?.let { nc ->
if (pageIndex < it.pageSize() - 1) {
return@with nc.page(0)?.removePageAloudSpan()
?: TextPage(title = nc.title).format()
}
return@with nc.page(1)?.removePageAloudSpan()
?: TextPage(title = nc.title).format()
}
}
return TextPage().format()
}
}

@ -22,7 +22,8 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
private fun onScroll(event: MotionEvent) {
//判断是否移动了
if (!isMoved) {
isMoved = abs(startX - event.x) > slop || abs(startY - event.y) > slop
isMoved = abs(startX - event.x) > slop
|| abs(startX - event.x) > abs(startY - event.y)
if (isMoved) {
if (event.x - startX > 0) {
//如果上一页不存在

@ -67,9 +67,14 @@ abstract class PageDelegate(protected val pageView: PageView) :
var isTextSelected = false
var selectedOnDown = false
var firstRelativePage = 0
var firstLineIndex: Int = 0
var firstCharIndex: Int = 0
init {
curPage.resetPageOffset()
}
open fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
@ -131,7 +136,7 @@ abstract class PageDelegate(protected val pageView: PageView) :
bitmap = null
}
fun setViewSize(width: Int, height: Int) {
open fun setViewSize(width: Int, height: Int) {
viewWidth = width
viewHeight = height
pageView.invalidate()
@ -282,23 +287,41 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 长按选择
*/
override fun onLongPress(e: MotionEvent) {
curPage.selectText(e) { lineIndex, charIndex ->
curPage.selectText(e) { relativePage, lineIndex, charIndex ->
isTextSelected = true
firstRelativePage = relativePage
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)
curPage.selectText(event) { relativePage, lineIndex, charIndex ->
when {
relativePage > firstRelativePage -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
relativePage < firstRelativePage -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
lineIndex > firstLineIndex -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
lineIndex < firstLineIndex -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
charIndex > firstCharIndex -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
else -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
}
}
}

@ -115,6 +115,11 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mFrontShadowDrawableHBT.gradientType = GradientDrawable.LINEAR_GRADIENT
}
override fun setViewSize(width: Int, height: Int) {
super.setViewSize(width, height)
mMaxLength = hypot(viewWidth.toDouble(), viewWidth.toDouble()).toFloat()
}
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) {
super.setStartPoint(x, y, invalidate)
calcCornerXY(x, y)
@ -171,7 +176,6 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
override fun onAnimStart() {
var dx: Float
val dy: Float
// dx 水平方向滑动的距离,负值会使滚动向左滚动
// dy 垂直方向滑动的距离,负值会使滚动向上滚动
if (isCancel) {
dx = if (mCornerX > 0 && mDirection == Direction.NEXT) {
@ -480,8 +484,10 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
private fun calcPoints() {
mTouchX = touchX
mTouchY = touchY
mMiddleX = (mTouchX + mCornerX) / 2
mMiddleY = (mTouchY + mCornerY) / 2
mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX)
mBezierControl1.y = mCornerY.toFloat()
@ -492,6 +498,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
} else {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY)
}
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2
mBezierStart1.y = mCornerY.toFloat()
//固定左边上下两个点
@ -500,24 +507,27 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
if (mBezierStart1.x < 0)
mBezierStart1.x = viewWidth - mBezierStart1.x
val f1: Float = abs(mCornerX - mTouchX)
val f2: Float = viewWidth * f1 / mBezierStart1.x
val f1 = abs(mCornerX - mTouchX)
val f2 = viewWidth * f1 / mBezierStart1.x
mTouchX = abs(mCornerX - f2)
val f3: Float = abs(mCornerX - mTouchX) * abs(mCornerY - mTouchY) / f1
val f3 = abs(mCornerX - mTouchX) * abs(mCornerY - mTouchY) / f1
mTouchY = abs(mCornerY - f3)
mMiddleX = (mTouchX + mCornerX) / 2
mMiddleY = (mTouchY + mCornerY) / 2
mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX)
mBezierControl1.y = mCornerY.toFloat()
mBezierControl2.x = mCornerX.toFloat()
mBezierControl2.y = if ((mCornerY - mMiddleY).toInt() == 0) {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f
} else {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY)
}
mBezierStart1.x = (mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2)
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2
}
}
mBezierStart2.x = mCornerX.toFloat()

@ -13,12 +13,14 @@
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:textSize="18sp"
android:visibility="gone"
android:text="@string/header" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:title="@string/padding_top"
app:max="100" />
@ -26,6 +28,7 @@
android:id="@+id/dsb_header_padding_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:title="@string/padding_bottom"
app:max="100" />
@ -33,6 +36,7 @@
android:id="@+id/dsb_header_padding_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:title="@string/padding_left"
app:max="100" />
@ -40,13 +44,14 @@
android:id="@+id/dsb_header_padding_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:title="@string/padding_right"
app:max="100" />
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_body_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/main_body" />

@ -9,7 +9,7 @@ buildscript {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.android.tools.build:gradle:3.6.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.3'
classpath 'com.google.gms:google-services:4.3.3'

@ -1,6 +1,6 @@
#Wed Aug 21 20:30:49 CST 2019
#Tue Feb 25 08:10:32 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

Loading…
Cancel
Save