|
@@ -16,7 +16,7 @@ import io.legado.app.help.config.AppConfig
|
|
|
import io.legado.app.help.config.ReadBookConfig
|
|
import io.legado.app.help.config.ReadBookConfig
|
|
|
import io.legado.app.lib.theme.accentColor
|
|
import io.legado.app.lib.theme.accentColor
|
|
|
import io.legado.app.model.ReadBook
|
|
import io.legado.app.model.ReadBook
|
|
|
-import io.legado.app.ui.book.read.page.entities.TextChar
|
|
|
|
|
|
|
+import io.legado.app.ui.book.read.page.entities.TextColumn
|
|
|
import io.legado.app.ui.book.read.page.entities.TextLine
|
|
import io.legado.app.ui.book.read.page.entities.TextLine
|
|
|
import io.legado.app.ui.book.read.page.entities.TextPage
|
|
import io.legado.app.ui.book.read.page.entities.TextPage
|
|
|
import io.legado.app.ui.book.read.page.entities.TextPos
|
|
import io.legado.app.ui.book.read.page.entities.TextPos
|
|
@@ -30,7 +30,7 @@ import io.legado.app.utils.toastOnUi
|
|
|
import kotlin.math.min
|
|
import kotlin.math.min
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 阅读内容界面
|
|
|
|
|
|
|
+ * 阅读内容视图
|
|
|
*/
|
|
*/
|
|
|
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
|
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
|
|
var selectAble = context.getPrefBoolean(PreferKey.textSelectAble, true)
|
|
var selectAble = context.getPrefBoolean(PreferKey.textSelectAble, true)
|
|
@@ -51,7 +51,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
private var cacheIncreased = false
|
|
private var cacheIncreased = false
|
|
|
private val increaseSize = 8 * 1024 * 1024
|
|
private val increaseSize = 8 * 1024 * 1024
|
|
|
private val maxCacheSize = 256 * 1024 * 1024
|
|
private val maxCacheSize = 256 * 1024 * 1024
|
|
|
- private val reviewButtonArea: ArrayList<FloatArray> = arrayListOf()
|
|
|
|
|
|
|
|
|
|
//滚动参数
|
|
//滚动参数
|
|
|
private val pageFactory: TextPageFactory get() = callBack.pageFactory
|
|
private val pageFactory: TextPageFactory get() = callBack.pageFactory
|
|
@@ -68,12 +67,18 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
callBack = activity as CallBack
|
|
callBack = activity as CallBack
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置内容
|
|
|
|
|
+ */
|
|
|
fun setContent(textPage: TextPage) {
|
|
fun setContent(textPage: TextPage) {
|
|
|
this.textPage = textPage
|
|
this.textPage = textPage
|
|
|
imagePaint.isAntiAlias = AppConfig.useAntiAlias
|
|
imagePaint.isAntiAlias = AppConfig.useAntiAlias
|
|
|
invalidate()
|
|
invalidate()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新绘制区域
|
|
|
|
|
+ */
|
|
|
fun upVisibleRect() {
|
|
fun upVisibleRect() {
|
|
|
visibleRect.set(
|
|
visibleRect.set(
|
|
|
ChapterProvider.paddingLeft.toFloat(),
|
|
ChapterProvider.paddingLeft.toFloat(),
|
|
@@ -102,7 +107,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
* 绘制页面
|
|
* 绘制页面
|
|
|
*/
|
|
*/
|
|
|
private fun drawPage(canvas: Canvas) {
|
|
private fun drawPage(canvas: Canvas) {
|
|
|
- reviewButtonArea.clear()
|
|
|
|
|
var relativeOffset = relativeOffset(0)
|
|
var relativeOffset = relativeOffset(0)
|
|
|
textPage.textLines.forEach { textLine ->
|
|
textPage.textLines.forEach { textLine ->
|
|
|
draw(canvas, textPage, textLine, relativeOffset)
|
|
draw(canvas, textPage, textLine, relativeOffset)
|
|
@@ -125,6 +129,9 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 绘制页面
|
|
|
|
|
+ */
|
|
|
private fun draw(
|
|
private fun draw(
|
|
|
canvas: Canvas,
|
|
canvas: Canvas,
|
|
|
textPage: TextPage,
|
|
textPage: TextPage,
|
|
@@ -161,23 +168,17 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
reviewCountPaint.textSize = textPaint.textSize * 0.6F
|
|
reviewCountPaint.textSize = textPaint.textSize * 0.6F
|
|
|
reviewCountPaint.color = textColor
|
|
reviewCountPaint.color = textColor
|
|
|
textLine.textChars.forEach {
|
|
textLine.textChars.forEach {
|
|
|
- if (it.isImage) {
|
|
|
|
|
- drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
|
|
|
|
|
- } else {
|
|
|
|
|
- textPaint.color = textColor
|
|
|
|
|
- if (it.isSearchResult) {
|
|
|
|
|
- textPaint.color = context.accentColor
|
|
|
|
|
|
|
+ when (it.style) {
|
|
|
|
|
+ 0 -> {
|
|
|
|
|
+ textPaint.color = textColor
|
|
|
|
|
+ if (it.isSearchResult) {
|
|
|
|
|
+ textPaint.color = context.accentColor
|
|
|
|
|
+ }
|
|
|
|
|
+ canvas.drawText(it.charData, it.start, lineBase, textPaint)
|
|
|
}
|
|
}
|
|
|
- if (it.charData == "\uD83D\uDCAC" && it.isLineEnd) {
|
|
|
|
|
|
|
+ 1 -> drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
|
|
|
|
|
+ 2 -> {
|
|
|
if (textLine.reviewCount <= 0) return@forEach
|
|
if (textLine.reviewCount <= 0) return@forEach
|
|
|
- reviewButtonArea.add(
|
|
|
|
|
- floatArrayOf(
|
|
|
|
|
- it.start - textPaint.textSize * 0.4F,
|
|
|
|
|
- lineBase + textPaint.textSize * 0.2F,
|
|
|
|
|
- it.start + textPaint.textSize * 2F,
|
|
|
|
|
- lineBase - textPaint.textSize * 1F,
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
canvas.drawLine(
|
|
canvas.drawLine(
|
|
|
it.start,
|
|
it.start,
|
|
|
lineBase - textPaint.textSize * 2 / 5,
|
|
lineBase - textPaint.textSize * 2 / 5,
|
|
@@ -243,7 +244,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
lineBase - textPaint.textSize / 6,
|
|
lineBase - textPaint.textSize / 6,
|
|
|
reviewCountPaint
|
|
reviewCountPaint
|
|
|
)
|
|
)
|
|
|
- } else canvas.drawText(it.charData, it.start, lineBase, textPaint)
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
if (it.selected) {
|
|
if (it.selected) {
|
|
|
canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
|
|
canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
|
|
@@ -259,7 +260,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
canvas: Canvas,
|
|
canvas: Canvas,
|
|
|
textPage: TextPage,
|
|
textPage: TextPage,
|
|
|
textLine: TextLine,
|
|
textLine: TextLine,
|
|
|
- textChar: TextChar,
|
|
|
|
|
|
|
+ textChar: TextColumn,
|
|
|
lineTop: Float,
|
|
lineTop: Float,
|
|
|
lineBottom: Float
|
|
lineBottom: Float
|
|
|
) {
|
|
) {
|
|
@@ -344,20 +345,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
invalidate()
|
|
invalidate()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fun resetPageOffset() {
|
|
|
|
|
- pageOffset = 0
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 点击评论按钮
|
|
|
|
|
|
|
+ * 重置滚动位置
|
|
|
*/
|
|
*/
|
|
|
- fun pressReviewButton(x: Float, y: Float): Boolean {
|
|
|
|
|
- reviewButtonArea.forEach {
|
|
|
|
|
- if (x in it[0]..it[2] && y in it[3]..it[1]) {
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return false
|
|
|
|
|
|
|
+ fun resetPageOffset() {
|
|
|
|
|
+ pageOffset = 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -366,34 +358,49 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
fun longPress(
|
|
fun longPress(
|
|
|
x: Float,
|
|
x: Float,
|
|
|
y: Float,
|
|
y: Float,
|
|
|
- select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
|
|
|
|
|
|
|
+ select: (textPos: TextPos) -> Unit,
|
|
|
) {
|
|
) {
|
|
|
- touch(x, y) { _, relativePos, _, lineIndex, _, charIndex, textChar ->
|
|
|
|
|
- if (textChar.isImage) {
|
|
|
|
|
- callBack.onImageLongPress(x, y, textChar.charData)
|
|
|
|
|
|
|
+ touch(x, y) { _, textPos, _, _, textColumn ->
|
|
|
|
|
+ if (textColumn.style == 2) return@touch
|
|
|
|
|
+ if (textColumn.style == 1) {
|
|
|
|
|
+ callBack.onImageLongPress(x, y, textColumn.charData)
|
|
|
} else {
|
|
} else {
|
|
|
if (!selectAble) return@touch
|
|
if (!selectAble) return@touch
|
|
|
- if (textChar.charData == "\uD83D\uDCAC" && textChar.isLineEnd) return@touch
|
|
|
|
|
- textChar.selected = true
|
|
|
|
|
|
|
+ textColumn.selected = true
|
|
|
invalidate()
|
|
invalidate()
|
|
|
- select(relativePos, lineIndex, charIndex)
|
|
|
|
|
|
|
+ select(textPos)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 单击
|
|
|
|
|
+ * @return true:已处理, false:未处理
|
|
|
|
|
+ */
|
|
|
|
|
+ fun click(x: Float, y: Float): Boolean {
|
|
|
|
|
+ var handled = false
|
|
|
|
|
+ touch(x, y) { _, textPos, textPage, textLine, textColumn ->
|
|
|
|
|
+ if (textColumn.style == 2) {
|
|
|
|
|
+ context.toastOnUi("Button Pressed!")
|
|
|
|
|
+ handled = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return handled
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 选择文字
|
|
* 选择文字
|
|
|
*/
|
|
*/
|
|
|
fun selectText(
|
|
fun selectText(
|
|
|
x: Float,
|
|
x: Float,
|
|
|
y: Float,
|
|
y: Float,
|
|
|
- select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
|
|
|
|
|
|
|
+ select: (textPos: TextPos) -> Unit,
|
|
|
) {
|
|
) {
|
|
|
- touch(x, y) { _, relativePos, _, lineIndex, _, charIndex, textChar ->
|
|
|
|
|
- if (textChar.charData == "\uD83D\uDCAC" && textChar.isLineEnd) return@touch
|
|
|
|
|
- textChar.selected = true
|
|
|
|
|
|
|
+ touch(x, y) { _, textPos, _, _, textColumn ->
|
|
|
|
|
+ if (textColumn.style == 2) return@touch
|
|
|
|
|
+ textColumn.selected = true
|
|
|
invalidate()
|
|
invalidate()
|
|
|
- select(relativePos, lineIndex, charIndex)
|
|
|
|
|
|
|
+ select(textPos)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -401,11 +408,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
* 开始选择符移动
|
|
* 开始选择符移动
|
|
|
*/
|
|
*/
|
|
|
fun selectStartMove(x: Float, y: Float) {
|
|
fun selectStartMove(x: Float, y: Float) {
|
|
|
- touch(x, y) { relativeOffset, relativePos, _, lineIndex, textLine, charIndex, textChar ->
|
|
|
|
|
- val pos = TextPos(relativePos, lineIndex, charIndex)
|
|
|
|
|
- if (selectStart.compare(pos) != 0) {
|
|
|
|
|
- if (pos.compare(selectEnd) <= 0) {
|
|
|
|
|
- selectStart.upData(pos = pos)
|
|
|
|
|
|
|
+ touch(x, y) { relativeOffset, textPos, _, textLine, textChar ->
|
|
|
|
|
+ if (selectStart.compare(textPos) != 0) {
|
|
|
|
|
+ if (textPos.compare(selectEnd) <= 0) {
|
|
|
|
|
+ selectStart.upData(pos = textPos)
|
|
|
upSelectedStart(
|
|
upSelectedStart(
|
|
|
textChar.start,
|
|
textChar.start,
|
|
|
textLine.lineBottom + relativeOffset,
|
|
textLine.lineBottom + relativeOffset,
|
|
@@ -421,11 +427,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
* 结束选择符移动
|
|
* 结束选择符移动
|
|
|
*/
|
|
*/
|
|
|
fun selectEndMove(x: Float, y: Float) {
|
|
fun selectEndMove(x: Float, y: Float) {
|
|
|
- touch(x, y) { relativeOffset, relativePos, _, lineIndex, textLine, charIndex, textChar ->
|
|
|
|
|
- val pos = TextPos(relativePos, lineIndex, charIndex)
|
|
|
|
|
- if (pos.compare(selectEnd) != 0) {
|
|
|
|
|
- if (pos.compare(selectStart) >= 0) {
|
|
|
|
|
- selectEnd.upData(pos)
|
|
|
|
|
|
|
+ touch(x, y) { relativeOffset, textPos, _, textLine, textChar ->
|
|
|
|
|
+ if (textPos.compare(selectEnd) != 0) {
|
|
|
|
|
+ if (textPos.compare(selectStart) >= 0) {
|
|
|
|
|
+ selectEnd.upData(textPos)
|
|
|
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
|
|
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
|
|
|
upSelectChars()
|
|
upSelectChars()
|
|
|
}
|
|
}
|
|
@@ -433,17 +438,19 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 触碰位置信息
|
|
|
|
|
+ * @param touched 回调
|
|
|
|
|
+ */
|
|
|
private fun touch(
|
|
private fun touch(
|
|
|
x: Float,
|
|
x: Float,
|
|
|
y: Float,
|
|
y: Float,
|
|
|
touched: (
|
|
touched: (
|
|
|
relativeOffset: Float,
|
|
relativeOffset: Float,
|
|
|
- relativePos: Int,
|
|
|
|
|
|
|
+ textPos: TextPos,
|
|
|
textPage: TextPage,
|
|
textPage: TextPage,
|
|
|
- lineIndex: Int,
|
|
|
|
|
textLine: TextLine,
|
|
textLine: TextLine,
|
|
|
- charIndex: Int,
|
|
|
|
|
- textChar: TextChar
|
|
|
|
|
|
|
+ textColumn: TextColumn
|
|
|
) -> Unit
|
|
) -> Unit
|
|
|
) {
|
|
) {
|
|
|
if (!visibleRect.contains(x, y)) return
|
|
if (!visibleRect.contains(x, y)) return
|
|
@@ -462,9 +469,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
if (textChar.isTouch(x)) {
|
|
if (textChar.isTouch(x)) {
|
|
|
touched.invoke(
|
|
touched.invoke(
|
|
|
relativeOffset,
|
|
relativeOffset,
|
|
|
- relativePos, textPage,
|
|
|
|
|
- lineIndex, textLine,
|
|
|
|
|
- charIndex, textChar
|
|
|
|
|
|
|
+ TextPos(relativePos, lineIndex, charIndex),
|
|
|
|
|
+ textPage, textLine, textChar
|
|
|
)
|
|
)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -514,7 +520,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|
|
textPos.lineIndex = lineIndex
|
|
textPos.lineIndex = lineIndex
|
|
|
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
|
|
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
|
|
|
textPos.charIndex = charIndex
|
|
textPos.charIndex = charIndex
|
|
|
- if (textChar.charData == "\uD83D\uDCAC" && textChar.isLineEnd) continue
|
|
|
|
|
|
|
+ if (textChar.style == 2) continue
|
|
|
textChar.selected =
|
|
textChar.selected =
|
|
|
textPos.compare(selectStart) >= 0 && textPos.compare(selectEnd) <= 0
|
|
textPos.compare(selectStart) >= 0 && textPos.compare(selectEnd) <= 0
|
|
|
textChar.isSearchResult = textChar.selected && callBack.isSelectingSearchResult
|
|
textChar.isSearchResult = textChar.selected && callBack.isSelectingSearchResult
|